openmagic 0.34.0 → 0.35.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/dist/cli.js +126 -2
- package/dist/cli.js.map +1 -1
- package/dist/toolbar/index.global.js +38 -38
- 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,56 @@ 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
|
+
}
|
|
2219
2275
|
function getProjectName(cwd = process.cwd()) {
|
|
2220
2276
|
const pkgPath = join4(cwd, "package.json");
|
|
2221
2277
|
if (!existsSync4(pkgPath)) return "this project";
|
|
@@ -2359,6 +2415,49 @@ async function healthCheck(proxyPort, _targetPort) {
|
|
|
2359
2415
|
}
|
|
2360
2416
|
console.log("");
|
|
2361
2417
|
}
|
|
2418
|
+
var detectedFramework = null;
|
|
2419
|
+
async function validateAppHealth(targetHost, targetPort) {
|
|
2420
|
+
try {
|
|
2421
|
+
const controller = new AbortController();
|
|
2422
|
+
const timeout = setTimeout(() => controller.abort(), 8e3);
|
|
2423
|
+
const res = await fetch(`http://${targetHost}:${targetPort}/`, {
|
|
2424
|
+
signal: controller.signal,
|
|
2425
|
+
redirect: "manual",
|
|
2426
|
+
headers: { Accept: "text/html" }
|
|
2427
|
+
});
|
|
2428
|
+
clearTimeout(timeout);
|
|
2429
|
+
const status = res.status;
|
|
2430
|
+
if (status >= 200 && status < 400) return;
|
|
2431
|
+
if (status === 404) {
|
|
2432
|
+
console.log(chalk.yellow(' \u26A0 Your app returned 404 for the root path ("/").'));
|
|
2433
|
+
console.log(chalk.dim(" The dev server is running, but no page matched."));
|
|
2434
|
+
console.log("");
|
|
2435
|
+
if (detectedFramework === "Next.js") {
|
|
2436
|
+
console.log(chalk.dim(" Common Next.js causes:"));
|
|
2437
|
+
console.log(chalk.dim(" \u2022 A stray package-lock.json in a parent directory confuses"));
|
|
2438
|
+
console.log(chalk.dim(" Turbopack's workspace root detection."));
|
|
2439
|
+
console.log(chalk.dim(" \u2192 Check for ~/package-lock.json and remove if unneeded"));
|
|
2440
|
+
console.log(chalk.dim(" \u2192 Or set turbopack.root in next.config (Next.js 15+)"));
|
|
2441
|
+
console.log(chalk.dim(" \u2022 Missing src/app/page.tsx (App Router) or pages/index.tsx"));
|
|
2442
|
+
console.log(chalk.dim(" \u2022 Middleware redirecting all routes to an auth provider"));
|
|
2443
|
+
} else if (detectedFramework === "Angular") {
|
|
2444
|
+
console.log(chalk.dim(" Angular hint: ensure the base href matches the proxy path."));
|
|
2445
|
+
} else if (detectedFramework === "Vite") {
|
|
2446
|
+
console.log(chalk.dim(" Vite hint: check that index.html exists in the project root."));
|
|
2447
|
+
} else {
|
|
2448
|
+
console.log(chalk.dim(" Check your framework's routing configuration."));
|
|
2449
|
+
}
|
|
2450
|
+
console.log("");
|
|
2451
|
+
console.log(chalk.dim(" The toolbar is still available \u2014 navigate to a working route."));
|
|
2452
|
+
console.log("");
|
|
2453
|
+
} else if (status >= 500) {
|
|
2454
|
+
console.log(chalk.yellow(` \u26A0 Your app returned HTTP ${status} on the root path.`));
|
|
2455
|
+
console.log(chalk.dim(" There may be a server-side error. Check your dev server output."));
|
|
2456
|
+
console.log("");
|
|
2457
|
+
}
|
|
2458
|
+
} catch {
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2362
2461
|
var program = new Command();
|
|
2363
2462
|
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
2463
|
"-l, --listen <port>",
|
|
@@ -2434,6 +2533,10 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
|
|
|
2434
2533
|
}
|
|
2435
2534
|
}
|
|
2436
2535
|
}
|
|
2536
|
+
if (!detectedFramework) {
|
|
2537
|
+
const scripts = detectDevScripts();
|
|
2538
|
+
if (scripts.length > 0) detectedFramework = scripts[0].framework;
|
|
2539
|
+
}
|
|
2437
2540
|
console.log(
|
|
2438
2541
|
chalk.green(` \u2713 Dev server running at ${targetHost}:${targetPort}`)
|
|
2439
2542
|
);
|
|
@@ -2462,6 +2565,7 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
|
|
|
2462
2565
|
);
|
|
2463
2566
|
console.log("");
|
|
2464
2567
|
await healthCheck(proxyPort, targetPort);
|
|
2568
|
+
await validateAppHealth(targetHost, targetPort);
|
|
2465
2569
|
console.log(chalk.dim(" Press Ctrl+C to stop."));
|
|
2466
2570
|
console.log(
|
|
2467
2571
|
chalk.dim(" Errors below are from your dev server, not OpenMagic.")
|
|
@@ -2625,6 +2729,18 @@ async function offerToStartDevServer(expectedPort) {
|
|
|
2625
2729
|
chosen = scripts[idx];
|
|
2626
2730
|
}
|
|
2627
2731
|
}
|
|
2732
|
+
detectedFramework = chosen.framework;
|
|
2733
|
+
const compat = checkNodeCompatibility(chosen.framework);
|
|
2734
|
+
if (!compat.ok) {
|
|
2735
|
+
console.log(chalk.red(`
|
|
2736
|
+
\u2717 ${compat.message}`));
|
|
2737
|
+
console.log("");
|
|
2738
|
+
console.log(chalk.white(" Switch Node.js version before running:"));
|
|
2739
|
+
console.log(chalk.cyan(" nvm use 20"));
|
|
2740
|
+
console.log(chalk.dim(" # then re-run: npx openmagic"));
|
|
2741
|
+
console.log("");
|
|
2742
|
+
return false;
|
|
2743
|
+
}
|
|
2628
2744
|
let port = expectedPort || chosen.defaultPort;
|
|
2629
2745
|
let portChanged = false;
|
|
2630
2746
|
if (await isPortOpen(port)) {
|
|
@@ -2750,6 +2866,14 @@ async function offerToStartDevServer(expectedPort) {
|
|
|
2750
2866
|
}
|
|
2751
2867
|
} catch {
|
|
2752
2868
|
}
|
|
2869
|
+
if (chosen?.framework) {
|
|
2870
|
+
const compat2 = checkNodeCompatibility(chosen.framework);
|
|
2871
|
+
if (!compat2.ok) {
|
|
2872
|
+
console.log(chalk.yellow(` ${compat2.message}`));
|
|
2873
|
+
console.log(chalk.dim(" Switch with: nvm use 20"));
|
|
2874
|
+
console.log("");
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2753
2877
|
console.log(chalk.white(" Options:"));
|
|
2754
2878
|
console.log(chalk.dim(" 1. Fix the error above and try again"));
|
|
2755
2879
|
console.log(chalk.dim(" 2. Start the server manually, then run:"));
|