openmagic 0.41.1 → 0.42.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 CHANGED
@@ -8,7 +8,7 @@ import { resolve as resolve3, join as join6 } from "path";
8
8
  import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
9
9
  import { spawn as spawn5, execSync as execSync2 } from "child_process";
10
10
  import http2 from "http";
11
- import { createInterface } from "readline";
11
+ import { createInterface, clearLine, cursorTo } from "readline";
12
12
 
13
13
  // src/proxy.ts
14
14
  import http from "http";
@@ -2859,20 +2859,139 @@ function checkDependenciesInstalled(cwd = process.cwd()) {
2859
2859
 
2860
2860
  // src/cli.ts
2861
2861
  import { createRequire as createRequire2 } from "module";
2862
+ try {
2863
+ execSync2("ulimit -n 65536", { shell: true, stdio: "ignore" });
2864
+ } catch {
2865
+ }
2862
2866
  var origEmitWarning = process.emitWarning;
2863
2867
  process.emitWarning = function(warning, ...args) {
2864
2868
  if (typeof warning === "string" && warning.includes("util._extend")) return;
2865
2869
  return origEmitWarning.call(process, warning, ...args);
2866
2870
  };
2871
+ var INDENT = " ";
2872
+ var LABEL_WIDTH = 10;
2873
+ var activeStatusLine = false;
2874
+ function clearActiveStatus() {
2875
+ if (!activeStatusLine || !process.stdout.isTTY) return;
2876
+ clearLine(process.stdout, 0);
2877
+ cursorTo(process.stdout, 0);
2878
+ activeStatusLine = false;
2879
+ }
2880
+ function writeLine(line = "") {
2881
+ clearActiveStatus();
2882
+ process.stdout.write(line ? `${line}
2883
+ ` : "\n");
2884
+ }
2885
+ function formatInfo(message) {
2886
+ return chalk.dim(`${INDENT}${message}`);
2887
+ }
2888
+ function formatPending(message) {
2889
+ return chalk.dim(`${INDENT}\u25CF ${message}`);
2890
+ }
2891
+ function formatSuccess(message) {
2892
+ return chalk.greenBright(`${INDENT}\u2713 ${message}`);
2893
+ }
2894
+ function formatReady(seconds = process.uptime()) {
2895
+ return chalk.greenBright(`${INDENT}\u2713 Ready in ${seconds.toFixed(1)}s`);
2896
+ }
2897
+ function formatWarning(message) {
2898
+ return chalk.yellow(`${INDENT}\u25B2 ${message}`);
2899
+ }
2900
+ function formatError(message) {
2901
+ return chalk.red(`${INDENT}\u2717 ${message}`);
2902
+ }
2903
+ function printInfo(message) {
2904
+ writeLine(formatInfo(message));
2905
+ }
2906
+ function printSuccess(message) {
2907
+ writeLine(formatSuccess(message));
2908
+ }
2909
+ function printWarning(message) {
2910
+ writeLine(formatWarning(message));
2911
+ }
2912
+ function printError(message) {
2913
+ writeLine(formatError(message));
2914
+ }
2915
+ function printDetail(message, formatter = chalk.dim) {
2916
+ writeLine(formatter(`${INDENT} ${message}`));
2917
+ }
2918
+ function printCommand(message) {
2919
+ printDetail(message, chalk.cyan);
2920
+ }
2921
+ function printLocation(label, value, brightValue = false) {
2922
+ const prefix = chalk.dim(`${INDENT}\u279C ${`${label}:`.padEnd(LABEL_WIDTH)}`);
2923
+ const renderedValue = brightValue ? chalk.whiteBright(value) : chalk.dim(value);
2924
+ writeLine(`${prefix}${renderedValue}`);
2925
+ }
2926
+ function startInlineStatus(message) {
2927
+ clearActiveStatus();
2928
+ const line = formatPending(message);
2929
+ if (!process.stdout.isTTY) {
2930
+ writeLine(line);
2931
+ return;
2932
+ }
2933
+ process.stdout.write(line);
2934
+ activeStatusLine = true;
2935
+ }
2936
+ function replaceInlineStatus(line) {
2937
+ if (!process.stdout.isTTY) {
2938
+ writeLine(line);
2939
+ return;
2940
+ }
2941
+ clearLine(process.stdout, 0);
2942
+ cursorTo(process.stdout, 0);
2943
+ process.stdout.write(`${line}
2944
+ `);
2945
+ activeStatusLine = false;
2946
+ }
2947
+ function finishInlineStatus(message) {
2948
+ replaceInlineStatus(formatSuccess(message));
2949
+ }
2950
+ function warnInlineStatus(message) {
2951
+ replaceInlineStatus(formatWarning(message));
2952
+ }
2953
+ function failInlineStatus(message) {
2954
+ replaceInlineStatus(formatError(message));
2955
+ }
2956
+ function finishInlineReady() {
2957
+ replaceInlineStatus(formatReady());
2958
+ }
2959
+ function getDetectedFrameworkLabel() {
2960
+ if (detectedFramework) return detectedFramework;
2961
+ const scripts = detectDevScripts();
2962
+ return scripts.length > 0 ? scripts[0].framework : null;
2963
+ }
2867
2964
  process.on("unhandledRejection", (err) => {
2868
- console.error(chalk.red("\n [OpenMagic] Unhandled error:"), err?.message || err);
2869
- console.error(chalk.dim(" Please report this at https://github.com/Kalmuraee/OpenMagic/issues"));
2965
+ writeLine();
2966
+ printError(`Unhandled error: ${err?.message || err}`);
2967
+ printDetail("Please report this at https://github.com/Kalmuraee/OpenMagic/issues");
2870
2968
  });
2871
2969
  process.on("uncaughtException", (err) => {
2872
- console.error(chalk.red("\n [OpenMagic] Fatal error:"), err.message);
2873
- console.error(chalk.dim(" Please report this at https://github.com/Kalmuraee/OpenMagic/issues"));
2970
+ writeLine();
2971
+ printError(`Fatal error: ${err.message}`);
2972
+ printDetail("Please report this at https://github.com/Kalmuraee/OpenMagic/issues");
2874
2973
  process.exit(1);
2875
2974
  });
2975
+ try {
2976
+ const fdLimit = parseInt(execSync2("ulimit -n", { encoding: "utf-8", shell: true }).trim(), 10);
2977
+ if (fdLimit > 0 && fdLimit < 4096) {
2978
+ try {
2979
+ execSync2("ulimit -n 65536", { shell: true, stdio: "ignore" });
2980
+ } catch {
2981
+ }
2982
+ const newLimit = parseInt(execSync2("ulimit -n", { encoding: "utf-8", shell: true }).trim(), 10);
2983
+ if (newLimit < 4096) {
2984
+ writeLine();
2985
+ printWarning(`File descriptor limit is ${fdLimit} (need 4096+).`);
2986
+ printDetail("This can cause EMFILE errors in large Next.js and Turbopack projects.");
2987
+ printDetail("Add this to your shell profile:");
2988
+ printCommand("ulimit -n 65536");
2989
+ printDetail("Then restart your terminal.");
2990
+ writeLine();
2991
+ }
2992
+ }
2993
+ } catch {
2994
+ }
2876
2995
  var childProcesses = [];
2877
2996
  var lastDetectedPort = null;
2878
2997
  var _require2 = createRequire2(import.meta.url);
@@ -2973,15 +3092,13 @@ function runCommand(cmd, args, cwd = process.cwd()) {
2973
3092
  child.stdout?.on("data", (data) => {
2974
3093
  const lines = data.toString().trim().split("\n");
2975
3094
  for (const line of lines) {
2976
- if (line.trim()) process.stdout.write(chalk.dim(` \u2502 ${line}
2977
- `));
3095
+ if (line.trim()) writeLine(chalk.dim(`${INDENT}\u2502 ${line}`));
2978
3096
  }
2979
3097
  });
2980
3098
  child.stderr?.on("data", (data) => {
2981
3099
  const lines = data.toString().trim().split("\n");
2982
3100
  for (const line of lines) {
2983
- if (line.trim()) process.stdout.write(chalk.dim(` \u2502 ${line}
2984
- `));
3101
+ if (line.trim()) writeLine(chalk.dim(`${INDENT}\u2502 ${line}`));
2985
3102
  }
2986
3103
  });
2987
3104
  child.on("error", () => resolve4(false));
@@ -2999,20 +3116,17 @@ async function healthCheck(proxyPort, _targetPort) {
2999
3116
  signal: controller.signal
3000
3117
  });
3001
3118
  clearTimeout(timeout);
3002
- if (res.ok) {
3003
- console.log(chalk.green(" \u2713 Toolbar ready."));
3004
- } else {
3005
- console.log(chalk.yellow(" \u26A0 Proxy started but toolbar health check failed."));
3006
- }
3119
+ if (res.ok) return null;
3120
+ return {
3121
+ message: "Proxy started, but the toolbar health check failed.",
3122
+ details: ["Try refreshing the page in a few seconds."]
3123
+ };
3007
3124
  } catch {
3008
- console.log(
3009
- chalk.yellow(" \u26A0 Could not verify proxy. The dev server may still be starting.")
3010
- );
3011
- console.log(
3012
- chalk.dim(" Try refreshing the page in a few seconds.")
3013
- );
3125
+ return {
3126
+ message: "Could not verify the proxy while it was starting.",
3127
+ details: ["Try refreshing the page in a few seconds."]
3128
+ };
3014
3129
  }
3015
- console.log("");
3016
3130
  }
3017
3131
  var detectedFramework = null;
3018
3132
  async function validateAppHealth(targetHost, targetPort) {
@@ -3033,39 +3147,36 @@ async function validateAppHealth(targetHost, targetPort) {
3033
3147
  continue;
3034
3148
  }
3035
3149
  if (status === 404) {
3036
- console.log(chalk.yellow(' \u26A0 Your app returned 404 for the root path ("/").'));
3037
- console.log(chalk.dim(" The dev server is running, but no page matched."));
3038
- console.log("");
3150
+ printWarning("Your app returned 404 for the root path (/).");
3151
+ printDetail("The dev server is running, but no page matched the root path.");
3039
3152
  if (detectedFramework === "Next.js") {
3040
3153
  const strayLockfiles = scanParentLockfiles(process.cwd());
3041
3154
  if (strayLockfiles.length > 0) {
3042
- console.log(chalk.yellow(" Found lockfiles in parent directories that confuse Turbopack:"));
3155
+ printDetail("Found lockfiles in parent directories that can confuse Turbopack.");
3043
3156
  for (const f of strayLockfiles) {
3044
- console.log(chalk.dim(` \u2022 ${f}`));
3157
+ printDetail(`- ${f}`);
3045
3158
  }
3046
- console.log("");
3047
- console.log(chalk.dim(" Fix: remove them, or add to your next.config:"));
3048
- console.log(chalk.cyan(" turbopack: { root: __dirname }"));
3159
+ printDetail("Fix it by removing those lockfiles, or add this to next.config:");
3160
+ printCommand("turbopack: { root: __dirname }");
3049
3161
  } else {
3050
- console.log(chalk.dim(" Common Next.js causes:"));
3051
- console.log(chalk.dim(" \u2022 Missing src/app/page.tsx (App Router) or pages/index.tsx"));
3052
- console.log(chalk.dim(" \u2022 Middleware redirecting all routes to an auth provider"));
3162
+ printDetail("Common Next.js causes:");
3163
+ printDetail("- Missing src/app/page.tsx (App Router) or pages/index.tsx");
3164
+ printDetail("- Middleware redirecting all routes to an auth provider");
3053
3165
  }
3054
3166
  } else if (detectedFramework === "Angular") {
3055
- console.log(chalk.dim(" Angular hint: ensure the base href matches the proxy path."));
3167
+ printDetail("Angular hint: make sure the base href matches the proxy path.");
3056
3168
  } else if (detectedFramework === "Vite") {
3057
- console.log(chalk.dim(" Vite hint: check that index.html exists in the project root."));
3169
+ printDetail("Vite hint: check that index.html exists in the project root.");
3058
3170
  } else {
3059
- console.log(chalk.dim(" Check your framework's routing configuration."));
3171
+ printDetail("Check your framework's routing configuration.");
3060
3172
  }
3061
- console.log("");
3062
- console.log(chalk.dim(" The toolbar is still available \u2014 navigate to a working route."));
3063
- console.log("");
3173
+ printDetail("The toolbar is still available \u2014 navigate to a working route.");
3174
+ writeLine();
3064
3175
  return false;
3065
3176
  } else if (status >= 500) {
3066
- console.log(chalk.yellow(` \u26A0 Your app returned HTTP ${status} on the root path.`));
3067
- console.log(chalk.dim(" There may be a server-side error. Check your dev server output."));
3068
- console.log("");
3177
+ printWarning(`Your app returned HTTP ${status} on the root path.`);
3178
+ printDetail("There may be a server-side error. Check your dev server output.");
3179
+ writeLine();
3069
3180
  if (attempt < 4) {
3070
3181
  await new Promise((r) => setTimeout(r, 2e3));
3071
3182
  continue;
@@ -3090,12 +3201,9 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
3090
3201
  "-r, --root <paths...>",
3091
3202
  "Project root directories (defaults to cwd)"
3092
3203
  ).option("--no-open", "Don't auto-open browser").option("--host <host>", "Dev server host", "localhost").action(async (opts) => {
3093
- console.log("");
3094
- console.log(
3095
- " \u{1FA84} " + chalk.bold.hex("#6c5ce7")("OpenMagic") + chalk.dim(` v${VERSION2}`) + " \u2728"
3096
- );
3097
- console.log(chalk.dim(" AI coding toolbar for any web app"));
3098
- console.log("");
3204
+ writeLine();
3205
+ writeLine(`${INDENT}${chalk.white("OpenMagic")} ${chalk.dim(`v${VERSION2}`)}`);
3206
+ writeLine();
3099
3207
  let targetPort;
3100
3208
  let targetHost = opts.host;
3101
3209
  if (opts.port) {
@@ -3115,18 +3223,22 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
3115
3223
  targetHost = recheck.host;
3116
3224
  }
3117
3225
  }
3226
+ const frameworkLabel = getDetectedFrameworkLabel() ?? "dev server";
3227
+ printSuccess(`Found ${frameworkLabel} on port ${targetPort}`);
3118
3228
  }
3119
3229
  } else {
3120
- console.log(chalk.dim(" Scanning for dev server..."));
3230
+ startInlineStatus("Scanning for dev server...");
3121
3231
  const detected = await detectDevServer();
3122
3232
  if (detected && detected.fromScripts) {
3123
3233
  const healthy = await isPortHealthy(detected.host, detected.port);
3124
3234
  if (healthy) {
3125
3235
  targetPort = detected.port;
3126
3236
  targetHost = detected.host;
3237
+ const frameworkLabel = getDetectedFrameworkLabel() ?? "dev server";
3238
+ finishInlineStatus(`Found ${frameworkLabel} on port ${detected.port}`);
3127
3239
  } else {
3128
- console.log(chalk.yellow(` \u26A0 Dev server on port ${detected.port} is not responding properly.`));
3129
- console.log(chalk.dim(" Cleaning up orphaned process..."));
3240
+ warnInlineStatus(`Dev server on port ${detected.port} is not responding`);
3241
+ printDetail("Cleaning up the orphaned process...");
3130
3242
  killPortProcess(detected.port);
3131
3243
  const freed = await waitForPortClose(detected.port, 5e3);
3132
3244
  if (!freed) {
@@ -3157,27 +3269,33 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
3157
3269
  targetPort = redetected.port;
3158
3270
  targetHost = redetected.host;
3159
3271
  } else {
3160
- console.log(chalk.red(" \u2717 Could not detect the dev server after starting."));
3161
- console.log(chalk.dim(" Try specifying the port: npx openmagic --port 3000"));
3272
+ printError("Could not detect the dev server after starting.");
3273
+ printDetail("Try specifying the port manually:");
3274
+ printCommand("npx openmagic --port 3000");
3162
3275
  process.exit(1);
3163
3276
  }
3164
3277
  }
3278
+ const frameworkLabel = getDetectedFrameworkLabel() ?? "dev server";
3279
+ printSuccess(`Found ${frameworkLabel} on port ${targetPort}`);
3165
3280
  }
3166
3281
  } else if (detected && !detected.fromScripts) {
3282
+ finishInlineStatus(`Found dev server on port ${detected.port}`);
3167
3283
  const answer = await ask(
3168
- chalk.yellow(` Found a server on port ${detected.port}. Is this your project's dev server? `) + chalk.dim("(y/n) ")
3284
+ chalk.yellow(`${INDENT}\u25B2 Found a server on port ${detected.port}. Is this your project's dev server? `) + chalk.dim("(Y/n) ")
3169
3285
  );
3170
3286
  if (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes" || answer === "") {
3171
3287
  targetPort = detected.port;
3172
3288
  targetHost = detected.host;
3173
3289
  } else {
3174
- console.log("");
3175
- console.log(chalk.dim(" Start your dev server, then run:"));
3176
- console.log(chalk.cyan(" npx openmagic --port <your-port>"));
3177
- console.log("");
3290
+ writeLine(formatInfo("Cancelled dev server selection."));
3291
+ writeLine();
3292
+ printInfo("Start your dev server, then run:");
3293
+ printCommand("npx openmagic --port <your-port>");
3294
+ writeLine();
3178
3295
  process.exit(0);
3179
3296
  }
3180
3297
  } else {
3298
+ warnInlineStatus("No dev server found. Starting one...");
3181
3299
  const started = await offerToStartDevServer();
3182
3300
  if (!started) {
3183
3301
  process.exit(1);
@@ -3187,34 +3305,34 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
3187
3305
  } else {
3188
3306
  const redetected = await detectDevServer();
3189
3307
  if (!redetected) {
3190
- console.log(chalk.red(" \u2717 Could not detect the dev server after starting."));
3191
- console.log(chalk.dim(" Try specifying the port: npx openmagic --port 3000"));
3192
- console.log("");
3308
+ printError("Could not detect the dev server after starting.");
3309
+ printDetail("Try specifying the port manually:");
3310
+ printCommand("npx openmagic --port 3000");
3311
+ writeLine();
3193
3312
  process.exit(1);
3194
3313
  }
3195
3314
  targetPort = redetected.port;
3196
3315
  targetHost = redetected.host;
3197
3316
  }
3317
+ const frameworkLabel = getDetectedFrameworkLabel() ?? "dev server";
3318
+ printSuccess(`Found ${frameworkLabel} on port ${targetPort}`);
3198
3319
  }
3199
3320
  }
3200
3321
  if (!detectedFramework) {
3201
3322
  const scripts = detectDevScripts();
3202
3323
  if (scripts.length > 0) detectedFramework = scripts[0].framework;
3203
3324
  }
3204
- console.log(
3205
- chalk.green(` \u2713 Dev server running at ${targetHost}:${targetPort}`)
3206
- );
3207
3325
  if (detectedFramework === "Next.js") {
3208
3326
  const strayLockfiles = scanParentLockfiles(process.cwd());
3209
3327
  if (strayLockfiles.length > 0) {
3210
- console.log("");
3211
- console.log(chalk.yellow(" \u26A0 Lockfiles found in parent directories:"));
3328
+ writeLine();
3329
+ printWarning("Lockfiles found in parent directories.");
3212
3330
  for (const f of strayLockfiles) {
3213
- console.log(chalk.dim(` \u2022 ${f}`));
3331
+ printDetail(`- ${f}`);
3214
3332
  }
3215
- console.log(chalk.dim(" Next.js Turbopack may use the wrong workspace root, causing 404s."));
3216
- console.log(chalk.dim(" Fix: remove them, or add to next.config:"));
3217
- console.log(chalk.cyan(" turbopack: { root: __dirname }"));
3333
+ printDetail("Next.js Turbopack may use the wrong workspace root and cause 404s.");
3334
+ printDetail("Fix it by removing those lockfiles, or add this to next.config:");
3335
+ printCommand("turbopack: { root: __dirname }");
3218
3336
  }
3219
3337
  }
3220
3338
  const roots = (opts.root || [process.cwd()]).map(
@@ -3223,35 +3341,41 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
3223
3341
  const config = loadConfig();
3224
3342
  saveConfig({ ...config, roots, targetPort });
3225
3343
  generateSessionToken();
3226
- let proxyPort = parseInt(opts.listen, 10);
3344
+ const requestedProxyPort = parseInt(opts.listen, 10);
3345
+ let proxyPort = requestedProxyPort;
3227
3346
  while (await isPortOpen(proxyPort)) {
3228
3347
  proxyPort++;
3229
- if (proxyPort > parseInt(opts.listen, 10) + 100) {
3230
- console.log(chalk.red(" Could not find an available port."));
3348
+ if (proxyPort > requestedProxyPort + 100) {
3349
+ printError("Could not find an available port for the OpenMagic proxy.");
3231
3350
  process.exit(1);
3232
3351
  }
3233
3352
  }
3353
+ if (proxyPort !== requestedProxyPort) {
3354
+ writeLine();
3355
+ printWarning(`Port ${requestedProxyPort} is in use \u2014 starting on ${proxyPort}`);
3356
+ }
3234
3357
  const proxyServer = createProxyServer(targetHost, targetPort, roots);
3235
3358
  proxyServer.listen(proxyPort, "localhost", async () => {
3236
3359
  const proxyUrl = `http://localhost:${proxyPort}`;
3237
- console.log("");
3238
- console.log(chalk.bold.green(" Ready!"));
3239
- console.log("");
3240
- console.log(
3241
- chalk.bold(" \u2192 ") + chalk.bold.underline.cyan(proxyUrl)
3242
- );
3243
- console.log("");
3244
- await healthCheck(proxyPort, targetPort);
3245
- console.log(chalk.dim(" Waiting for app to compile..."));
3246
- const appReady = await validateAppHealth(targetHost, targetPort);
3247
- if (appReady) {
3248
- console.log(chalk.green(" \u2713 App is ready."));
3249
- }
3250
- console.log(chalk.dim(" Press Ctrl+C to stop."));
3251
- console.log(
3252
- chalk.dim(" Errors below are from your dev server, not OpenMagic.")
3253
- );
3254
- console.log("");
3360
+ const proxyWarning = await healthCheck(proxyPort, targetPort);
3361
+ const frameworkLabel = getDetectedFrameworkLabel();
3362
+ const targetLabel = frameworkLabel ? `${targetHost}:${targetPort} (${frameworkLabel})` : `${targetHost}:${targetPort}`;
3363
+ printLocation("Local", proxyUrl, true);
3364
+ printLocation("Target", targetLabel);
3365
+ writeLine();
3366
+ startInlineStatus("Waiting for app to compile...");
3367
+ await validateAppHealth(targetHost, targetPort);
3368
+ finishInlineReady();
3369
+ if (proxyWarning) {
3370
+ writeLine();
3371
+ printWarning(proxyWarning.message);
3372
+ for (const detail of proxyWarning.details || []) {
3373
+ printDetail(detail);
3374
+ }
3375
+ }
3376
+ writeLine();
3377
+ printInfo("Press Ctrl+C to stop");
3378
+ writeLine();
3255
3379
  if (opts.open !== false) {
3256
3380
  open(`http://localhost:${proxyPort}`).catch(() => {
3257
3381
  });
@@ -3261,8 +3385,8 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
3261
3385
  const shutdown = async () => {
3262
3386
  if (shuttingDown) return;
3263
3387
  shuttingDown = true;
3264
- console.log("");
3265
- console.log(chalk.dim(" Shutting down OpenMagic..."));
3388
+ writeLine();
3389
+ printInfo("Shutting down OpenMagic...");
3266
3390
  cleanupBackups();
3267
3391
  proxyServer.close();
3268
3392
  const alive = childProcesses.filter((cp) => cp.exitCode === null);
@@ -3278,7 +3402,7 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
3278
3402
  if (targetPort && await isPortOpen(targetPort)) {
3279
3403
  const freed = await waitForPortClose(targetPort, 4e3);
3280
3404
  if (!freed) {
3281
- console.log(chalk.dim(" Force-killing remaining processes..."));
3405
+ printInfo("Force-killing remaining processes...");
3282
3406
  if (targetPort) {
3283
3407
  try {
3284
3408
  const pids = execSync2(`lsof -i :${targetPort} -sTCP:LISTEN -t 2>/dev/null`, {
@@ -3341,18 +3465,16 @@ async function offerToStartDevServer(expectedPort) {
3341
3465
  if (scripts.length === 0) {
3342
3466
  const htmlPath = join6(process.cwd(), "index.html");
3343
3467
  if (existsSync6(htmlPath)) {
3344
- console.log(
3345
- chalk.dim(" No dev scripts found, but index.html detected.")
3346
- );
3347
- console.log("");
3468
+ printInfo("No dev scripts found, but index.html was detected.");
3469
+ writeLine();
3348
3470
  const answer = await ask(
3349
- chalk.white(" Serve this directory as a static site? ") + chalk.dim("(Y/n) ")
3471
+ chalk.dim(`${INDENT}Serve this directory as a static site? `) + chalk.dim("(Y/n) ")
3350
3472
  );
3351
3473
  if (answer.toLowerCase() === "n" || answer.toLowerCase() === "no") {
3352
3474
  return false;
3353
3475
  }
3354
3476
  const staticPort = expectedPort || 8080;
3355
- console.log(chalk.dim(` Starting static server on port ${staticPort}...`));
3477
+ startInlineStatus(`Starting static server on port ${staticPort}...`);
3356
3478
  const staticChild = spawn5("node", ["-e", `
3357
3479
  const http = require("http");
3358
3480
  const fs = require("fs");
@@ -3367,7 +3489,7 @@ async function offerToStartDevServer(expectedPort) {
3367
3489
  res.writeHead(200, {"Content-Type": mimes[ext] || "application/octet-stream"});
3368
3490
  res.end(data);
3369
3491
  });
3370
- }).listen(${staticPort}, "localhost", () => console.log("Static server ready on port ${staticPort}"));
3492
+ }).listen(${staticPort}, "localhost");
3371
3493
  `], {
3372
3494
  cwd: process.cwd(),
3373
3495
  stdio: ["ignore", "pipe", "pipe"],
@@ -3376,99 +3498,90 @@ async function offerToStartDevServer(expectedPort) {
3376
3498
  childProcesses.push(staticChild);
3377
3499
  staticChild.stdout?.on("data", (d) => {
3378
3500
  for (const line of d.toString().trim().split("\n")) {
3379
- if (line.trim()) process.stdout.write(chalk.dim(` \u2502 ${line}
3380
- `));
3501
+ if (line.trim()) writeLine(chalk.dim(`${INDENT}\u2502 ${line}`));
3381
3502
  }
3382
3503
  });
3383
3504
  const up = await waitForPort(staticPort, 5e3);
3384
3505
  if (up) {
3385
3506
  lastDetectedPort = staticPort;
3386
- console.log(chalk.green(` \u2713 Static server running on port ${staticPort}`));
3507
+ finishInlineStatus(`Static server running on port ${staticPort}`);
3387
3508
  return true;
3388
3509
  }
3389
- console.log(chalk.red(" \u2717 Failed to start static server."));
3510
+ failInlineStatus("Static server failed to start");
3390
3511
  return false;
3391
3512
  }
3392
- console.log(
3393
- chalk.yellow(" \u26A0 No dev server detected and no dev scripts found.")
3394
- );
3395
- console.log("");
3396
- console.log(chalk.white(" Start your dev server manually, then run:"));
3397
- console.log(chalk.cyan(" npx openmagic --port <your-port>"));
3398
- console.log("");
3513
+ printWarning("No dev server detected and no dev scripts were found.");
3514
+ writeLine();
3515
+ printInfo("Start your dev server manually, then run:");
3516
+ printCommand("npx openmagic --port <your-port>");
3517
+ writeLine();
3399
3518
  return false;
3400
3519
  }
3401
3520
  const deps = checkDependenciesInstalled();
3402
3521
  if (!deps.installed) {
3403
- console.log(
3404
- chalk.yellow(" \u26A0 node_modules/ not found. Dependencies need to be installed.")
3405
- );
3406
- console.log("");
3522
+ printWarning("node_modules was not found. Dependencies need to be installed.");
3523
+ writeLine();
3407
3524
  const answer = await ask(
3408
- chalk.white(` Run `) + chalk.cyan(deps.installCommand) + chalk.white("? ") + chalk.dim("(Y/n) ")
3525
+ chalk.white(`${INDENT}Run `) + chalk.cyan(deps.installCommand) + chalk.white("? ") + chalk.dim("(Y/n) ")
3409
3526
  );
3410
3527
  if (answer.toLowerCase() === "n" || answer.toLowerCase() === "no") {
3411
- console.log("");
3412
- console.log(chalk.dim(` Run ${deps.installCommand} manually, then try again.`));
3413
- console.log("");
3528
+ writeLine();
3529
+ printInfo(`Run ${deps.installCommand} manually, then try again.`);
3530
+ writeLine();
3414
3531
  return false;
3415
3532
  }
3416
- console.log("");
3417
- console.log(chalk.dim(` Installing dependencies with ${deps.packageManager}...`));
3533
+ writeLine();
3534
+ printInfo(`Installing dependencies with ${deps.packageManager}...`);
3418
3535
  const [installCmd, ...installArgs] = deps.installCommand.split(" ");
3419
3536
  const installed = await runCommand(installCmd, installArgs);
3420
3537
  if (!installed) {
3421
- console.log(chalk.red(" \u2717 Dependency installation failed."));
3422
- console.log(chalk.dim(` Try running ${deps.installCommand} manually.`));
3423
- console.log("");
3538
+ printError("Dependency installation failed.");
3539
+ printDetail(`Try running ${deps.installCommand} manually.`);
3540
+ writeLine();
3424
3541
  return false;
3425
3542
  }
3426
- console.log(chalk.green(" \u2713 Dependencies installed."));
3427
- console.log("");
3543
+ printSuccess("Dependencies installed.");
3544
+ writeLine();
3428
3545
  }
3429
3546
  let chosen = scripts[0];
3430
3547
  if (scripts.length === 1) {
3431
- console.log(
3432
- chalk.yellow(" \u26A0 No dev server detected.")
3433
- );
3434
- console.log("");
3435
- console.log(
3436
- chalk.white(` Found `) + chalk.cyan(`npm run ${chosen.name}`) + chalk.white(` in ${projectName}`) + chalk.dim(` (${chosen.framework})`)
3548
+ printWarning("No dev server detected.");
3549
+ writeLine();
3550
+ writeLine(
3551
+ chalk.white(`${INDENT}Found `) + chalk.cyan(`npm run ${chosen.name}`) + chalk.white(` in ${projectName}`) + chalk.dim(` (${chosen.framework})`)
3437
3552
  );
3438
- console.log(chalk.dim(` \u2192 ${chosen.command}`));
3439
- console.log("");
3553
+ printDetail(`\u279C ${chosen.command}`);
3554
+ writeLine();
3440
3555
  const answer = await ask(
3441
- chalk.white(` Start it now? `) + chalk.dim("(Y/n) ")
3556
+ chalk.white(`${INDENT}Start it now? `) + chalk.dim("(Y/n) ")
3442
3557
  );
3443
3558
  if (answer.toLowerCase() === "n" || answer.toLowerCase() === "no") {
3444
- console.log("");
3445
- console.log(chalk.dim(" Start your dev server first, then run openmagic again."));
3446
- console.log("");
3559
+ writeLine();
3560
+ printInfo("Start your dev server first, then run OpenMagic again.");
3561
+ writeLine();
3447
3562
  return false;
3448
3563
  }
3449
3564
  } else {
3450
- console.log(
3451
- chalk.yellow(" \u26A0 No dev server detected.")
3565
+ printWarning("No dev server detected.");
3566
+ writeLine();
3567
+ writeLine(
3568
+ chalk.white(`${INDENT}Found ${scripts.length} dev scripts in ${projectName}:`)
3452
3569
  );
3453
- console.log("");
3454
- console.log(
3455
- chalk.white(` Found ${scripts.length} dev scripts in ${projectName}:`)
3456
- );
3457
- console.log("");
3570
+ writeLine();
3458
3571
  scripts.forEach((s, i) => {
3459
- console.log(
3460
- chalk.cyan(` ${i + 1}) `) + chalk.white(`npm run ${s.name}`) + chalk.dim(` \u2014 ${s.framework} (port ${s.defaultPort})`)
3572
+ writeLine(
3573
+ chalk.cyan(`${INDENT}${i + 1}. `) + chalk.white(`npm run ${s.name}`) + chalk.dim(` (${s.framework}, port ${s.defaultPort})`)
3461
3574
  );
3462
- console.log(chalk.dim(` ${s.command}`));
3575
+ printDetail(s.command);
3463
3576
  });
3464
- console.log("");
3577
+ writeLine();
3465
3578
  const answer = await ask(
3466
- chalk.white(` Which one to start? `) + chalk.dim(`(1-${scripts.length}, or n to cancel) `)
3579
+ chalk.white(`${INDENT}Which one should OpenMagic start? `) + chalk.dim(`(1-${scripts.length}, or n to cancel) `)
3467
3580
  );
3468
3581
  if (answer.toLowerCase() === "n" || answer.toLowerCase() === "no" || answer === "") {
3469
- console.log("");
3470
- console.log(chalk.dim(" Start your dev server first, then run openmagic again."));
3471
- console.log("");
3582
+ writeLine();
3583
+ printInfo("Start your dev server first, then run OpenMagic again.");
3584
+ writeLine();
3472
3585
  return false;
3473
3586
  }
3474
3587
  const idx = parseInt(answer, 10) - 1;
@@ -3481,13 +3594,13 @@ async function offerToStartDevServer(expectedPort) {
3481
3594
  detectedFramework = chosen.framework;
3482
3595
  const compat = checkNodeCompatibility(chosen.framework);
3483
3596
  if (!compat.ok) {
3484
- console.log(chalk.red(`
3485
- \u2717 ${compat.message}`));
3486
- console.log("");
3487
- console.log(chalk.white(" Switch Node.js version before running:"));
3488
- console.log(chalk.cyan(" nvm use 20"));
3489
- console.log(chalk.dim(" # then re-run: npx openmagic"));
3490
- console.log("");
3597
+ writeLine();
3598
+ printError(compat.message);
3599
+ writeLine();
3600
+ printInfo("Switch Node.js before running:");
3601
+ printCommand("nvm use 20");
3602
+ printDetail("Then re-run: npx openmagic");
3603
+ writeLine();
3491
3604
  return false;
3492
3605
  }
3493
3606
  let port = expectedPort || chosen.defaultPort;
@@ -3495,24 +3608,19 @@ async function offerToStartDevServer(expectedPort) {
3495
3608
  if (await isPortOpen(port)) {
3496
3609
  const owned = verifyPortOwnership(port, process.cwd());
3497
3610
  if (owned === true) {
3498
- console.log(chalk.green(` \u2713 Dev server already running on port ${port}`));
3611
+ printSuccess(`Dev server already running on port ${port}`);
3499
3612
  lastDetectedPort = port;
3500
3613
  return true;
3501
3614
  }
3502
3615
  const altPort = await findAvailablePort(port + 1);
3503
- console.log("");
3504
- console.log(
3505
- chalk.yellow(` \u26A0 Port ${port} is already in use by another process.`)
3506
- );
3507
- console.log(
3508
- chalk.dim(` Starting on port ${altPort} instead.`)
3509
- );
3616
+ writeLine();
3617
+ printWarning(`Port ${port} is in use \u2014 starting on ${altPort}`);
3510
3618
  port = altPort;
3511
3619
  portChanged = true;
3512
3620
  }
3513
- console.log("");
3514
- console.log(
3515
- chalk.dim(` Starting `) + chalk.cyan(`npm run ${chosen.name}`) + (portChanged ? chalk.dim(` (port ${port})`) : "") + chalk.dim("...")
3621
+ writeLine();
3622
+ writeLine(
3623
+ chalk.dim(`${INDENT}\u25CF Starting `) + chalk.cyan(`npm run ${chosen.name}`) + (portChanged ? chalk.dim(` (port ${port})`) : "") + chalk.dim("...")
3516
3624
  );
3517
3625
  const depsInfo = checkDependenciesInstalled();
3518
3626
  const runCmd = depsInfo.packageManager === "yarn" ? "yarn" : depsInfo.packageManager === "pnpm" ? "pnpm" : depsInfo.packageManager === "bun" ? "bun" : "npm";
@@ -3527,7 +3635,9 @@ async function offerToStartDevServer(expectedPort) {
3527
3635
  }
3528
3636
  let child;
3529
3637
  try {
3530
- child = spawn5(runCmd, runArgs, {
3638
+ const escapedArgs = runArgs.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
3639
+ const shellCmd = `ulimit -n 65536 2>/dev/null; exec ${runCmd} ${escapedArgs}`;
3640
+ child = spawn5("sh", ["-c", shellCmd], {
3531
3641
  cwd: process.cwd(),
3532
3642
  stdio: "inherit",
3533
3643
  env: {
@@ -3538,21 +3648,21 @@ async function offerToStartDevServer(expectedPort) {
3538
3648
  }
3539
3649
  });
3540
3650
  } catch (e) {
3541
- console.log(chalk.red(` \u2717 Failed to start: ${e.message}`));
3651
+ printError(`Failed to start: ${e.message}`);
3542
3652
  return false;
3543
3653
  }
3544
3654
  childProcesses.push(child);
3545
3655
  let childExited = false;
3546
3656
  child.on("error", (err) => {
3547
3657
  childExited = true;
3548
- console.log(chalk.red(`
3549
- \u2717 Failed to start: ${err.message}`));
3658
+ writeLine();
3659
+ printError(`Failed to start: ${err.message}`);
3550
3660
  });
3551
3661
  child.on("exit", (code) => {
3552
3662
  childExited = true;
3553
3663
  if (code !== null && code !== 0) {
3554
- console.log(chalk.red(`
3555
- \u2717 Dev server exited with code ${code}`));
3664
+ writeLine();
3665
+ printError(`Dev server exited with code ${code}`);
3556
3666
  }
3557
3667
  });
3558
3668
  process.once("exit", () => {
@@ -3565,13 +3675,11 @@ async function offerToStartDevServer(expectedPort) {
3565
3675
  }
3566
3676
  }
3567
3677
  });
3568
- console.log(
3569
- chalk.dim(` Waiting for dev server on port ${port}...`)
3570
- );
3678
+ writeLine(formatPending(`Waiting for ${chosen.framework} on port ${port}...`));
3571
3679
  const isUp = await waitForPort(port, 6e4, () => childExited);
3572
3680
  if (isUp) {
3573
3681
  lastDetectedPort = port;
3574
- console.log("");
3682
+ printSuccess(`${chosen.framework} is listening on port ${port}`);
3575
3683
  return true;
3576
3684
  }
3577
3685
  if (!childExited) {
@@ -3580,28 +3688,23 @@ async function offerToStartDevServer(expectedPort) {
3580
3688
  if (await isPortOpen(scanPort)) {
3581
3689
  const owned = verifyPortOwnership(scanPort, process.cwd());
3582
3690
  if (owned === false) continue;
3583
- console.log(
3584
- chalk.green(`
3585
- \u2713 Dev server found on port ${scanPort}.`)
3586
- );
3691
+ printSuccess(`Found ${chosen.framework} on port ${scanPort}`);
3587
3692
  lastDetectedPort = scanPort;
3588
3693
  return true;
3589
3694
  }
3590
3695
  }
3591
3696
  }
3592
3697
  if (childExited) {
3593
- console.log(
3594
- chalk.red(` \u2717 Dev server failed to start.`)
3595
- );
3596
- console.log("");
3698
+ printError("Dev server failed to start");
3699
+ writeLine();
3597
3700
  try {
3598
3701
  const pkgPath = join6(process.cwd(), "package.json");
3599
3702
  if (existsSync6(pkgPath)) {
3600
3703
  const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
3601
3704
  if (pkg.engines?.node) {
3602
- console.log(chalk.yellow(` This project requires Node.js ${pkg.engines.node}`));
3603
- console.log(chalk.dim(` You are running Node.js ${process.version}`));
3604
- console.log("");
3705
+ printWarning(`This project requires Node.js ${pkg.engines.node}`);
3706
+ printDetail(`You are running Node.js ${process.version}`);
3707
+ writeLine();
3605
3708
  }
3606
3709
  }
3607
3710
  } catch {
@@ -3609,26 +3712,24 @@ async function offerToStartDevServer(expectedPort) {
3609
3712
  if (chosen?.framework) {
3610
3713
  const compat2 = checkNodeCompatibility(chosen.framework);
3611
3714
  if (!compat2.ok) {
3612
- console.log(chalk.yellow(` ${compat2.message}`));
3613
- console.log(chalk.dim(" Switch with: nvm use 20"));
3614
- console.log("");
3715
+ printWarning(compat2.message);
3716
+ printDetail("Switch with:");
3717
+ printCommand("nvm use 20");
3718
+ writeLine();
3615
3719
  }
3616
3720
  }
3617
- console.log(chalk.white(" Options:"));
3618
- console.log(chalk.dim(" 1. Fix the error above and try again"));
3619
- console.log(chalk.dim(" 2. Start the server manually, then run:"));
3620
- console.log(chalk.cyan(" npx openmagic --port <your-port>"));
3621
- console.log("");
3721
+ writeLine(chalk.white(`${INDENT}Options:`));
3722
+ printDetail("1. Fix the error above and try again");
3723
+ printDetail("2. Start the server manually, then run:");
3724
+ printCommand("npx openmagic --port <your-port>");
3725
+ writeLine();
3622
3726
  return false;
3623
3727
  }
3624
- console.log(
3625
- chalk.yellow(`
3626
- \u26A0 Could not find the dev server after 60s.`)
3627
- );
3628
- console.log(chalk.dim(` Check the output above for errors.`));
3629
- console.log(chalk.dim(` Or start the server manually, then run:`));
3630
- console.log(chalk.cyan(` npx openmagic --port <your-port>`));
3631
- console.log("");
3728
+ printWarning("Could not find the dev server after 60s");
3729
+ printDetail("Check the output above for errors.");
3730
+ printDetail("Or start the server manually, then run:");
3731
+ printCommand("npx openmagic --port <your-port>");
3732
+ writeLine();
3632
3733
  return false;
3633
3734
  }
3634
3735
  program.parse();