openmagic 0.31.5 → 0.31.6

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
@@ -14,6 +14,7 @@
14
14
  [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue?style=flat-square)](https://www.typescriptlang.org/)
15
15
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen?style=flat-square)](https://github.com/Kalmuraee/OpenMagic/pulls)
16
16
  [![Node](https://img.shields.io/node/v/openmagic?style=flat-square)](https://nodejs.org/)
17
+ [![Hits](https://hits.sh/github.com/Kalmuraee/OpenMagic.svg?style=flat-square&label=views&color=6c5ce7)](https://hits.sh/github.com/Kalmuraee/OpenMagic/)
17
18
 
18
19
  OpenMagic injects a floating AI toolbar into your running web app via reverse proxy.
19
20
  Select any element, describe what you want, review the diff, approve — your code updates and HMR refreshes the page.
@@ -43,7 +44,7 @@ npm run dev
43
44
  npx openmagic@latest
44
45
  ```
45
46
 
46
- Run `npx openmagic@latest` from your project folder so it can find your source files and dev server. A proxied version of your app opens with the AI toolbar overlaid. That is it.
47
+ Run this from your project folder so OpenMagic can find your source files and dev server. A proxied version of your app opens with the toolbar overlaid.
47
48
 
48
49
  ---
49
50
 
@@ -90,7 +91,7 @@ OpenMagic is a single-port reverse proxy. It sits between your browser and your
90
91
  3. **Server** -- Local Node.js process handling file I/O and proxying LLM calls. API keys never leave your machine.
91
92
  4. **HMR** -- When the AI modifies source files, your dev server's hot module replacement picks up changes automatically.
92
93
 
93
- Stop with `Ctrl+C`. No files modified. No dependencies added. No traces.
94
+ Stop with `Ctrl+C`. Nothing stays behind.
94
95
 
95
96
  ---
96
97
 
@@ -181,11 +182,9 @@ npx openmagic --port 3000
181
182
 
182
183
  ## Known Limitations
183
184
 
184
- Honesty matters. Here is what you should know:
185
-
186
- - **Origin change** -- Your app runs on `:3000` but is accessed via `:4567`. This can affect OAuth redirect URIs, `localStorage` isolation, and Service Worker scope. Most dev workflows are unaffected, but apps that depend on `window.location.origin` may need dev config adjustments.
187
- - **CSP via meta tags** -- OpenMagic strips CSP response headers to allow the toolbar script, but CSP defined in `<meta>` tags cannot be modified at the proxy level and may block the toolbar on strict pages.
188
- - **Not for production** -- OpenMagic is a development tool. Do not deploy the proxy to production.
185
+ - **Origin change** -- Your app runs on `:3000` but you access it via `:4567`. This can break OAuth redirect URIs, `localStorage` isolation, and Service Worker scope. Most dev setups work fine, but if your app checks `window.location.origin`, you may need to adjust your dev config.
186
+ - **CSP via meta tags** -- OpenMagic strips CSP response headers so the toolbar script can load, but `<meta>` tag CSP can't be modified at the proxy level and may still block it.
187
+ - **Not for production** -- This is a dev tool. Don't deploy the proxy to production.
189
188
 
190
189
  ---
191
190
 
@@ -220,15 +219,17 @@ OpenMagic works via reverse proxy, so it supports any framework that serves HTML
220
219
 
221
220
  ## Contributing
222
221
 
222
+ PRs are welcome. Bug fixes, new providers, UI improvements, docs.
223
+
223
224
  ```bash
224
225
  git clone https://github.com/Kalmuraee/OpenMagic.git
225
226
  cd OpenMagic
226
227
  npm install
227
228
  npm run build
228
- node dist/cli.js --port 3000
229
+ node dist/cli.js --port 3000 # Test with your dev server
229
230
  ```
230
231
 
231
- See the repo for project structure and contribution guidelines.
232
+ See **[CONTRIBUTING.md](./CONTRIBUTING.md)** for the architecture overview, how to add providers, and PR process.
232
233
 
233
234
  ---
234
235
 
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { Command } from "commander";
5
5
  import chalk from "chalk";
6
6
  import open from "open";
7
- import { resolve as resolve2, join as join5 } from "path";
7
+ import { resolve as resolve3, join as join5 } from "path";
8
8
  import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
9
9
  import { spawn } from "child_process";
10
10
  import { createInterface } from "readline";
@@ -1849,7 +1849,8 @@ function buildInjectionScript(token) {
1849
1849
  // src/detect.ts
1850
1850
  import { createConnection } from "net";
1851
1851
  import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
1852
- import { join as join4 } from "path";
1852
+ import { join as join4, resolve as resolve2 } from "path";
1853
+ import { execSync } from "child_process";
1853
1854
  var COMMON_DEV_PORTS = [
1854
1855
  3e3,
1855
1856
  // React (CRA), Next.js, Express
@@ -1883,19 +1884,19 @@ var COMMON_DEV_PORTS = [
1883
1884
  // Flask (last — macOS AirPlay also uses 5000)
1884
1885
  ];
1885
1886
  function checkPortSingle(port, host) {
1886
- return new Promise((resolve3) => {
1887
+ return new Promise((resolve4) => {
1887
1888
  const socket = createConnection({ port, host, timeout: 500 });
1888
1889
  socket.on("connect", () => {
1889
1890
  socket.destroy();
1890
- resolve3(true);
1891
+ resolve4(true);
1891
1892
  });
1892
1893
  socket.on("error", () => {
1893
1894
  socket.destroy();
1894
- resolve3(false);
1895
+ resolve4(false);
1895
1896
  });
1896
1897
  socket.on("timeout", () => {
1897
1898
  socket.destroy();
1898
- resolve3(false);
1899
+ resolve4(false);
1899
1900
  });
1900
1901
  });
1901
1902
  }
@@ -1907,12 +1908,45 @@ async function checkPort(port, host = "127.0.0.1") {
1907
1908
  ]);
1908
1909
  return results.some(Boolean);
1909
1910
  }
1910
- async function detectDevServer() {
1911
- const scripts = detectDevScripts();
1911
+ function verifyPortOwnership(port, expectedDir) {
1912
+ try {
1913
+ const pidOutput = execSync(`lsof -i :${port} -sTCP:LISTEN -t 2>/dev/null`, {
1914
+ encoding: "utf-8",
1915
+ timeout: 3e3
1916
+ }).trim();
1917
+ if (!pidOutput) return null;
1918
+ const pids = pidOutput.split("\n").map((p) => p.trim()).filter(Boolean);
1919
+ const expected = resolve2(expectedDir);
1920
+ for (const pid of pids) {
1921
+ try {
1922
+ const cwdOutput = execSync(
1923
+ `lsof -a -p ${pid} -d cwd -Fn 2>/dev/null | grep ^n | head -1`,
1924
+ { encoding: "utf-8", timeout: 3e3 }
1925
+ ).trim();
1926
+ if (!cwdOutput) continue;
1927
+ const processCwd = resolve2(cwdOutput.slice(1));
1928
+ if (processCwd === expected || expected.startsWith(processCwd) || processCwd.startsWith(expected)) {
1929
+ return true;
1930
+ }
1931
+ } catch {
1932
+ continue;
1933
+ }
1934
+ }
1935
+ return false;
1936
+ } catch {
1937
+ return null;
1938
+ }
1939
+ }
1940
+ async function detectDevServer(cwd = process.cwd()) {
1941
+ const scripts = detectDevScripts(cwd);
1912
1942
  const scriptPorts = scripts.map((s) => s.defaultPort).filter((p, i, a) => a.indexOf(p) === i);
1913
1943
  if (scriptPorts.length > 0) {
1914
1944
  for (const port of scriptPorts) {
1915
1945
  if (await checkPort(port)) {
1946
+ const owned = verifyPortOwnership(port, cwd);
1947
+ if (owned === false) {
1948
+ continue;
1949
+ }
1916
1950
  return { port, host: "localhost", fromScripts: true };
1917
1951
  }
1918
1952
  }
@@ -1923,8 +1957,10 @@ async function detectDevServer() {
1923
1957
  return isOpen ? port : null;
1924
1958
  });
1925
1959
  const results = await Promise.all(checks);
1926
- const foundPort = results.find((p) => p !== null);
1927
- if (foundPort) {
1960
+ for (const foundPort of results) {
1961
+ if (foundPort === null) continue;
1962
+ const owned = verifyPortOwnership(foundPort, cwd);
1963
+ if (owned === false) continue;
1928
1964
  return { port: foundPort, host: "localhost", fromScripts: false };
1929
1965
  }
1930
1966
  return null;
@@ -2044,27 +2080,27 @@ var lastDetectedPort = null;
2044
2080
  var VERSION2 = "0.31.1";
2045
2081
  function ask(question) {
2046
2082
  const rl = createInterface({ input: process.stdin, output: process.stdout });
2047
- return new Promise((resolve3) => {
2083
+ return new Promise((resolve4) => {
2048
2084
  rl.question(question, (answer) => {
2049
2085
  rl.close();
2050
- resolve3(answer.trim());
2086
+ resolve4(answer.trim());
2051
2087
  });
2052
2088
  });
2053
2089
  }
2054
2090
  function waitForPort(port, timeoutMs = 3e4, shouldAbort) {
2055
2091
  const start = Date.now();
2056
- return new Promise((resolve3) => {
2092
+ return new Promise((resolve4) => {
2057
2093
  const check = async () => {
2058
2094
  if (shouldAbort?.()) {
2059
- resolve3(false);
2095
+ resolve4(false);
2060
2096
  return;
2061
2097
  }
2062
2098
  if (await isPortOpen(port)) {
2063
- resolve3(true);
2099
+ resolve4(true);
2064
2100
  return;
2065
2101
  }
2066
2102
  if (Date.now() - start > timeoutMs) {
2067
- resolve3(false);
2103
+ resolve4(false);
2068
2104
  return;
2069
2105
  }
2070
2106
  setTimeout(check, 500);
@@ -2073,7 +2109,7 @@ function waitForPort(port, timeoutMs = 3e4, shouldAbort) {
2073
2109
  });
2074
2110
  }
2075
2111
  function runCommand(cmd, args, cwd = process.cwd()) {
2076
- return new Promise((resolve3) => {
2112
+ return new Promise((resolve4) => {
2077
2113
  try {
2078
2114
  const child = spawn(cmd, args, {
2079
2115
  cwd,
@@ -2094,10 +2130,10 @@ function runCommand(cmd, args, cwd = process.cwd()) {
2094
2130
  `));
2095
2131
  }
2096
2132
  });
2097
- child.on("error", () => resolve3(false));
2098
- child.on("close", (code) => resolve3(code === 0));
2133
+ child.on("error", () => resolve4(false));
2134
+ child.on("close", (code) => resolve4(code === 0));
2099
2135
  } catch {
2100
- resolve3(false);
2136
+ resolve4(false);
2101
2137
  }
2102
2138
  });
2103
2139
  }
@@ -2220,7 +2256,7 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
2220
2256
  chalk.green(` \u2713 Dev server running at ${targetHost}:${targetPort}`)
2221
2257
  );
2222
2258
  const roots = (opts.root || [process.cwd()]).map(
2223
- (r) => resolve2(r)
2259
+ (r) => resolve3(r)
2224
2260
  );
2225
2261
  const config = loadConfig();
2226
2262
  saveConfig({ ...config, roots, targetPort });