openmagic 0.34.1 → 0.35.1
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/cli.js +163 -2
- package/dist/cli.js.map +1 -1
- package/dist/toolbar/index.global.js +1 -1
- package/dist/toolbar/index.global.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1915,7 +1915,9 @@ function serveToolbarBundle(res) {
|
|
|
1915
1915
|
function createProxyServer(targetHost, targetPort, roots) {
|
|
1916
1916
|
const proxy = httpProxy.createProxyServer({
|
|
1917
1917
|
target: `http://${targetHost}:${targetPort}`,
|
|
1918
|
-
selfHandleResponse: true
|
|
1918
|
+
selfHandleResponse: true,
|
|
1919
|
+
changeOrigin: true
|
|
1920
|
+
// Rewrite Host header to match upstream — required by Vite 5.4+ and some Next.js setups
|
|
1919
1921
|
// ws: false — we handle WebSocket upgrades manually in server.on("upgrade")
|
|
1920
1922
|
});
|
|
1921
1923
|
const token = getSessionToken();
|
|
@@ -2124,7 +2126,11 @@ function verifyPortOwnership(port, expectedDir) {
|
|
|
2124
2126
|
}
|
|
2125
2127
|
async function detectDevServer(cwd = process.cwd()) {
|
|
2126
2128
|
const scripts = detectDevScripts(cwd);
|
|
2127
|
-
const
|
|
2129
|
+
const envPort = checkEnvPort(cwd);
|
|
2130
|
+
const scriptPorts = [
|
|
2131
|
+
...envPort ? [envPort] : [],
|
|
2132
|
+
...scripts.map((s) => s.defaultPort)
|
|
2133
|
+
].filter((p, i, a) => a.indexOf(p) === i);
|
|
2128
2134
|
if (scriptPorts.length > 0) {
|
|
2129
2135
|
for (const port of scriptPorts) {
|
|
2130
2136
|
if (await checkPort(port)) {
|
|
@@ -2216,6 +2222,73 @@ function detectDevScripts(cwd = process.cwd()) {
|
|
|
2216
2222
|
}
|
|
2217
2223
|
return scripts;
|
|
2218
2224
|
}
|
|
2225
|
+
var FRAMEWORK_NODE_REQUIREMENTS = {
|
|
2226
|
+
"Next.js": { minNode: "18.17.0", label: "Next.js 14+" },
|
|
2227
|
+
"Vite": { minNode: "18.0.0", label: "Vite 5+" },
|
|
2228
|
+
"Angular": { minNode: "18.13.0", label: "Angular 17+" },
|
|
2229
|
+
"SvelteKit": { minNode: "18.13.0", label: "SvelteKit 2+" },
|
|
2230
|
+
"Nuxt": { minNode: "18.0.0", label: "Nuxt 3+" },
|
|
2231
|
+
"Astro": { minNode: "18.14.1", label: "Astro 4+" },
|
|
2232
|
+
"Remix": { minNode: "18.0.0", label: "Remix 2+" },
|
|
2233
|
+
"Create React App": { minNode: "14.0.0", label: "Create React App" },
|
|
2234
|
+
"Gatsby": { minNode: "18.0.0", label: "Gatsby 5+" },
|
|
2235
|
+
"Vue CLI": { minNode: "14.0.0", label: "Vue CLI" },
|
|
2236
|
+
"Webpack": { minNode: "14.0.0", label: "Webpack 5+" },
|
|
2237
|
+
"Parcel": { minNode: "16.0.0", label: "Parcel 2+" }
|
|
2238
|
+
};
|
|
2239
|
+
function semverGte(a, b) {
|
|
2240
|
+
const pa = a.split(".").map(Number);
|
|
2241
|
+
const pb = b.split(".").map(Number);
|
|
2242
|
+
for (let i = 0; i < 3; i++) {
|
|
2243
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return true;
|
|
2244
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return false;
|
|
2245
|
+
}
|
|
2246
|
+
return true;
|
|
2247
|
+
}
|
|
2248
|
+
function checkNodeCompatibility(framework) {
|
|
2249
|
+
const req = FRAMEWORK_NODE_REQUIREMENTS[framework];
|
|
2250
|
+
if (!req) return { ok: true };
|
|
2251
|
+
const current = process.versions.node;
|
|
2252
|
+
if (!semverGte(current, req.minNode)) {
|
|
2253
|
+
return {
|
|
2254
|
+
ok: false,
|
|
2255
|
+
message: `${req.label} requires Node.js >= ${req.minNode}, but you are running v${current}`
|
|
2256
|
+
};
|
|
2257
|
+
}
|
|
2258
|
+
return { ok: true };
|
|
2259
|
+
}
|
|
2260
|
+
function checkEnvPort(cwd = process.cwd()) {
|
|
2261
|
+
const envFiles = [".env.local", ".env.development.local", ".env.development", ".env"];
|
|
2262
|
+
for (const envFile of envFiles) {
|
|
2263
|
+
const envPath = join4(cwd, envFile);
|
|
2264
|
+
if (!existsSync4(envPath)) continue;
|
|
2265
|
+
try {
|
|
2266
|
+
const content = readFileSync4(envPath, "utf-8");
|
|
2267
|
+
const match = content.match(/^PORT\s*=\s*(\d+)/m);
|
|
2268
|
+
if (match) return parseInt(match[1], 10);
|
|
2269
|
+
} catch {
|
|
2270
|
+
continue;
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
return null;
|
|
2274
|
+
}
|
|
2275
|
+
var LOCKFILE_NAMES = ["package-lock.json", "bun.lock", "bun.lockb", "yarn.lock", "pnpm-lock.yaml"];
|
|
2276
|
+
function scanParentLockfiles(projectDir) {
|
|
2277
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
2278
|
+
const project = resolve2(projectDir);
|
|
2279
|
+
const found = [];
|
|
2280
|
+
let dir = resolve2(project, "..");
|
|
2281
|
+
while (dir.length > 1 && dir.length >= home.length) {
|
|
2282
|
+
for (const lockfile of LOCKFILE_NAMES) {
|
|
2283
|
+
const p = join4(dir, lockfile);
|
|
2284
|
+
if (existsSync4(p)) found.push(p);
|
|
2285
|
+
}
|
|
2286
|
+
const parent = resolve2(dir, "..");
|
|
2287
|
+
if (parent === dir) break;
|
|
2288
|
+
dir = parent;
|
|
2289
|
+
}
|
|
2290
|
+
return found;
|
|
2291
|
+
}
|
|
2219
2292
|
function getProjectName(cwd = process.cwd()) {
|
|
2220
2293
|
const pkgPath = join4(cwd, "package.json");
|
|
2221
2294
|
if (!existsSync4(pkgPath)) return "this project";
|
|
@@ -2359,6 +2432,56 @@ async function healthCheck(proxyPort, _targetPort) {
|
|
|
2359
2432
|
}
|
|
2360
2433
|
console.log("");
|
|
2361
2434
|
}
|
|
2435
|
+
var detectedFramework = null;
|
|
2436
|
+
async function validateAppHealth(targetHost, targetPort) {
|
|
2437
|
+
try {
|
|
2438
|
+
const controller = new AbortController();
|
|
2439
|
+
const timeout = setTimeout(() => controller.abort(), 8e3);
|
|
2440
|
+
const res = await fetch(`http://${targetHost}:${targetPort}/`, {
|
|
2441
|
+
signal: controller.signal,
|
|
2442
|
+
redirect: "manual",
|
|
2443
|
+
headers: { Accept: "text/html" }
|
|
2444
|
+
});
|
|
2445
|
+
clearTimeout(timeout);
|
|
2446
|
+
const status = res.status;
|
|
2447
|
+
if (status >= 200 && status < 400) return;
|
|
2448
|
+
if (status === 404) {
|
|
2449
|
+
console.log(chalk.yellow(' \u26A0 Your app returned 404 for the root path ("/").'));
|
|
2450
|
+
console.log(chalk.dim(" The dev server is running, but no page matched."));
|
|
2451
|
+
console.log("");
|
|
2452
|
+
if (detectedFramework === "Next.js") {
|
|
2453
|
+
const strayLockfiles = scanParentLockfiles(process.cwd());
|
|
2454
|
+
if (strayLockfiles.length > 0) {
|
|
2455
|
+
console.log(chalk.yellow(" Found lockfiles in parent directories that confuse Turbopack:"));
|
|
2456
|
+
for (const f of strayLockfiles) {
|
|
2457
|
+
console.log(chalk.dim(` \u2022 ${f}`));
|
|
2458
|
+
}
|
|
2459
|
+
console.log("");
|
|
2460
|
+
console.log(chalk.dim(" Fix: remove them, or add to your next.config:"));
|
|
2461
|
+
console.log(chalk.cyan(" turbopack: { root: __dirname }"));
|
|
2462
|
+
} else {
|
|
2463
|
+
console.log(chalk.dim(" Common Next.js causes:"));
|
|
2464
|
+
console.log(chalk.dim(" \u2022 Missing src/app/page.tsx (App Router) or pages/index.tsx"));
|
|
2465
|
+
console.log(chalk.dim(" \u2022 Middleware redirecting all routes to an auth provider"));
|
|
2466
|
+
}
|
|
2467
|
+
} else if (detectedFramework === "Angular") {
|
|
2468
|
+
console.log(chalk.dim(" Angular hint: ensure the base href matches the proxy path."));
|
|
2469
|
+
} else if (detectedFramework === "Vite") {
|
|
2470
|
+
console.log(chalk.dim(" Vite hint: check that index.html exists in the project root."));
|
|
2471
|
+
} else {
|
|
2472
|
+
console.log(chalk.dim(" Check your framework's routing configuration."));
|
|
2473
|
+
}
|
|
2474
|
+
console.log("");
|
|
2475
|
+
console.log(chalk.dim(" The toolbar is still available \u2014 navigate to a working route."));
|
|
2476
|
+
console.log("");
|
|
2477
|
+
} else if (status >= 500) {
|
|
2478
|
+
console.log(chalk.yellow(` \u26A0 Your app returned HTTP ${status} on the root path.`));
|
|
2479
|
+
console.log(chalk.dim(" There may be a server-side error. Check your dev server output."));
|
|
2480
|
+
console.log("");
|
|
2481
|
+
}
|
|
2482
|
+
} catch {
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2362
2485
|
var program = new Command();
|
|
2363
2486
|
program.name("openmagic").description("AI-powered coding toolbar for any web application").version(VERSION2).option("-p, --port <port>", "Dev server port to proxy", "").option(
|
|
2364
2487
|
"-l, --listen <port>",
|
|
@@ -2434,9 +2557,26 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
|
|
|
2434
2557
|
}
|
|
2435
2558
|
}
|
|
2436
2559
|
}
|
|
2560
|
+
if (!detectedFramework) {
|
|
2561
|
+
const scripts = detectDevScripts();
|
|
2562
|
+
if (scripts.length > 0) detectedFramework = scripts[0].framework;
|
|
2563
|
+
}
|
|
2437
2564
|
console.log(
|
|
2438
2565
|
chalk.green(` \u2713 Dev server running at ${targetHost}:${targetPort}`)
|
|
2439
2566
|
);
|
|
2567
|
+
if (detectedFramework === "Next.js") {
|
|
2568
|
+
const strayLockfiles = scanParentLockfiles(process.cwd());
|
|
2569
|
+
if (strayLockfiles.length > 0) {
|
|
2570
|
+
console.log("");
|
|
2571
|
+
console.log(chalk.yellow(" \u26A0 Lockfiles found in parent directories:"));
|
|
2572
|
+
for (const f of strayLockfiles) {
|
|
2573
|
+
console.log(chalk.dim(` \u2022 ${f}`));
|
|
2574
|
+
}
|
|
2575
|
+
console.log(chalk.dim(" Next.js Turbopack may use the wrong workspace root, causing 404s."));
|
|
2576
|
+
console.log(chalk.dim(" Fix: remove them, or add to next.config:"));
|
|
2577
|
+
console.log(chalk.cyan(" turbopack: { root: __dirname }"));
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2440
2580
|
const roots = (opts.root || [process.cwd()]).map(
|
|
2441
2581
|
(r) => resolve3(r)
|
|
2442
2582
|
);
|
|
@@ -2462,6 +2602,7 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
|
|
|
2462
2602
|
);
|
|
2463
2603
|
console.log("");
|
|
2464
2604
|
await healthCheck(proxyPort, targetPort);
|
|
2605
|
+
await validateAppHealth(targetHost, targetPort);
|
|
2465
2606
|
console.log(chalk.dim(" Press Ctrl+C to stop."));
|
|
2466
2607
|
console.log(
|
|
2467
2608
|
chalk.dim(" Errors below are from your dev server, not OpenMagic.")
|
|
@@ -2625,6 +2766,18 @@ async function offerToStartDevServer(expectedPort) {
|
|
|
2625
2766
|
chosen = scripts[idx];
|
|
2626
2767
|
}
|
|
2627
2768
|
}
|
|
2769
|
+
detectedFramework = chosen.framework;
|
|
2770
|
+
const compat = checkNodeCompatibility(chosen.framework);
|
|
2771
|
+
if (!compat.ok) {
|
|
2772
|
+
console.log(chalk.red(`
|
|
2773
|
+
\u2717 ${compat.message}`));
|
|
2774
|
+
console.log("");
|
|
2775
|
+
console.log(chalk.white(" Switch Node.js version before running:"));
|
|
2776
|
+
console.log(chalk.cyan(" nvm use 20"));
|
|
2777
|
+
console.log(chalk.dim(" # then re-run: npx openmagic"));
|
|
2778
|
+
console.log("");
|
|
2779
|
+
return false;
|
|
2780
|
+
}
|
|
2628
2781
|
let port = expectedPort || chosen.defaultPort;
|
|
2629
2782
|
let portChanged = false;
|
|
2630
2783
|
if (await isPortOpen(port)) {
|
|
@@ -2750,6 +2903,14 @@ async function offerToStartDevServer(expectedPort) {
|
|
|
2750
2903
|
}
|
|
2751
2904
|
} catch {
|
|
2752
2905
|
}
|
|
2906
|
+
if (chosen?.framework) {
|
|
2907
|
+
const compat2 = checkNodeCompatibility(chosen.framework);
|
|
2908
|
+
if (!compat2.ok) {
|
|
2909
|
+
console.log(chalk.yellow(` ${compat2.message}`));
|
|
2910
|
+
console.log(chalk.dim(" Switch with: nvm use 20"));
|
|
2911
|
+
console.log("");
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2753
2914
|
console.log(chalk.white(" Options:"));
|
|
2754
2915
|
console.log(chalk.dim(" 1. Fix the error above and try again"));
|
|
2755
2916
|
console.log(chalk.dim(" 2. Start the server manually, then run:"));
|