@absolutejs/absolute 0.19.0-beta.1015 → 0.19.0-beta.1017

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.
Files changed (44) hide show
  1. package/dist/angular/browser.js +2 -2
  2. package/dist/angular/browser.js.map +3 -3
  3. package/dist/angular/components/constants.js +13 -0
  4. package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
  5. package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
  6. package/dist/angular/index.js +2 -2
  7. package/dist/angular/index.js.map +3 -3
  8. package/dist/angular/server.js +2 -2
  9. package/dist/angular/server.js.map +3 -3
  10. package/dist/build.js +3 -3
  11. package/dist/build.js.map +4 -4
  12. package/dist/cli/config/server.js +33576 -33407
  13. package/dist/cli/index.js +1163 -300
  14. package/dist/index.js +100 -10
  15. package/dist/index.js.map +7 -6
  16. package/dist/islands/index.js +2 -2
  17. package/dist/islands/index.js.map +3 -3
  18. package/dist/react/components/index.js +2 -2
  19. package/dist/react/components/index.js.map +3 -3
  20. package/dist/react/index.js +2 -2
  21. package/dist/react/index.js.map +3 -3
  22. package/dist/react/server.js +2 -2
  23. package/dist/react/server.js.map +3 -3
  24. package/dist/src/angular/components/constants.d.ts +13 -0
  25. package/dist/src/cli/instanceStatus.d.ts +21 -0
  26. package/dist/src/cli/listTui.d.ts +1 -0
  27. package/dist/src/cli/scripts/list.d.ts +1 -0
  28. package/dist/src/cli/tuiPrimitives.d.ts +14 -0
  29. package/dist/src/constants.d.ts +13 -0
  30. package/dist/src/utils/instanceRegistry.d.ts +13 -0
  31. package/dist/svelte/index.js +2 -2
  32. package/dist/svelte/index.js.map +3 -3
  33. package/dist/svelte/server.js +2 -2
  34. package/dist/svelte/server.js.map +3 -3
  35. package/dist/types/cli.d.ts +39 -0
  36. package/dist/vue/components/Image.js +2 -2
  37. package/dist/vue/components/Image.js.map +3 -3
  38. package/dist/vue/components/index.js +2 -2
  39. package/dist/vue/components/index.js.map +3 -3
  40. package/dist/vue/index.js +2 -2
  41. package/dist/vue/index.js.map +3 -3
  42. package/dist/vue/server.js +2 -2
  43. package/dist/vue/server.js.map +3 -3
  44. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -18,7 +18,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
18
18
  var __require = import.meta.require;
19
19
 
20
20
  // src/constants.ts
21
- var ANSI_ESCAPE_CODE = 27, ANSI_ESCAPE_LENGTH = 3, ASCII_SPACE = 32, BASE_36_RADIX = 36, BYTES_PER_KILOBYTE = 1024, CLI_ARGS_OFFSET = 3, DEFAULT_PORT = 3000, HTTP_STATUS_OK = 200, HOURS_IN_DAY = 24, HOURS_IN_HALF_DAY = 12, MAX_ERROR_LENGTH = 200, MILLISECONDS_IN_A_SECOND = 1000, MINUTES_IN_AN_HOUR = 60, SECONDS_IN_A_MINUTE = 60, MILLISECONDS_IN_A_MINUTE, MILLISECONDS_IN_A_DAY, SIGINT_EXIT_CODE = 130, SIGTERM_EXIT_CODE = 143, TIME_PRECISION = 2, TWO_THIRDS, UNFOUND_INDEX = -1, WORKSPACE_COMMAND_ARGS_OFFSET = 3, WORKSPACE_FAILURE_LOG_PRINT_LIMIT = 30, WORKSPACE_FAILURE_RECENT_LOG_LIMIT = 60, WORKSPACE_READY_ATTEMPT_TIMEOUT_MS = 5000, WORKSPACE_READY_PROBE_INTERVAL_MS = 250, WORKSPACE_READY_TIMEOUT_MS = 30000, WORKSPACE_SHUTDOWN_TIMEOUT_MS = 1e4, WORKSPACE_TUI_DEFAULT_HEIGHT = 28, WORKSPACE_TUI_DEFAULT_WIDTH = 100, WORKSPACE_TUI_ESCAPE_SEQUENCE_TIMEOUT_MS = 30, WORKSPACE_TUI_FOOTER_LINE_COUNT = 3, WORKSPACE_TUI_MIN_LOG_HEIGHT = 3, WORKSPACE_TUI_MIN_SERVICE_NAME_WIDTH = 7, WORKSPACE_TUI_MIN_TARGET_WIDTH = 8, WORKSPACE_TUI_MIN_WRAP_WIDTH = 12, WORKSPACE_TUI_PROMPT_CURSOR_OFFSET = 3, WORKSPACE_TUI_RECENT_LOG_LIMIT = 40, WORKSPACE_TUI_RENDER_DEBOUNCE_MS = 16, WORKSPACE_TUI_STATUS_WIDTH = 10, WORKSPACE_TUI_TARGET_PADDING_WIDTH = 6, WORKSPACE_TUI_VISIBILITY_WIDTH = 8;
21
+ var ANSI_ESCAPE_CODE = 27, ANSI_ESCAPE_LENGTH = 3, ASCII_SPACE = 32, BASE_36_RADIX = 36, BYTES_PER_KILOBYTE = 1024, CLI_ARGS_OFFSET = 3, DEFAULT_PORT = 3000, EXCLUDE_LAST_OFFSET = -1, HTTP_STATUS_OK = 200, HOURS_IN_DAY = 24, HOURS_IN_HALF_DAY = 12, INSTANCE_PROBE_TIMEOUT_MS = 250, MAX_ERROR_LENGTH = 200, MILLISECONDS_IN_A_SECOND = 1000, MINUTES_IN_AN_HOUR = 60, SECONDS_IN_A_MINUTE = 60, MILLISECONDS_IN_A_MINUTE, MILLISECONDS_IN_A_DAY, LIST_LOG_TAIL_MAX_BYTES = 65536, LIST_TUI_COLUMN_GAP = 2, LIST_TUI_DEFAULT_HEIGHT = 28, LIST_TUI_DEFAULT_WIDTH = 100, LIST_TUI_ESCAPE_SEQUENCE_TIMEOUT_MS = 30, LIST_TUI_FOOTER_LINE_COUNT = 2, LIST_TUI_MARKER_WIDTH = 2, LIST_TUI_MIN_LOG_HEIGHT = 3, LIST_TUI_MIN_URL_WIDTH = 16, LIST_TUI_RENDER_DEBOUNCE_MS = 16, LIST_TUI_STATUS_MESSAGE_TIMEOUT_MS = 4000, LIST_WATCH_REFRESH_MS = 1000, SIGINT_EXIT_CODE = 130, SIGTERM_EXIT_CODE = 143, TIME_PRECISION = 2, TWO_THIRDS, UNFOUND_INDEX = -1, WORKSPACE_COMMAND_ARGS_OFFSET = 3, WORKSPACE_FAILURE_LOG_PRINT_LIMIT = 30, WORKSPACE_FAILURE_RECENT_LOG_LIMIT = 60, WORKSPACE_READY_ATTEMPT_TIMEOUT_MS = 5000, WORKSPACE_READY_PROBE_INTERVAL_MS = 250, WORKSPACE_READY_TIMEOUT_MS = 30000, WORKSPACE_SHUTDOWN_TIMEOUT_MS = 1e4, WORKSPACE_TUI_DEFAULT_HEIGHT = 28, WORKSPACE_TUI_DEFAULT_WIDTH = 100, WORKSPACE_TUI_ESCAPE_SEQUENCE_TIMEOUT_MS = 30, WORKSPACE_TUI_FOOTER_LINE_COUNT = 3, WORKSPACE_TUI_MIN_LOG_HEIGHT = 3, WORKSPACE_TUI_MIN_SERVICE_NAME_WIDTH = 7, WORKSPACE_TUI_MIN_TARGET_WIDTH = 8, WORKSPACE_TUI_MIN_WRAP_WIDTH = 12, WORKSPACE_TUI_PROMPT_CURSOR_OFFSET = 3, WORKSPACE_TUI_RECENT_LOG_LIMIT = 40, WORKSPACE_TUI_RENDER_DEBOUNCE_MS = 16, WORKSPACE_TUI_STATUS_WIDTH = 10, WORKSPACE_TUI_TARGET_PADDING_WIDTH = 6, WORKSPACE_TUI_VISIBILITY_WIDTH = 8;
22
22
  var init_constants = __esm(() => {
23
23
  MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
24
24
  MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_DAY;
@@ -391,6 +391,120 @@ var init_buildDirectoryLock = __esm(() => {
391
391
  heldLocks = new Map;
392
392
  });
393
393
 
394
+ // src/utils/instanceRegistry.ts
395
+ import {
396
+ mkdirSync as mkdirSync3,
397
+ readFileSync as readFileSync4,
398
+ readdirSync,
399
+ unlinkSync as unlinkSync2,
400
+ writeFileSync as writeFileSync3
401
+ } from "fs";
402
+ import { homedir as homedir2 } from "os";
403
+ import { basename, join as join4 } from "path";
404
+ var registeredPids, exitHandlerRegistered = false, instanceFilePath = (pid) => join4(instanceRegistryDir(), `${pid}.json`), instanceLogPath = (pid) => join4(instanceRegistryDir(), `${pid}.log`), instanceRegistryDir = () => join4(homedir2(), ".absolutejs", "instances"), removeInstanceFilesSync = (pid) => {
405
+ try {
406
+ unlinkSync2(instanceFilePath(pid));
407
+ } catch {}
408
+ try {
409
+ unlinkSync2(instanceLogPath(pid));
410
+ } catch {}
411
+ }, registerExitHandlerOnce = () => {
412
+ if (exitHandlerRegistered)
413
+ return;
414
+ exitHandlerRegistered = true;
415
+ process.on("exit", () => {
416
+ for (const pid of registeredPids) {
417
+ removeInstanceFilesSync(pid);
418
+ }
419
+ registeredPids.clear();
420
+ });
421
+ }, isProcessAlive2 = (pid) => {
422
+ try {
423
+ process.kill(pid, 0);
424
+ return true;
425
+ } catch (error) {
426
+ const code = error instanceof Error && "code" in error ? error.code : undefined;
427
+ if (code === "ESRCH")
428
+ return false;
429
+ return true;
430
+ }
431
+ }, readJsonFile = (path) => {
432
+ try {
433
+ return JSON.parse(readFileSync4(path, "utf-8"));
434
+ } catch {
435
+ return null;
436
+ }
437
+ }, SOURCES, toStringArray = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [], coerceSource = (value) => SOURCES.find((item) => item === value) ?? "standalone", coerceRecord = (parsed) => {
438
+ if (typeof parsed !== "object" || parsed === null)
439
+ return null;
440
+ if (typeof parsed.pid !== "number")
441
+ return null;
442
+ return {
443
+ command: toStringArray(parsed.command),
444
+ configPath: typeof parsed.configPath === "string" ? parsed.configPath : null,
445
+ controllerPid: typeof parsed.controllerPid === "number" ? parsed.controllerPid : parsed.pid,
446
+ cwd: typeof parsed.cwd === "string" ? parsed.cwd : "",
447
+ frameworks: toStringArray(parsed.frameworks),
448
+ host: typeof parsed.host === "string" ? parsed.host : "localhost",
449
+ https: parsed.https === true,
450
+ logFile: typeof parsed.logFile === "string" ? parsed.logFile : null,
451
+ name: typeof parsed.name === "string" ? parsed.name : "unknown",
452
+ pid: parsed.pid,
453
+ port: typeof parsed.port === "number" ? parsed.port : null,
454
+ ppid: typeof parsed.ppid === "number" ? parsed.ppid : 0,
455
+ source: coerceSource(parsed.source),
456
+ startedAt: typeof parsed.startedAt === "string" ? parsed.startedAt : new Date().toISOString()
457
+ };
458
+ }, readRecordFile = (path) => coerceRecord(readJsonFile(path)), compareInstances = (left, right) => {
459
+ const leftPort = left.port ?? Number.MAX_SAFE_INTEGER;
460
+ const rightPort = right.port ?? Number.MAX_SAFE_INTEGER;
461
+ if (leftPort !== rightPort)
462
+ return leftPort - rightPort;
463
+ return left.name.localeCompare(right.name);
464
+ }, deregisterInstance = (pid) => {
465
+ registeredPids.delete(pid);
466
+ removeInstanceFilesSync(pid);
467
+ }, listLiveInstances = () => {
468
+ const directory = instanceRegistryDir();
469
+ let entries;
470
+ try {
471
+ entries = readdirSync(directory);
472
+ } catch {
473
+ return [];
474
+ }
475
+ const live = entries.filter((entry) => entry.endsWith(".json")).map((entry) => readRecordFile(join4(directory, entry))).filter((record) => record !== null).filter((record) => {
476
+ if (isProcessAlive2(record.pid))
477
+ return true;
478
+ removeInstanceFilesSync(record.pid);
479
+ return false;
480
+ });
481
+ return live.sort(compareInstances);
482
+ }, registerInstance = (record) => {
483
+ mkdirSync3(instanceRegistryDir(), { recursive: true });
484
+ writeFileSync3(instanceFilePath(record.pid), JSON.stringify(record, null, 2));
485
+ registeredPids.add(record.pid);
486
+ registerExitHandlerOnce();
487
+ return record;
488
+ }, resolveProjectName = (cwd) => {
489
+ const parsed = readJsonFile(join4(cwd, "package.json"));
490
+ if (parsed !== null && typeof parsed === "object" && typeof parsed.name === "string" && parsed.name.trim().length > 0) {
491
+ return parsed.name;
492
+ }
493
+ return basename(cwd) || "unknown";
494
+ }, updateInstance = (pid, updates) => {
495
+ const current = readRecordFile(instanceFilePath(pid));
496
+ if (!current)
497
+ return;
498
+ const next = { ...current, ...updates };
499
+ try {
500
+ writeFileSync3(instanceFilePath(pid), JSON.stringify(next, null, 2));
501
+ } catch {}
502
+ };
503
+ var init_instanceRegistry = __esm(() => {
504
+ registeredPids = new Set;
505
+ SOURCES = ["dev", "standalone", "start", "workspace"];
506
+ });
507
+
394
508
  // src/utils/loadConfig.ts
395
509
  import { resolve } from "path";
396
510
  var RESERVED_TOP_LEVEL_KEYS, isObject = (value) => typeof value === "object" && value !== null, isCommandService = (service) => service.kind === "command" || Array.isArray(service.command), isServiceCandidate = (value) => isObject(value) && (typeof value.entry === "string" || Array.isArray(value.command)), isWorkspaceConfig = (config) => {
@@ -495,11 +609,11 @@ var init_loadConfig = __esm(() => {
495
609
  // src/cli/utils.ts
496
610
  var {$ } = globalThis.Bun;
497
611
  import { execSync } from "child_process";
498
- import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
612
+ import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
499
613
  import { resolve as resolve2 } from "path";
500
614
  var COMPOSE_PATH = "db/docker-compose.db.yml", DEFAULT_SERVER_ENTRY = "src/backend/server.ts", isWSLEnvironment = () => {
501
615
  try {
502
- const release = readFileSync4("/proc/version", "utf-8");
616
+ const release = readFileSync5("/proc/version", "utf-8");
503
617
  return /microsoft|wsl/i.test(release);
504
618
  } catch {
505
619
  return false;
@@ -532,6 +646,32 @@ var COMPOSE_PATH = "db/docker-compose.db.yml", DEFAULT_SERVER_ENTRY = "src/backe
532
646
  return;
533
647
  }
534
648
  console.log(`\x1B[2m${formatTimestamp()}\x1B[0m \x1B[33m[cli]\x1B[0m \x1B[33m${message}\x1B[0m`);
649
+ }, openUrlInBrowser = (url, onError) => {
650
+ if (process.env.ABSOLUTE_NO_OPEN)
651
+ return false;
652
+ const { platform: platform2 } = process;
653
+ const isWSL = platform2 === "linux" && isWSLEnvironment();
654
+ let command;
655
+ if (isWSL) {
656
+ command = "cmd.exe";
657
+ } else if (platform2 === "darwin") {
658
+ command = "open";
659
+ } else if (platform2 === "win32") {
660
+ command = "start";
661
+ } else {
662
+ command = "xdg-open";
663
+ }
664
+ const commandArgs = isWSL ? ["/c", "start", url] : [url];
665
+ try {
666
+ Bun.spawn([command, ...commandArgs], {
667
+ stderr: "ignore",
668
+ stdout: "ignore"
669
+ });
670
+ return true;
671
+ } catch {
672
+ onError?.(`Could not open browser automatically. Visit ${url}`);
673
+ return false;
674
+ }
535
675
  }, printHelp = (subject = "server") => {
536
676
  const title = subject === "workspace" ? "workspace" : subject;
537
677
  console.log("");
@@ -592,15 +732,15 @@ __export(exports_devCert, {
592
732
  import {
593
733
  copyFileSync,
594
734
  existsSync as existsSync4,
595
- mkdirSync as mkdirSync3,
596
- readFileSync as readFileSync5,
735
+ mkdirSync as mkdirSync4,
736
+ readFileSync as readFileSync6,
597
737
  rmSync
598
738
  } from "fs";
599
739
  import { platform as platform2 } from "os";
600
- import { join as join4 } from "path";
740
+ import { join as join5 } from "path";
601
741
  var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, devLog = (msg) => console.log(`\x1B[2m${new Date().toLocaleTimeString()}\x1B[0m \x1B[36m[dev]\x1B[0m ${msg}`), devWarn = (msg) => console.log(`\x1B[2m${new Date().toLocaleTimeString()}\x1B[0m \x1B[33m[dev]\x1B[0m \x1B[33m${msg}\x1B[0m`), certFilesExist = () => existsSync4(CERT_PATH) && existsSync4(KEY_PATH), isCertExpired = () => {
602
742
  try {
603
- const certPem = readFileSync5(CERT_PATH, "utf-8");
743
+ const certPem = readFileSync6(CERT_PATH, "utf-8");
604
744
  const proc = Bun.spawnSync(["openssl", "x509", "-enddate", "-noout"], {
605
745
  stdin: new TextEncoder().encode(certPem)
606
746
  });
@@ -669,7 +809,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, devLog = (msg) => c
669
809
  generateSelfSigned();
670
810
  }
671
811
  }, ensureDevCert = () => {
672
- mkdirSync3(CERT_DIR, { recursive: true });
812
+ mkdirSync4(CERT_DIR, { recursive: true });
673
813
  if (hasCert()) {
674
814
  return { cert: CERT_PATH, key: KEY_PATH };
675
815
  }
@@ -689,8 +829,8 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, devLog = (msg) => c
689
829
  return null;
690
830
  try {
691
831
  return {
692
- cert: readFileSync5(paths.cert, "utf-8"),
693
- key: readFileSync5(paths.key, "utf-8")
832
+ cert: readFileSync6(paths.cert, "utf-8"),
833
+ key: readFileSync6(paths.key, "utf-8")
694
834
  };
695
835
  } catch {
696
836
  return null;
@@ -786,7 +926,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, devLog = (msg) => c
786
926
  if (platform2() !== "linux")
787
927
  return false;
788
928
  try {
789
- return /microsoft|wsl/i.test(readFileSync5("/proc/version", "utf-8"));
929
+ return /microsoft|wsl/i.test(readFileSync6("/proc/version", "utf-8"));
790
930
  } catch {
791
931
  return false;
792
932
  }
@@ -809,13 +949,13 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, devLog = (msg) => c
809
949
  const caRoot = mkcertCaRoot();
810
950
  if (!caRoot)
811
951
  return false;
812
- const rootCa = join4(caRoot, "rootCA.pem");
952
+ const rootCa = join5(caRoot, "rootCA.pem");
813
953
  if (!existsSync4(rootCa))
814
954
  return false;
815
955
  const winTemp = windowsTempDir();
816
956
  if (!winTemp)
817
957
  return false;
818
- const staged = join4(winTemp, "absolutejs-mkcert-rootCA.crt");
958
+ const staged = join5(winTemp, "absolutejs-mkcert-rootCA.crt");
819
959
  try {
820
960
  copyFileSync(rootCa, staged);
821
961
  } catch {
@@ -851,7 +991,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, devLog = (msg) => c
851
991
  devLog("Trusted the local CA in the Windows store \u2014 Chrome/Edge on Windows now accept dev HTTPS");
852
992
  } else {
853
993
  const caRoot = mkcertCaRoot();
854
- const hint = caRoot ? toWindowsPath(join4(caRoot, "rootCA.pem")) : null;
994
+ const hint = caRoot ? toWindowsPath(join5(caRoot, "rootCA.pem")) : null;
855
995
  devWarn("Could not auto-trust the local CA on Windows; Windows browsers may warn.");
856
996
  if (hint) {
857
997
  console.log(` Run in PowerShell: Import-Certificate -FilePath "${hint}" -CertStoreLocation Cert:\\CurrentUser\\Root`);
@@ -860,16 +1000,16 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, devLog = (msg) => c
860
1000
  }
861
1001
  rmSync(CERT_PATH, { force: true });
862
1002
  rmSync(KEY_PATH, { force: true });
863
- mkdirSync3(CERT_DIR, { recursive: true });
1003
+ mkdirSync4(CERT_DIR, { recursive: true });
864
1004
  generateWithMkcert();
865
1005
  console.log("");
866
1006
  devLog("mkcert installed \u2014 HTTPS certificates are now locally trusted");
867
1007
  return true;
868
1008
  };
869
1009
  var init_devCert = __esm(() => {
870
- CERT_DIR = join4(process.cwd(), ".absolutejs");
871
- CERT_PATH = join4(CERT_DIR, "cert.pem");
872
- KEY_PATH = join4(CERT_DIR, "key.pem");
1010
+ CERT_DIR = join5(process.cwd(), ".absolutejs");
1011
+ CERT_PATH = join5(CERT_DIR, "cert.pem");
1012
+ KEY_PATH = join5(CERT_DIR, "key.pem");
873
1013
  });
874
1014
 
875
1015
  // src/core/prerender.ts
@@ -882,15 +1022,15 @@ __export(exports_prerender, {
882
1022
  prerender: () => prerender,
883
1023
  PRERENDER_BYPASS_HEADER: () => PRERENDER_BYPASS_HEADER
884
1024
  });
885
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync9 } from "fs";
886
- import { join as join6 } from "path";
1025
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync10 } from "fs";
1026
+ import { join as join7 } from "path";
887
1027
  var SERVER_OUTPUT_LIMIT = 4000, STARTUP_POLL_INTERVAL_MS = 100, DEFAULT_STARTUP_TIMEOUT_MS = 30000, PRERENDER_BYPASS_HEADER = "X-Absolute-Prerender-Bypass", routeToFilename = (route) => route === "/" ? "index.html" : `${route.slice(1).replace(/\//g, "-")}.html`, writeTimestamp = async (htmlPath) => {
888
1028
  const metaPath = htmlPath.replace(/\.html$/, ".meta");
889
1029
  await Bun.write(metaPath, String(Date.now()));
890
1030
  }, readTimestamp = (htmlPath) => {
891
1031
  const metaPath = htmlPath.replace(/\.html$/, ".meta");
892
1032
  try {
893
- const content = readFileSync9(metaPath, "utf-8");
1033
+ const content = readFileSync10(metaPath, "utf-8");
894
1034
  return Number(content) || 0;
895
1035
  } catch {
896
1036
  return 0;
@@ -949,7 +1089,7 @@ var SERVER_OUTPUT_LIMIT = 4000, STARTUP_POLL_INTERVAL_MS = 100, DEFAULT_STARTUP_
949
1089
  return false;
950
1090
  const html = await res.text();
951
1091
  const fileName = routeToFilename(route);
952
- const filePath = join6(prerenderDir, fileName);
1092
+ const filePath = join7(prerenderDir, fileName);
953
1093
  await Bun.write(filePath, html);
954
1094
  await writeTimestamp(filePath);
955
1095
  return true;
@@ -975,14 +1115,14 @@ var SERVER_OUTPUT_LIMIT = 4000, STARTUP_POLL_INTERVAL_MS = 100, DEFAULT_STARTUP_
975
1115
  }
976
1116
  const html = await res.text();
977
1117
  const fileName = routeToFilename(route);
978
- const filePath = join6(prerenderDir, fileName);
1118
+ const filePath = join7(prerenderDir, fileName);
979
1119
  await Bun.write(filePath, html);
980
1120
  await writeTimestamp(filePath);
981
1121
  result.routes.set(route, filePath);
982
1122
  log?.(` Pre-rendered ${route} \u2192 ${fileName} (${html.length} bytes)`);
983
1123
  }, prerender = async (port, outDir, staticConfig, log) => {
984
- const prerenderDir = join6(outDir, "_prerendered");
985
- mkdirSync4(prerenderDir, { recursive: true });
1124
+ const prerenderDir = join7(outDir, "_prerendered");
1125
+ mkdirSync5(prerenderDir, { recursive: true });
986
1126
  const baseUrl = `http://localhost:${port}`;
987
1127
  let routes;
988
1128
  if (staticConfig.routes === "all") {
@@ -1138,7 +1278,7 @@ var init_nativeRewrite = __esm(() => {
1138
1278
 
1139
1279
  // src/build/rewriteImportsPlugin.ts
1140
1280
  import { readdir } from "fs/promises";
1141
- import { join as join7 } from "path";
1281
+ import { join as join8 } from "path";
1142
1282
  var escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), jsRewriteImports = (content, replacements) => {
1143
1283
  let result = content;
1144
1284
  for (const [specifier, webPath] of replacements) {
@@ -1216,7 +1356,7 @@ ${content}`;
1216
1356
  const entries = await readdir(dir);
1217
1357
  for (const entry of entries) {
1218
1358
  if (entry.endsWith(".js"))
1219
- allFiles.push(join7(dir, entry));
1359
+ allFiles.push(join8(dir, entry));
1220
1360
  }
1221
1361
  } catch {}
1222
1362
  }
@@ -1270,86 +1410,837 @@ var rewriteImports = async (outputPaths, vendorPaths) => {
1270
1410
  return;
1271
1411
  throw err;
1272
1412
  }
1273
- const rewritten = rewriteImportsInContent(original, vendorPaths);
1274
- if (rewritten === original)
1413
+ const rewritten = rewriteImportsInContent(original, vendorPaths);
1414
+ if (rewritten === original)
1415
+ return;
1416
+ try {
1417
+ await Bun.write(filePath, rewritten);
1418
+ } catch (err) {
1419
+ const code = err.code;
1420
+ if (code === "ENOENT")
1421
+ return;
1422
+ throw err;
1423
+ }
1424
+ }));
1425
+ }, rewriteVendorDirectories2;
1426
+ var init_rewriteImports = __esm(() => {
1427
+ init_rewriteImportsPlugin();
1428
+ rewriteVendorDirectories2 = rewriteVendorDirectories;
1429
+ });
1430
+
1431
+ // src/cli/tuiPrimitives.ts
1432
+ import { openSync as openSync2 } from "fs";
1433
+ import { ReadStream as ReadStream2 } from "tty";
1434
+ var ANSI_REGEX, trySetRawMode2 = () => {
1435
+ if (typeof process.stdin.setRawMode !== "function") {
1436
+ return null;
1437
+ }
1438
+ try {
1439
+ process.stdin.setRawMode(true);
1440
+ } catch {
1441
+ return null;
1442
+ }
1443
+ return process.stdin;
1444
+ }, splitLongWord = (word, width) => {
1445
+ const parts = [];
1446
+ for (let index = 0;index < word.length; index += width) {
1447
+ parts.push(word.slice(index, index + width));
1448
+ }
1449
+ return parts;
1450
+ }, appendWrappedWord = (lines, current, word, width) => {
1451
+ if (current.length === 0) {
1452
+ if (word.length <= width)
1453
+ return word;
1454
+ lines.push(...splitLongWord(word, width));
1455
+ return "";
1456
+ }
1457
+ const next = `${current} ${word}`;
1458
+ if (next.length <= width)
1459
+ return next;
1460
+ lines.push(current);
1461
+ if (word.length <= width)
1462
+ return word;
1463
+ lines.push(...splitLongWord(word, width));
1464
+ return "";
1465
+ }, wrapLine = (line, width) => {
1466
+ if (line.length === 0)
1467
+ return [""];
1468
+ if (line.length <= width)
1469
+ return [line];
1470
+ const lines = [];
1471
+ let current = "";
1472
+ for (const word of line.split(/\s+/)) {
1473
+ current = appendWrappedWord(lines, current, word, width);
1474
+ }
1475
+ if (current.length > 0)
1476
+ lines.push(current);
1477
+ return lines;
1478
+ }, ANSI_ESCAPE_PREFIX = "\x1B[", colors, ESCAPE = "\x1B", appendRightEdge = (value, width, marker) => {
1479
+ if (width <= 0) {
1480
+ return "";
1481
+ }
1482
+ return `${padLine(value, Math.max(0, width - 1))}${marker}`;
1483
+ }, formatTimestamp2 = () => new Date().toLocaleTimeString([], {
1484
+ hour: "numeric",
1485
+ hour12: true,
1486
+ minute: "2-digit",
1487
+ second: "2-digit"
1488
+ }), isPartialEscapeSequence = (value) => {
1489
+ if (!value.startsWith(ANSI_ESCAPE_PREFIX)) {
1490
+ return false;
1491
+ }
1492
+ return Array.from(value.slice(ANSI_ESCAPE_PREFIX.length)).every((char) => char >= "0" && char <= "9");
1493
+ }, openTtyStream2 = () => {
1494
+ const fromStdin = trySetRawMode2();
1495
+ if (fromStdin) {
1496
+ return fromStdin;
1497
+ }
1498
+ try {
1499
+ const ttyStream = new ReadStream2(openSync2("/dev/tty", "r"));
1500
+ ttyStream.setRawMode(true);
1501
+ return ttyStream;
1502
+ } catch {
1503
+ return null;
1504
+ }
1505
+ }, padLine = (value, width) => {
1506
+ const plainLength = visibleLength(value);
1507
+ if (plainLength >= width) {
1508
+ return value;
1509
+ }
1510
+ return `${value}${" ".repeat(width - plainLength)}`;
1511
+ }, stripAnsi = (value) => value.replace(ANSI_REGEX, ""), truncateText = (value, width) => {
1512
+ if (width <= 0) {
1513
+ return "";
1514
+ }
1515
+ if (value.length <= width) {
1516
+ return value;
1517
+ }
1518
+ if (width <= 1) {
1519
+ return value.slice(0, width);
1520
+ }
1521
+ return `${value.slice(0, width - 1)}\u2026`;
1522
+ }, visibleLength = (value) => value.replace(ANSI_REGEX, "").length, wrapText = (value, width) => {
1523
+ if (width <= 0) {
1524
+ return [""];
1525
+ }
1526
+ const lines = value.split(`
1527
+ `).flatMap((rawLine) => wrapLine(rawLine.trimEnd(), width));
1528
+ return lines.length > 0 ? lines : [""];
1529
+ };
1530
+ var init_tuiPrimitives = __esm(() => {
1531
+ init_constants();
1532
+ ANSI_REGEX = new RegExp(`${String.fromCharCode(ANSI_ESCAPE_CODE)}\\[[0-?]*[ -/]*[@-~]`, "g");
1533
+ colors = {
1534
+ bold: "\x1B[1m",
1535
+ cyan: "\x1B[36m",
1536
+ dim: "\x1B[2m",
1537
+ green: "\x1B[32m",
1538
+ red: "\x1B[31m",
1539
+ reset: "\x1B[0m",
1540
+ yellow: "\x1B[33m"
1541
+ };
1542
+ });
1543
+
1544
+ // src/cli/scripts/build.ts
1545
+ var exports_build = {};
1546
+ __export(exports_build, {
1547
+ build: () => build
1548
+ });
1549
+ import { resolve as resolve9 } from "path";
1550
+ var cliTag3 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`, tryImportBuild2 = async (candidate) => {
1551
+ try {
1552
+ const mod = await import(candidate);
1553
+ const buildFn = mod.build;
1554
+ return buildFn;
1555
+ } catch {
1556
+ return null;
1557
+ }
1558
+ }, resolveBuildModule2 = async (candidates) => {
1559
+ const [candidate, ...remaining] = candidates;
1560
+ if (!candidate) {
1561
+ return;
1562
+ }
1563
+ const mod = await tryImportBuild2(candidate);
1564
+ if (mod) {
1565
+ return mod;
1566
+ }
1567
+ return resolveBuildModule2(remaining);
1568
+ }, build = async (outdir, configPath2) => {
1569
+ const resolvedOutdir = resolve9(outdir ?? "build");
1570
+ const buildStart = performance.now();
1571
+ process.stdout.write(cliTag3("\x1B[36m", "Building assets"));
1572
+ const buildConfig = await loadConfig(configPath2);
1573
+ buildConfig.buildDirectory = resolvedOutdir;
1574
+ buildConfig.mode = "production";
1575
+ try {
1576
+ const buildApp = await resolveBuildModule2([
1577
+ resolve9(import.meta.dir, "..", "..", "core", "build"),
1578
+ resolve9(import.meta.dir, "..", "build")
1579
+ ]);
1580
+ if (!buildApp)
1581
+ throw new Error("Could not locate build module");
1582
+ await buildApp(buildConfig);
1583
+ } catch (err) {
1584
+ sendTelemetryEvent("build:error", {
1585
+ durationMs: Math.round(performance.now() - buildStart)
1586
+ });
1587
+ console.error(cliTag3("\x1B[31m", "Build step failed."));
1588
+ console.error(err);
1589
+ process.exit(1);
1590
+ }
1591
+ sendTelemetryEvent("build:complete", {
1592
+ durationMs: Math.round(performance.now() - buildStart)
1593
+ });
1594
+ console.log(` \x1B[2m(${getDurationString(performance.now() - buildStart)})\x1B[0m`);
1595
+ };
1596
+ var init_build = __esm(() => {
1597
+ init_getDurationString();
1598
+ init_loadConfig();
1599
+ init_startupBanner();
1600
+ init_telemetryEvent();
1601
+ });
1602
+
1603
+ // src/cli/instanceStatus.ts
1604
+ import { createConnection as createConnection2 } from "net";
1605
+ var displayHost = (host) => host === "0.0.0.0" || host === "::" ? "localhost" : host, probePort = (host, port) => {
1606
+ const { promise, resolve: resolve10 } = Promise.withResolvers();
1607
+ const socket = createConnection2({ host: displayHost(host), port });
1608
+ const timeout = setTimeout(() => {
1609
+ socket.destroy();
1610
+ resolve10(false);
1611
+ }, INSTANCE_PROBE_TIMEOUT_MS);
1612
+ socket.once("connect", () => {
1613
+ clearTimeout(timeout);
1614
+ socket.end();
1615
+ resolve10(true);
1616
+ });
1617
+ socket.once("error", () => {
1618
+ clearTimeout(timeout);
1619
+ socket.destroy();
1620
+ resolve10(false);
1621
+ });
1622
+ return promise;
1623
+ }, probeStatus = async (record) => {
1624
+ if (record.port === null) {
1625
+ return "starting";
1626
+ }
1627
+ const reachable = await probePort(record.host, record.port);
1628
+ return reachable ? "ready" : "starting";
1629
+ }, enrichInstances = async (records) => {
1630
+ const now = Date.now();
1631
+ const statuses = await Promise.all(records.map(probeStatus));
1632
+ return records.map((record, index) => ({
1633
+ ...record,
1634
+ status: statuses[index] ?? "starting",
1635
+ uptimeMs: Math.max(0, now - Date.parse(record.startedAt)),
1636
+ url: instanceUrl(record)
1637
+ }));
1638
+ }, instanceUrl = (record) => {
1639
+ if (record.port === null)
1640
+ return null;
1641
+ return `${record.https ? "https" : "http"}://${displayHost(record.host)}:${record.port}/`;
1642
+ };
1643
+ var init_instanceStatus = __esm(() => {
1644
+ init_constants();
1645
+ });
1646
+
1647
+ // src/cli/listTui.ts
1648
+ var exports_listTui = {};
1649
+ __export(exports_listTui, {
1650
+ runListTui: () => runListTui
1651
+ });
1652
+ import { spawn } from "child_process";
1653
+ import { closeSync, fstatSync, openSync as openSync3, readSync } from "fs";
1654
+ var TUI_HEADERS, STATUS_INDEX = 5, URL_INDEX = 6, helpLines2, statusLevelColor = (level) => {
1655
+ if (level === "error")
1656
+ return colors.red;
1657
+ if (level === "warn")
1658
+ return colors.yellow;
1659
+ if (level === "success")
1660
+ return colors.green;
1661
+ return colors.cyan;
1662
+ }, statusColor = (status2) => {
1663
+ if (status2 === "ready")
1664
+ return colors.green;
1665
+ if (status2 === "starting")
1666
+ return colors.yellow;
1667
+ return colors.dim;
1668
+ }, instanceRowCells = (instance) => [
1669
+ instance.name,
1670
+ instance.source,
1671
+ instance.port === null ? "-" : String(instance.port),
1672
+ String(instance.pid),
1673
+ getDurationString(instance.uptimeMs),
1674
+ instance.status,
1675
+ instance.url ?? "-"
1676
+ ], columnWidths = (allCells) => TUI_HEADERS.map((header, index) => Math.max(visibleLength(header), ...allCells.map((cells) => visibleLength(cells[index] ?? "")))), layoutWidths = (allCells, width) => {
1677
+ const natural = columnWidths(allCells);
1678
+ const gaps = (TUI_HEADERS.length - 1) * LIST_TUI_COLUMN_GAP;
1679
+ const available = width - LIST_TUI_MARKER_WIDTH - gaps;
1680
+ const fixed = natural.reduce((sum, value, index) => index === URL_INDEX ? sum : sum + value, 0);
1681
+ const urlWidth = Math.max(LIST_TUI_MIN_URL_WIDTH, available - fixed);
1682
+ return natural.map((value, index) => index === URL_INDEX ? Math.min(value, urlWidth) : value);
1683
+ }, openReadFd = (path) => {
1684
+ try {
1685
+ return openSync3(path, "r");
1686
+ } catch {
1687
+ return null;
1688
+ }
1689
+ }, readLogTail = (path) => {
1690
+ if (!path)
1691
+ return [];
1692
+ const descriptor = openReadFd(path);
1693
+ if (descriptor === null)
1694
+ return [];
1695
+ try {
1696
+ const { size } = fstatSync(descriptor);
1697
+ const start2 = Math.max(0, size - LIST_LOG_TAIL_MAX_BYTES);
1698
+ const length = size - start2;
1699
+ const buffer = Buffer.alloc(length);
1700
+ readSync(descriptor, buffer, 0, length, start2);
1701
+ return buffer.toString("utf-8").split(`
1702
+ `).filter((line) => line.trim().length > 0);
1703
+ } finally {
1704
+ closeSync(descriptor);
1705
+ }
1706
+ }, driveListTui = async (terminal) => {
1707
+ const { promise, resolve: resolveExit } = Promise.withResolvers();
1708
+ let instances = [];
1709
+ let selectedIndex = 0;
1710
+ let mode = "list";
1711
+ let helpVisible = false;
1712
+ let portBuffer = "";
1713
+ let statusMessage = null;
1714
+ let statusTimer = null;
1715
+ let renderTimer = null;
1716
+ let refreshTimer = null;
1717
+ let escapeBuffer = "";
1718
+ let escapeTimer = null;
1719
+ let disposed = false;
1720
+ let logScrollOffset = 0;
1721
+ let lastLogLineCount = 0;
1722
+ let lastLogViewportHeight = 0;
1723
+ const selectedInstance = () => instances[selectedIndex];
1724
+ const scheduleRender = () => {
1725
+ if (disposed || renderTimer)
1726
+ return;
1727
+ renderTimer = setTimeout(() => {
1728
+ renderTimer = null;
1729
+ render();
1730
+ }, LIST_TUI_RENDER_DEBOUNCE_MS);
1731
+ };
1732
+ const setStatus = (text, level) => {
1733
+ statusMessage = { level, text };
1734
+ if (statusTimer)
1735
+ clearTimeout(statusTimer);
1736
+ statusTimer = setTimeout(() => {
1737
+ statusMessage = null;
1738
+ scheduleRender();
1739
+ }, LIST_TUI_STATUS_MESSAGE_TIMEOUT_MS);
1740
+ scheduleRender();
1741
+ };
1742
+ const refresh = async () => {
1743
+ const previousPid = selectedInstance()?.pid;
1744
+ instances = await enrichInstances(listLiveInstances());
1745
+ const foundIndex = previousPid === undefined ? 0 : instances.findIndex((instance) => instance.pid === previousPid);
1746
+ selectedIndex = foundIndex >= 0 ? foundIndex : Math.min(selectedIndex, Math.max(0, instances.length - 1));
1747
+ scheduleRender();
1748
+ };
1749
+ const signalPid = (pid, signal) => {
1750
+ try {
1751
+ process.kill(-pid, signal);
1752
+ return;
1753
+ } catch {}
1754
+ try {
1755
+ process.kill(pid, signal);
1756
+ } catch {}
1757
+ };
1758
+ const stopSelected = () => {
1759
+ const instance = selectedInstance();
1760
+ if (!instance)
1761
+ return;
1762
+ signalPid(instance.controllerPid, "SIGTERM");
1763
+ const message = instance.source === "workspace" ? `Stopping ${instance.name}'s workspace (pid ${instance.controllerPid}) \u2014 all its services` : `Stopped ${instance.name} (pid ${instance.controllerPid})`;
1764
+ setStatus(message, "success");
1765
+ refresh();
1766
+ };
1767
+ const restartSelected = () => {
1768
+ const instance = selectedInstance();
1769
+ if (!instance)
1770
+ return;
1771
+ if (instance.source === "workspace") {
1772
+ setStatus(`${instance.name} is managed by its workspace \u2014 restart the workspace itself.`, "warn");
1773
+ return;
1774
+ }
1775
+ const [command, ...commandArgs] = instance.command;
1776
+ if (!command) {
1777
+ setStatus(`Cannot restart ${instance.name}: no launch command recorded.`, "warn");
1778
+ return;
1779
+ }
1780
+ signalPid(instance.controllerPid, "SIGTERM");
1781
+ const child = spawn(command, commandArgs, {
1782
+ cwd: instance.cwd,
1783
+ detached: true,
1784
+ stdio: "ignore"
1785
+ });
1786
+ child.unref();
1787
+ setStatus(`Restarting ${instance.name}\u2026`, "info");
1788
+ refresh();
1789
+ };
1790
+ const openSelected = () => {
1791
+ const instance = selectedInstance();
1792
+ if (!instance)
1793
+ return;
1794
+ if (!instance.url) {
1795
+ setStatus(`${instance.name} has no URL yet.`, "warn");
1796
+ return;
1797
+ }
1798
+ openUrlInBrowser(instance.url, (message) => setStatus(message, "warn"));
1799
+ setStatus(`Opening ${instance.url}`, "info");
1800
+ };
1801
+ const stopAll = () => {
1802
+ const count = instances.length;
1803
+ instances.forEach((instance) => signalPid(instance.pid, "SIGTERM"));
1804
+ setStatus(`Stopped ${count} server${count === 1 ? "" : "s"}.`, "success");
1805
+ refresh();
1806
+ };
1807
+ const freePort = (value) => {
1808
+ const port = Number(value);
1809
+ if (!Number.isInteger(port) || port <= 0) {
1810
+ setStatus(`Invalid port: ${value}`, "error");
1811
+ return;
1812
+ }
1813
+ let killed = false;
1814
+ killStaleProcesses(port, (message) => {
1815
+ killed = true;
1816
+ setStatus(message, "warn");
1817
+ });
1818
+ if (!killed)
1819
+ setStatus(`Nothing is listening on port ${port}.`, "info");
1820
+ refresh();
1821
+ };
1822
+ const moveSelection = (direction) => {
1823
+ if (instances.length === 0)
1824
+ return;
1825
+ const delta = direction === "up" ? UNFOUND_INDEX : 1;
1826
+ selectedIndex = Math.max(0, Math.min(instances.length - 1, selectedIndex + delta));
1827
+ logScrollOffset = 0;
1828
+ scheduleRender();
1829
+ };
1830
+ const scrollLogs = (direction) => {
1831
+ const maxOffset = Math.max(0, lastLogLineCount - lastLogViewportHeight);
1832
+ const pageSize = Math.max(1, lastLogViewportHeight - 1);
1833
+ if (direction === "up") {
1834
+ logScrollOffset = Math.min(maxOffset, logScrollOffset + 1);
1835
+ } else if (direction === "down") {
1836
+ logScrollOffset = Math.max(0, logScrollOffset - 1);
1837
+ } else if (direction === "pageUp") {
1838
+ logScrollOffset = Math.min(maxOffset, logScrollOffset + pageSize);
1839
+ } else {
1840
+ logScrollOffset = Math.max(0, logScrollOffset - pageSize);
1841
+ }
1842
+ scheduleRender();
1843
+ };
1844
+ const dispose = () => {
1845
+ if (disposed)
1846
+ return;
1847
+ disposed = true;
1848
+ if (renderTimer)
1849
+ clearTimeout(renderTimer);
1850
+ if (statusTimer)
1851
+ clearTimeout(statusTimer);
1852
+ if (escapeTimer)
1853
+ clearTimeout(escapeTimer);
1854
+ if (refreshTimer)
1855
+ clearInterval(refreshTimer);
1856
+ process.stdout.off("resize", onResize);
1857
+ terminal.off("data", onData);
1858
+ if (terminal.setRawMode)
1859
+ terminal.setRawMode(false);
1860
+ if (terminal !== process.stdin)
1861
+ terminal.destroy();
1862
+ process.stdout.write("\x1B[?25h\x1B[?1049l");
1863
+ };
1864
+ const quit = () => {
1865
+ dispose();
1866
+ resolveExit();
1867
+ };
1868
+ const listActions = new Map([
1869
+ ["f", () => enterPortMode()],
1870
+ ["h", () => toggleHelp()],
1871
+ ["j", () => moveSelection("down")],
1872
+ ["k", () => moveSelection("up")],
1873
+ ["o", () => openSelected()],
1874
+ ["q", () => quit()],
1875
+ ["r", () => restartSelected()],
1876
+ ["s", () => stopSelected()],
1877
+ ["x", () => enterConfirmMode()],
1878
+ ["?", () => toggleHelp()]
1879
+ ]);
1880
+ const toggleHelp = () => {
1881
+ helpVisible = !helpVisible;
1882
+ scheduleRender();
1883
+ };
1884
+ const enterPortMode = () => {
1885
+ mode = "port";
1886
+ portBuffer = "";
1887
+ scheduleRender();
1888
+ };
1889
+ const enterConfirmMode = () => {
1890
+ if (instances.length === 0)
1891
+ return;
1892
+ mode = "confirm";
1893
+ scheduleRender();
1894
+ };
1895
+ const handleListChar = (char) => {
1896
+ const action = listActions.get(char.toLowerCase());
1897
+ if (action)
1898
+ action();
1899
+ };
1900
+ const handlePortChar = (char) => {
1901
+ if (char === "\r" || char === `
1902
+ `) {
1903
+ const value = portBuffer;
1904
+ mode = "list";
1905
+ portBuffer = "";
1906
+ freePort(value);
1907
+ return;
1908
+ }
1909
+ if (char === "\x7F" || char === "\b") {
1910
+ portBuffer = portBuffer.slice(0, EXCLUDE_LAST_OFFSET);
1911
+ scheduleRender();
1912
+ return;
1913
+ }
1914
+ if (char >= "0" && char <= "9") {
1915
+ portBuffer += char;
1916
+ scheduleRender();
1917
+ }
1918
+ };
1919
+ const handleConfirmChar = (char) => {
1920
+ mode = "list";
1921
+ if (char.toLowerCase() === "y") {
1922
+ stopAll();
1923
+ return;
1924
+ }
1925
+ scheduleRender();
1926
+ };
1927
+ const clearEscapeTimer = () => {
1928
+ if (!escapeTimer)
1929
+ return;
1930
+ clearTimeout(escapeTimer);
1931
+ escapeTimer = null;
1932
+ };
1933
+ const onBareEscape = () => {
1934
+ if (helpVisible) {
1935
+ helpVisible = false;
1936
+ } else if (mode !== "list") {
1937
+ mode = "list";
1938
+ portBuffer = "";
1939
+ }
1940
+ scheduleRender();
1941
+ };
1942
+ const armEscapeTimer = () => {
1943
+ clearEscapeTimer();
1944
+ escapeTimer = setTimeout(() => {
1945
+ escapeTimer = null;
1946
+ escapeBuffer = "";
1947
+ onBareEscape();
1948
+ }, LIST_TUI_ESCAPE_SEQUENCE_TIMEOUT_MS);
1949
+ };
1950
+ const resetEscape = () => {
1951
+ clearEscapeTimer();
1952
+ escapeBuffer = "";
1953
+ };
1954
+ const handleEscapeSequence = (char) => {
1955
+ escapeBuffer += char;
1956
+ if (escapeBuffer === `${ESCAPE}[`) {
1957
+ armEscapeTimer();
1958
+ return;
1959
+ }
1960
+ if (escapeBuffer === `${ESCAPE}[A`) {
1961
+ resetEscape();
1962
+ moveSelection("up");
1963
+ return;
1964
+ }
1965
+ if (escapeBuffer === `${ESCAPE}[B`) {
1966
+ resetEscape();
1967
+ moveSelection("down");
1968
+ return;
1969
+ }
1970
+ if (escapeBuffer === `${ESCAPE}[5~`) {
1971
+ resetEscape();
1972
+ scrollLogs("pageUp");
1973
+ return;
1974
+ }
1975
+ if (escapeBuffer === `${ESCAPE}[6~`) {
1976
+ resetEscape();
1977
+ scrollLogs("pageDown");
1978
+ return;
1979
+ }
1980
+ if (isPartialEscapeSequence(escapeBuffer)) {
1981
+ armEscapeTimer();
1982
+ return;
1983
+ }
1984
+ resetEscape();
1985
+ onBareEscape();
1986
+ };
1987
+ const handleChar = (char) => {
1988
+ if (char === "\x03") {
1989
+ quit();
1990
+ return;
1991
+ }
1992
+ if (escapeBuffer) {
1993
+ handleEscapeSequence(char);
1994
+ return;
1995
+ }
1996
+ if (char === ESCAPE) {
1997
+ escapeBuffer = ESCAPE;
1998
+ armEscapeTimer();
1999
+ return;
2000
+ }
2001
+ if (mode === "port") {
2002
+ handlePortChar(char);
1275
2003
  return;
1276
- try {
1277
- await Bun.write(filePath, rewritten);
1278
- } catch (err) {
1279
- const code = err.code;
1280
- if (code === "ENOENT")
1281
- return;
1282
- throw err;
1283
2004
  }
1284
- }));
1285
- }, rewriteVendorDirectories2;
1286
- var init_rewriteImports = __esm(() => {
1287
- init_rewriteImportsPlugin();
1288
- rewriteVendorDirectories2 = rewriteVendorDirectories;
2005
+ if (mode === "confirm") {
2006
+ handleConfirmChar(char);
2007
+ return;
2008
+ }
2009
+ handleListChar(char);
2010
+ };
2011
+ const onData = (chunk) => {
2012
+ for (const char of chunk.toString()) {
2013
+ handleChar(char);
2014
+ }
2015
+ };
2016
+ const onResize = () => {
2017
+ scheduleRender();
2018
+ };
2019
+ const titleLine = (width) => {
2020
+ const label = `${instances.length} live`;
2021
+ const left = `${colors.cyan}${colors.bold}ABSOLUTEJS${colors.reset} ${colors.dim}running servers${colors.reset} ${colors.bold}${label}${colors.reset}`;
2022
+ const right = `${colors.dim}${formatTimestamp2()}${colors.reset}`;
2023
+ const gap = Math.max(1, width - visibleLength(left) - visibleLength(right));
2024
+ return `${left}${" ".repeat(gap)}${right}`;
2025
+ };
2026
+ const dividerLine = (width) => `${colors.dim}${"\u2500".repeat(Math.max(width, 1))}${colors.reset}`;
2027
+ const colorizeCell = (cell, index, status2, isSelected, widths) => {
2028
+ const padded = padLine(truncateText(cell, widths[index] ?? 0), widths[index] ?? 0);
2029
+ if (index === STATUS_INDEX) {
2030
+ return `${statusColor(status2)}${padded}${colors.reset}`;
2031
+ }
2032
+ if (index === 0 && isSelected) {
2033
+ return `${colors.cyan}${colors.bold}${padded}${colors.reset}`;
2034
+ }
2035
+ return padded;
2036
+ };
2037
+ const renderInstanceRow = (instance, widths, isSelected) => {
2038
+ const body = instanceRowCells(instance).map((cell, index) => colorizeCell(cell, index, instance.status, isSelected, widths)).join(" ".repeat(LIST_TUI_COLUMN_GAP));
2039
+ const marker = isSelected ? `${colors.cyan}\u276F${colors.reset}` : " ";
2040
+ return `${marker} ${body}`;
2041
+ };
2042
+ const pushInstanceRows = (rows, width) => {
2043
+ if (instances.length === 0) {
2044
+ rows.push(padLine(`${colors.dim}No servers running. Start one with \`absolute dev\`.${colors.reset}`, width));
2045
+ return;
2046
+ }
2047
+ const allCells = instances.map(instanceRowCells);
2048
+ const widths = layoutWidths(allCells, width);
2049
+ const header = TUI_HEADERS.map((label, index) => padLine(label, widths[index] ?? 0)).join(" ".repeat(LIST_TUI_COLUMN_GAP));
2050
+ rows.push(padLine(` ${colors.dim}${header}${colors.reset}`, width));
2051
+ instances.forEach((instance, index) => {
2052
+ rows.push(padLine(renderInstanceRow(instance, widths, index === selectedIndex), width));
2053
+ });
2054
+ };
2055
+ const logContentLines = (width) => {
2056
+ if (helpVisible)
2057
+ return helpLines2;
2058
+ const instance = selectedInstance();
2059
+ if (!instance) {
2060
+ return [`${colors.dim}No server selected.${colors.reset}`];
2061
+ }
2062
+ const lines = readLogTail(instance.logFile);
2063
+ if (lines.length === 0) {
2064
+ return [`${colors.dim}No output yet.${colors.reset}`];
2065
+ }
2066
+ return lines.map((line) => truncateText(stripAnsi(line), Math.max(1, width - 1)));
2067
+ };
2068
+ const pushLogRows = (rows, width, logHeight) => {
2069
+ const contentLines = logContentLines(width);
2070
+ lastLogLineCount = contentLines.length;
2071
+ lastLogViewportHeight = logHeight;
2072
+ const end = helpVisible ? Math.min(contentLines.length, logHeight) : Math.max(0, contentLines.length - logScrollOffset);
2073
+ const start2 = helpVisible ? 0 : Math.max(0, end - logHeight);
2074
+ const visible = contentLines.slice(start2, end);
2075
+ visible.forEach((line) => rows.push(padLine(line, width)));
2076
+ for (let index = visible.length;index < logHeight; index++) {
2077
+ rows.push(" ".repeat(width));
2078
+ }
2079
+ };
2080
+ const footerLine = (width) => {
2081
+ if (helpVisible) {
2082
+ return padLine(`${colors.dim}esc or ? closes help${colors.reset}`, width);
2083
+ }
2084
+ if (mode === "port") {
2085
+ return padLine(`${colors.yellow}free port:${colors.reset} ${portBuffer}\u258C ${colors.dim}enter to kill \xB7 esc to cancel${colors.reset}`, width);
2086
+ }
2087
+ if (mode === "confirm") {
2088
+ return padLine(`${colors.yellow}Stop ALL ${instances.length} servers? ${colors.reset}${colors.bold}y${colors.reset}${colors.dim}/N${colors.reset}`, width);
2089
+ }
2090
+ const hint = "\u2191\u2193 select \xB7 s stop \xB7 r restart \xB7 o open \xB7 f free port \xB7 x stop all \xB7 ? help \xB7 q quit";
2091
+ return padLine(`${colors.dim}${truncateText(hint, width)}${colors.reset}`, width);
2092
+ };
2093
+ const statusLine = (width) => {
2094
+ if (!statusMessage) {
2095
+ return padLine(`${colors.dim}live \xB7 refreshing every ${LIST_WATCH_REFRESH_MS}ms${colors.reset}`, width);
2096
+ }
2097
+ return padLine(`${statusLevelColor(statusMessage.level)}${statusMessage.text}${colors.reset}`, width);
2098
+ };
2099
+ const render = () => {
2100
+ if (disposed)
2101
+ return;
2102
+ const width = process.stdout.columns ?? LIST_TUI_DEFAULT_WIDTH;
2103
+ const height = process.stdout.rows ?? LIST_TUI_DEFAULT_HEIGHT;
2104
+ const rows = [];
2105
+ rows.push(padLine(titleLine(width), width));
2106
+ rows.push(dividerLine(width));
2107
+ pushInstanceRows(rows, width);
2108
+ rows.push(dividerLine(width));
2109
+ const instance = selectedInstance();
2110
+ const logTitle = helpVisible || !instance ? "logs" : `logs \xB7 ${instance.name}${instance.frameworks.length > 0 ? ` \xB7 ${instance.frameworks.join(", ")}` : ""}`;
2111
+ rows.push(padLine(`${colors.bold}${logTitle}${colors.reset}`, width));
2112
+ const fixedHeight = rows.length + LIST_TUI_FOOTER_LINE_COUNT + 1;
2113
+ const logHeight = Math.max(height - fixedHeight, LIST_TUI_MIN_LOG_HEIGHT);
2114
+ pushLogRows(rows, width, logHeight);
2115
+ rows.push(dividerLine(width));
2116
+ rows.push(statusLine(width));
2117
+ rows.push(footerLine(width));
2118
+ const screen = rows.slice(0, height).map((line) => `\x1B[2K${line}`).join(`
2119
+ `);
2120
+ process.stdout.write(`\x1B[H${screen}\x1B[?25l`);
2121
+ };
2122
+ process.on("SIGINT", quit);
2123
+ process.on("SIGTERM", quit);
2124
+ process.stdout.write("\x1B[?1049h\x1B[2J\x1B[H\x1B[?25l");
2125
+ terminal.resume();
2126
+ terminal.on("data", onData);
2127
+ process.stdout.on("resize", onResize);
2128
+ refreshTimer = setInterval(() => {
2129
+ refresh();
2130
+ }, LIST_WATCH_REFRESH_MS);
2131
+ await refresh();
2132
+ render();
2133
+ await promise;
2134
+ }, runListTui = async () => {
2135
+ const input = openTtyStream2();
2136
+ if (!input) {
2137
+ process.stdout.write("Interactive ls requires a TTY. Run `absolute ls` for a snapshot.\n");
2138
+ return;
2139
+ }
2140
+ await driveListTui(input);
2141
+ };
2142
+ var init_listTui = __esm(() => {
2143
+ init_constants();
2144
+ init_getDurationString();
2145
+ init_instanceRegistry();
2146
+ init_instanceStatus();
2147
+ init_tuiPrimitives();
2148
+ init_utils();
2149
+ TUI_HEADERS = [
2150
+ "NAME",
2151
+ "SOURCE",
2152
+ "PORT",
2153
+ "PID",
2154
+ "UPTIME",
2155
+ "STATUS",
2156
+ "URL"
2157
+ ];
2158
+ helpLines2 = [
2159
+ "Hotkeys",
2160
+ " \u2191/\u2193 or j/k Select a server",
2161
+ " s Stop the selected server",
2162
+ " r Restart the selected server",
2163
+ " o Open the selected server in the browser",
2164
+ " f Free a port (kill whatever is listening on it)",
2165
+ " x Stop every listed server",
2166
+ " PgUp/PgDn Scroll the log pane",
2167
+ " ? or h Toggle this help",
2168
+ " q Quit (servers keep running)"
2169
+ ];
1289
2170
  });
1290
2171
 
1291
- // src/cli/scripts/build.ts
1292
- var exports_build = {};
1293
- __export(exports_build, {
1294
- build: () => build
2172
+ // src/cli/scripts/list.ts
2173
+ var exports_list = {};
2174
+ __export(exports_list, {
2175
+ runList: () => runList
1295
2176
  });
1296
- import { resolve as resolve9 } from "path";
1297
- var cliTag3 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`, tryImportBuild2 = async (candidate) => {
1298
- try {
1299
- const mod = await import(candidate);
1300
- const buildFn = mod.build;
1301
- return buildFn;
1302
- } catch {
1303
- return null;
1304
- }
1305
- }, resolveBuildModule2 = async (candidates) => {
1306
- const [candidate, ...remaining] = candidates;
1307
- if (!candidate) {
2177
+ var TABLE_HEADERS, statusColor2 = (status2) => {
2178
+ if (status2 === "ready")
2179
+ return colors.green;
2180
+ if (status2 === "starting")
2181
+ return colors.yellow;
2182
+ return colors.dim;
2183
+ }, instanceCells = (instance) => [
2184
+ instance.name,
2185
+ instance.source,
2186
+ instance.port === null ? "-" : String(instance.port),
2187
+ String(instance.pid),
2188
+ getDurationString(instance.uptimeMs),
2189
+ `${statusColor2(instance.status)}${instance.status}${colors.reset}`,
2190
+ instance.url ?? "-"
2191
+ ], columnWidths2 = (rows) => TABLE_HEADERS.map((header, index) => Math.max(visibleLength(header), ...rows.map((cells) => visibleLength(cells[index] ?? "")))), renderRow = (cells, widths) => cells.map((cell, index) => padLine(cell, widths[index] ?? 0)).join(" ".repeat(LIST_TUI_COLUMN_GAP)), printInstanceTable = (instances) => {
2192
+ if (instances.length === 0) {
2193
+ process.stdout.write(`${colors.dim}No AbsoluteJS servers are running. Start one with \`absolute dev\`.${colors.reset}
2194
+ `);
1308
2195
  return;
1309
2196
  }
1310
- const mod = await tryImportBuild2(candidate);
1311
- if (mod) {
1312
- return mod;
1313
- }
1314
- return resolveBuildModule2(remaining);
1315
- }, build = async (outdir, configPath2) => {
1316
- const resolvedOutdir = resolve9(outdir ?? "build");
1317
- const buildStart = performance.now();
1318
- process.stdout.write(cliTag3("\x1B[36m", "Building assets"));
1319
- const buildConfig = await loadConfig(configPath2);
1320
- buildConfig.buildDirectory = resolvedOutdir;
1321
- buildConfig.mode = "production";
1322
- try {
1323
- const buildApp = await resolveBuildModule2([
1324
- resolve9(import.meta.dir, "..", "..", "core", "build"),
1325
- resolve9(import.meta.dir, "..", "build")
1326
- ]);
1327
- if (!buildApp)
1328
- throw new Error("Could not locate build module");
1329
- await buildApp(buildConfig);
1330
- } catch (err) {
1331
- sendTelemetryEvent("build:error", {
1332
- durationMs: Math.round(performance.now() - buildStart)
1333
- });
1334
- console.error(cliTag3("\x1B[31m", "Build step failed."));
1335
- console.error(err);
1336
- process.exit(1);
2197
+ const rows = instances.map(instanceCells);
2198
+ const widths = columnWidths2(rows);
2199
+ process.stdout.write(`${colors.dim}${renderRow(TABLE_HEADERS, widths)}${colors.reset}
2200
+ `);
2201
+ for (const cells of rows) {
2202
+ process.stdout.write(`${renderRow(cells, widths)}
2203
+ `);
1337
2204
  }
1338
- sendTelemetryEvent("build:complete", {
1339
- durationMs: Math.round(performance.now() - buildStart)
2205
+ }, runList = async (args) => {
2206
+ process.stdout.on("error", (error) => {
2207
+ if (error instanceof Error && "code" in error && error.code === "EPIPE") {
2208
+ process.exit(0);
2209
+ }
1340
2210
  });
1341
- console.log(` \x1B[2m(${getDurationString(performance.now() - buildStart)})\x1B[0m`);
2211
+ if (args.includes("--watch") || args.includes("-w")) {
2212
+ const { runListTui: runListTui2 } = await Promise.resolve().then(() => (init_listTui(), exports_listTui));
2213
+ await runListTui2();
2214
+ return;
2215
+ }
2216
+ const instances = await enrichInstances(listLiveInstances());
2217
+ if (args.includes("--json")) {
2218
+ process.stdout.write(`${JSON.stringify(instances, null, 2)}
2219
+ `);
2220
+ return;
2221
+ }
2222
+ printInstanceTable(instances);
1342
2223
  };
1343
- var init_build = __esm(() => {
2224
+ var init_list = __esm(() => {
2225
+ init_constants();
1344
2226
  init_getDurationString();
1345
- init_loadConfig();
1346
- init_startupBanner();
1347
- init_telemetryEvent();
2227
+ init_instanceRegistry();
2228
+ init_instanceStatus();
2229
+ init_tuiPrimitives();
2230
+ TABLE_HEADERS = [
2231
+ "NAME",
2232
+ "SOURCE",
2233
+ "PORT",
2234
+ "PID",
2235
+ "UPTIME",
2236
+ "STATUS",
2237
+ "URL"
2238
+ ];
1348
2239
  });
1349
2240
 
1350
2241
  // src/build/externalAssetPlugin.ts
1351
- import { copyFileSync as copyFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync6, statSync } from "fs";
1352
- import { basename as basename2, dirname as dirname3, join as join9, resolve as resolve10 } from "path";
2242
+ import { copyFileSync as copyFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, statSync } from "fs";
2243
+ import { basename as basename3, dirname as dirname3, join as join10, resolve as resolve10 } from "path";
1353
2244
  var createExternalAssetPlugin = (outDir, userSourceRoots = []) => ({
1354
2245
  name: "absolute-external-asset",
1355
2246
  setup(bld) {
@@ -1374,10 +2265,10 @@ var createExternalAssetPlugin = (outDir, userSourceRoots = []) => ({
1374
2265
  continue;
1375
2266
  if (!statSync(assetPath).isFile())
1376
2267
  continue;
1377
- const targetPath = join9(outDir, basename2(assetPath));
2268
+ const targetPath = join10(outDir, basename3(assetPath));
1378
2269
  if (existsSync10(targetPath))
1379
2270
  continue;
1380
- mkdirSync6(dirname3(targetPath), { recursive: true });
2271
+ mkdirSync7(dirname3(targetPath), { recursive: true });
1381
2272
  copyFileSync2(assetPath, targetPath);
1382
2273
  }
1383
2274
  return;
@@ -1396,15 +2287,15 @@ var {env: env3 } = globalThis.Bun;
1396
2287
  import {
1397
2288
  cpSync,
1398
2289
  existsSync as existsSync11,
1399
- mkdirSync as mkdirSync7,
1400
- readdirSync as readdirSync2,
1401
- readFileSync as readFileSync12,
2290
+ mkdirSync as mkdirSync8,
2291
+ readdirSync as readdirSync3,
2292
+ readFileSync as readFileSync13,
1402
2293
  rmSync as rmSync4,
1403
2294
  statSync as statSync2,
1404
- unlinkSync as unlinkSync3,
1405
- writeFileSync as writeFileSync4
2295
+ unlinkSync as unlinkSync4,
2296
+ writeFileSync as writeFileSync5
1406
2297
  } from "fs";
1407
- import { basename as basename3, dirname as dirname4, join as join10, relative, resolve as resolve11 } from "path";
2298
+ import { basename as basename4, dirname as dirname4, join as join11, relative, resolve as resolve11 } from "path";
1408
2299
  var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`, compileBanner = (version2) => {
1409
2300
  const resolvedVersion = version2 || "unknown";
1410
2301
  console.log("");
@@ -1412,14 +2303,14 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1412
2303
  console.log("");
1413
2304
  }, collectFiles2 = (dir) => {
1414
2305
  const result = [];
1415
- let pending = readdirSync2(dir, { withFileTypes: true });
2306
+ let pending = readdirSync3(dir, { withFileTypes: true });
1416
2307
  while (pending.length > 0) {
1417
2308
  const entry = pending.pop();
1418
2309
  if (!entry)
1419
2310
  continue;
1420
- const fullPath = join10(entry.parentPath, entry.name);
2311
+ const fullPath = join11(entry.parentPath, entry.name);
1421
2312
  if (entry.isDirectory())
1422
- pending = pending.concat(readdirSync2(fullPath, { withFileTypes: true }));
2313
+ pending = pending.concat(readdirSync3(fullPath, { withFileTypes: true }));
1423
2314
  else
1424
2315
  result.push(fullPath);
1425
2316
  }
@@ -1432,16 +2323,16 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1432
2323
  return `./${parts.join("/")}`;
1433
2324
  }, collectProjectSourceFiles = (dir) => {
1434
2325
  const result = [];
1435
- let pending = readdirSync2(dir, { withFileTypes: true });
2326
+ let pending = readdirSync3(dir, { withFileTypes: true });
1436
2327
  while (pending.length > 0) {
1437
2328
  const entry = pending.pop();
1438
2329
  if (!entry)
1439
2330
  continue;
1440
- const fullPath = join10(entry.parentPath, entry.name);
2331
+ const fullPath = join11(entry.parentPath, entry.name);
1441
2332
  if (entry.isDirectory()) {
1442
2333
  if (SERVER_RUNTIME_SCAN_SKIP_DIRS.has(entry.name))
1443
2334
  continue;
1444
- pending = pending.concat(readdirSync2(fullPath, { withFileTypes: true }));
2335
+ pending = pending.concat(readdirSync3(fullPath, { withFileTypes: true }));
1445
2336
  } else if (hasSourceExtension(fullPath)) {
1446
2337
  result.push(fullPath);
1447
2338
  }
@@ -1460,11 +2351,11 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1460
2351
  if (copied.has(assetTarget))
1461
2352
  return;
1462
2353
  copied.add(assetTarget);
1463
- mkdirSync7(dirname4(assetTarget), { recursive: true });
2354
+ mkdirSync8(dirname4(assetTarget), { recursive: true });
1464
2355
  cpSync(assetSource, assetTarget, { force: true });
1465
2356
  };
1466
2357
  for (const filePath of collectProjectSourceFiles(process.cwd())) {
1467
- const source = readFileSync12(filePath, "utf-8");
2358
+ const source = readFileSync13(filePath, "utf-8");
1468
2359
  SERVER_RUNTIME_ASSET_RE.lastIndex = 0;
1469
2360
  let match;
1470
2361
  while ((match = SERVER_RUNTIME_ASSET_RE.exec(source)) !== null) {
@@ -1493,7 +2384,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1493
2384
  }
1494
2385
  }, readPackageVersion4 = (candidate) => {
1495
2386
  try {
1496
- const pkg = JSON.parse(readFileSync12(candidate, "utf-8"));
2387
+ const pkg = JSON.parse(readFileSync13(candidate, "utf-8"));
1497
2388
  if (pkg.name !== "@absolutejs/absolute")
1498
2389
  return null;
1499
2390
  const ver = pkg.version;
@@ -1552,7 +2443,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1552
2443
  return true;
1553
2444
  }, tryReadNodePackageJson = (packageDir) => {
1554
2445
  try {
1555
- return JSON.parse(readFileSync12(join10(packageDir, "package.json"), "utf-8"));
2446
+ return JSON.parse(readFileSync13(join11(packageDir, "package.json"), "utf-8"));
1556
2447
  } catch {
1557
2448
  return null;
1558
2449
  }
@@ -1564,7 +2455,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1564
2455
  if (!pkg)
1565
2456
  return;
1566
2457
  seen.add(specifier);
1567
- const destDir = join10(outdir, "node_modules", ...specifier.split("/"));
2458
+ const destDir = join11(outdir, "node_modules", ...specifier.split("/"));
1568
2459
  rmSync4(destDir, { force: true, recursive: true });
1569
2460
  cpSync(srcDir, destDir, {
1570
2461
  filter(source) {
@@ -1587,7 +2478,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1587
2478
  if (!buildConfig.angularDirectory)
1588
2479
  return;
1589
2480
  const angularScopeDir = resolve11(process.cwd(), "node_modules", "@angular");
1590
- const angularPackages = existsSync11(angularScopeDir) ? readdirSync2(angularScopeDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).filter((entry) => entry.name !== "compiler-cli").map((entry) => `@angular/${entry.name}`) : [];
2481
+ const angularPackages = existsSync11(angularScopeDir) ? readdirSync3(angularScopeDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).filter((entry) => entry.name !== "compiler-cli").map((entry) => `@angular/${entry.name}`) : [];
1591
2482
  const roots = new Set([...angularPackages, "rxjs", "tslib", "typescript"]);
1592
2483
  const seen = new Set;
1593
2484
  for (const specifier of roots) {
@@ -1604,16 +2495,16 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1604
2495
  }
1605
2496
  copyAngularRuntimePackages(buildConfig, outdir);
1606
2497
  }, collectRuntimePackageSpecifiers = (distDir) => {
1607
- const nodeModulesDir = join10(distDir, "node_modules");
2498
+ const nodeModulesDir = join11(distDir, "node_modules");
1608
2499
  if (!existsSync11(nodeModulesDir))
1609
2500
  return [];
1610
2501
  const specifiers = [];
1611
- for (const entry of readdirSync2(nodeModulesDir, { withFileTypes: true })) {
2502
+ for (const entry of readdirSync3(nodeModulesDir, { withFileTypes: true })) {
1612
2503
  if (!entry.isDirectory())
1613
2504
  continue;
1614
2505
  if (entry.name.startsWith("@")) {
1615
- const scopeDir = join10(nodeModulesDir, entry.name);
1616
- for (const scopedEntry of readdirSync2(scopeDir, {
2506
+ const scopeDir = join11(nodeModulesDir, entry.name);
2507
+ for (const scopedEntry of readdirSync3(scopeDir, {
1617
2508
  withFileTypes: true
1618
2509
  })) {
1619
2510
  if (scopedEntry.isDirectory()) {
@@ -1644,18 +2535,18 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1644
2535
  const packageSpecifier = packageSpecifiers.find((root) => specifier === root || specifier.startsWith(`${root}/`));
1645
2536
  if (!packageSpecifier)
1646
2537
  return null;
1647
- const packageDir = join10(distDir, "node_modules", ...packageSpecifier.split("/"));
2538
+ const packageDir = join11(distDir, "node_modules", ...packageSpecifier.split("/"));
1648
2539
  const subpath = specifier.slice(packageSpecifier.length);
1649
- const subPackageDir = subpath ? join10(packageDir, ...subpath.slice(1).split("/")) : null;
1650
- const resolvedPackageDir = subPackageDir && existsSync11(join10(subPackageDir, "package.json")) ? subPackageDir : packageDir;
1651
- const packageJsonPath = join10(resolvedPackageDir, "package.json");
2540
+ const subPackageDir = subpath ? join11(packageDir, ...subpath.slice(1).split("/")) : null;
2541
+ const resolvedPackageDir = subPackageDir && existsSync11(join11(subPackageDir, "package.json")) ? subPackageDir : packageDir;
2542
+ const packageJsonPath = join11(resolvedPackageDir, "package.json");
1652
2543
  if (!existsSync11(packageJsonPath))
1653
2544
  return null;
1654
- const pkg = JSON.parse(readFileSync12(packageJsonPath, "utf-8"));
2545
+ const pkg = JSON.parse(readFileSync13(packageJsonPath, "utf-8"));
1655
2546
  const exportKey = resolvedPackageDir === subPackageDir ? "." : subpath ? `.${subpath}` : ".";
1656
2547
  const rootExport = pkg.exports?.[exportKey];
1657
2548
  const entry = pickExportEntry(rootExport) ?? (resolvedPackageDir === subPackageDir || !subpath ? pkg.module ?? pkg.main ?? "index.js" : `.${subpath}`);
1658
- return join10(resolvedPackageDir, entry);
2549
+ return join11(resolvedPackageDir, entry);
1659
2550
  }, RUNTIME_JS_EXTENSIONS, MODULE_SPECIFIER_RE, isRuntimeJsFile = (filePath) => RUNTIME_JS_EXTENSIONS.some((extension) => filePath.endsWith(extension)), isNodeModulesPath = (filePath) => filePath.split(/[\\/]/).includes("node_modules"), isFile = (filePath) => {
1660
2551
  try {
1661
2552
  return statSync2(filePath).isFile();
@@ -1668,13 +2559,13 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1668
2559
  const candidates = [
1669
2560
  candidate,
1670
2561
  ...RUNTIME_JS_EXTENSIONS.map((extension) => `${candidate}${extension}`),
1671
- ...RUNTIME_JS_EXTENSIONS.map((extension) => join10(candidate, `index${extension}`))
2562
+ ...RUNTIME_JS_EXTENSIONS.map((extension) => join11(candidate, `index${extension}`))
1672
2563
  ];
1673
2564
  return candidates.find((filePath) => isRuntimeJsFile(filePath) && isFile(filePath)) ?? null;
1674
2565
  }, findContainingRuntimePackageDir = (filePath) => {
1675
2566
  let dir = dirname4(filePath);
1676
2567
  while (dir !== dirname4(dir)) {
1677
- if (isNodeModulesPath(dir) && existsSync11(join10(dir, "package.json"))) {
2568
+ if (isNodeModulesPath(dir) && existsSync11(join11(dir, "package.json"))) {
1678
2569
  return dir;
1679
2570
  }
1680
2571
  dir = dirname4(dir);
@@ -1690,7 +2581,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1690
2581
  const entry = pickExportEntry(pkg?.imports?.[specifier]);
1691
2582
  if (!entry)
1692
2583
  return null;
1693
- return join10(packageDir, entry);
2584
+ return join11(packageDir, entry);
1694
2585
  }, collectRuntimeRewriteRoots = (distDir) => collectFiles2(distDir).filter((filePath) => isRuntimeJsFile(filePath) && !isNodeModulesPath(filePath)), rewriteRuntimeModuleSpecifiers = (distDir) => {
1695
2586
  const packageSpecifiers = collectRuntimePackageSpecifiers(distDir);
1696
2587
  if (packageSpecifiers.length === 0)
@@ -1709,7 +2600,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1709
2600
  if (!filePath || seen.has(filePath))
1710
2601
  continue;
1711
2602
  seen.add(filePath);
1712
- const source = readFileSync12(filePath, "utf-8");
2603
+ const source = readFileSync13(filePath, "utf-8");
1713
2604
  const rewritten = source.replace(MODULE_SPECIFIER_RE, (match, prefix, quote, specifier) => {
1714
2605
  if (typeof specifier === "string" && specifier.startsWith(".")) {
1715
2606
  enqueue(resolveRuntimeJsFile(resolve11(dirname4(filePath), specifier)));
@@ -1727,12 +2618,12 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1727
2618
  return `${prefix}${quote}${ensureRelativeModuleSpecifier(filePath, target)}${quote}`;
1728
2619
  });
1729
2620
  if (rewritten !== source) {
1730
- writeFileSync4(filePath, rewritten);
2621
+ writeFileSync5(filePath, rewritten);
1731
2622
  }
1732
2623
  }
1733
2624
  }, generateEntrypoint = (distDir, serverEntry, prerenderMap, version2, buildConfig) => {
1734
2625
  const allFiles = collectFiles2(distDir);
1735
- const serverBundleName = `${basename3(serverEntry).replace(/\.[^.]+$/, "")}.js`;
2626
+ const serverBundleName = `${basename4(serverEntry).replace(/\.[^.]+$/, "")}.js`;
1736
2627
  const embeddedSkip = new Set(["_compile_entrypoint.ts"]);
1737
2628
  const assetSkip = new Set([
1738
2629
  serverBundleName,
@@ -2115,7 +3006,7 @@ console.log(\`
2115
3006
  }, compileUnlocked = async (serverEntry, resolvedOutdir, outfile, configPath2) => {
2116
3007
  const prerenderPort = Number(env3.COMPILE_PORT) || Number(env3.PORT) || DEFAULT_PORT + 1;
2117
3008
  killStaleProcesses(prerenderPort);
2118
- const entryName = basename3(serverEntry).replace(/\.[^.]+$/, "");
3009
+ const entryName = basename4(serverEntry).replace(/\.[^.]+$/, "");
2119
3010
  const resolvedOutfile = resolve11(outfile ?? "compiled-server");
2120
3011
  const absoluteVersion = resolvePackageVersion3([
2121
3012
  resolve11(import.meta.dir, "..", "..", "..", "package.json"),
@@ -2181,7 +3072,7 @@ console.log(\`
2181
3072
  }
2182
3073
  if (existsSync11(resolve11(resolvedOutdir, "angular", "vendor", "server"))) {
2183
3074
  const vendorDir = resolve11(resolvedOutdir, "angular", "vendor", "server");
2184
- const vendorEntries = readdirSync2(vendorDir).filter((f) => f.endsWith(".js"));
3075
+ const vendorEntries = readdirSync3(vendorDir).filter((f) => f.endsWith(".js"));
2185
3076
  const angularServerVendorPaths = {};
2186
3077
  for (const file of vendorEntries) {
2187
3078
  const stem = file.replace(/\.js$/, "");
@@ -2201,7 +3092,7 @@ console.log(\`
2201
3092
  copyServerRuntimeAssetReferences(resolvedOutdir);
2202
3093
  const prerenderStart = performance.now();
2203
3094
  process.stdout.write(cliTag4("\x1B[36m", "Pre-rendering pages"));
2204
- rmSync4(join10(resolvedOutdir, "_prerendered"), {
3095
+ rmSync4(join11(resolvedOutdir, "_prerendered"), {
2205
3096
  force: true,
2206
3097
  recursive: true
2207
3098
  });
@@ -2220,9 +3111,9 @@ console.log(\`
2220
3111
  const compileStart = performance.now();
2221
3112
  process.stdout.write(cliTag4("\x1B[36m", "Compiling standalone executable"));
2222
3113
  const entrypointCode = generateEntrypoint(resolvedOutdir, serverEntry, prerenderMap, absoluteVersion, buildConfig);
2223
- const entrypointPath = join10(resolvedOutdir, "_compile_entrypoint.ts");
3114
+ const entrypointPath = join11(resolvedOutdir, "_compile_entrypoint.ts");
2224
3115
  await Bun.write(entrypointPath, entrypointCode);
2225
- mkdirSync7(dirname4(resolvedOutfile), { recursive: true });
3116
+ mkdirSync8(dirname4(resolvedOutfile), { recursive: true });
2226
3117
  const result = await Bun.build({
2227
3118
  compile: { outfile: resolvedOutfile },
2228
3119
  define: { "process.env.NODE_ENV": '"production"' },
@@ -2244,13 +3135,13 @@ console.log(\`
2244
3135
  }
2245
3136
  console.log(` \x1B[2m(${getDurationString(performance.now() - compileStart)})\x1B[0m`);
2246
3137
  try {
2247
- unlinkSync3(entrypointPath);
3138
+ unlinkSync4(entrypointPath);
2248
3139
  } catch {}
2249
3140
  const BYTES_PER_MB = 1048576;
2250
3141
  const size = (Bun.file(resolvedOutfile).size / BYTES_PER_MB).toFixed(0);
2251
3142
  const totalDuration = getDurationString(performance.now() - totalStart);
2252
3143
  console.log(cliTag4("\x1B[32m", `Compiled to ${resolvedOutfile} (${size}MB) in ${totalDuration}`));
2253
- console.log(cliTag4("\x1B[2m", `Run with: ./${basename3(resolvedOutfile)}`));
3144
+ console.log(cliTag4("\x1B[2m", `Run with: ./${basename4(resolvedOutfile)}`));
2254
3145
  sendTelemetryEvent("compile:complete", {
2255
3146
  durationMs: Math.round(performance.now() - totalStart),
2256
3147
  entry: serverEntry,
@@ -2314,8 +3205,8 @@ var exports_typecheck = {};
2314
3205
  __export(exports_typecheck, {
2315
3206
  typecheck: () => typecheck
2316
3207
  });
2317
- import { resolve as resolve12, join as join11 } from "path";
2318
- import { existsSync as existsSync12, readFileSync as readFileSync13 } from "fs";
3208
+ import { resolve as resolve12, join as join12 } from "path";
3209
+ import { existsSync as existsSync12, readFileSync as readFileSync14 } from "fs";
2319
3210
  import { mkdir as mkdir2, writeFile } from "fs/promises";
2320
3211
  var isCommandService3 = (service) => service.kind === "command" || Array.isArray(service.command), resolveConfigPath = (configPath2) => resolve12(configPath2 ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts"), getTypecheckTargets = async (configPath2) => {
2321
3212
  if (!existsSync12(resolveConfigPath(configPath2))) {
@@ -2394,7 +3285,7 @@ Found ${errorCount} error${suffix}.`;
2394
3285
  return candidates.find((candidate) => existsSync12(candidate)) ?? candidates[0];
2395
3286
  }, ABSOLUTE_TYPECHECK_FILES, readProjectTsconfig = () => {
2396
3287
  try {
2397
- return JSON.parse(readFileSync13(resolve12("tsconfig.json"), "utf-8"));
3288
+ return JSON.parse(readFileSync14(resolve12("tsconfig.json"), "utf-8"));
2398
3289
  } catch {
2399
3290
  return {};
2400
3291
  }
@@ -2422,7 +3313,7 @@ Found ${errorCount} error${suffix}.`;
2422
3313
  console.error("\x1B[31m\u2717\x1B[0m vue-tsc is required for Vue type checking. Install it: bun add -d vue-tsc");
2423
3314
  process.exit(1);
2424
3315
  }
2425
- const vueTsconfigPath = join11(cacheDir, "tsconfig.vue-check.json");
3316
+ const vueTsconfigPath = join12(cacheDir, "tsconfig.vue-check.json");
2426
3317
  return writeFile(vueTsconfigPath, JSON.stringify({
2427
3318
  compilerOptions: {
2428
3319
  rootDir: ".."
@@ -2437,7 +3328,7 @@ Found ${errorCount} error${suffix}.`;
2437
3328
  resolve12(vueTsconfigPath),
2438
3329
  "--incremental",
2439
3330
  "--tsBuildInfoFile",
2440
- join11(cacheDir, "vue-tsc.tsbuildinfo"),
3331
+ join12(cacheDir, "vue-tsc.tsbuildinfo"),
2441
3332
  "--pretty"
2442
3333
  ]));
2443
3334
  }, buildAngularCheck = async (cacheDir, angularDir) => {
@@ -2446,7 +3337,7 @@ Found ${errorCount} error${suffix}.`;
2446
3337
  console.error("\x1B[31m\u2717\x1B[0m @angular/compiler-cli is required for Angular type checking. Install it: bun add -d @angular/compiler-cli");
2447
3338
  process.exit(1);
2448
3339
  }
2449
- const angularTsconfigPath = join11(cacheDir, "tsconfig.angular-check.json");
3340
+ const angularTsconfigPath = join12(cacheDir, "tsconfig.angular-check.json");
2450
3341
  await writeFile(angularTsconfigPath, JSON.stringify({
2451
3342
  angularCompilerOptions: {
2452
3343
  strictTemplates: true
@@ -2466,7 +3357,7 @@ Found ${errorCount} error${suffix}.`;
2466
3357
  console.error("\x1B[31m\u2717\x1B[0m typescript is required for type checking. Install it: bun add -d typescript");
2467
3358
  process.exit(1);
2468
3359
  }
2469
- const tscConfigPath = join11(cacheDir, "tsconfig.typecheck.json");
3360
+ const tscConfigPath = join12(cacheDir, "tsconfig.typecheck.json");
2470
3361
  return writeFile(tscConfigPath, JSON.stringify({
2471
3362
  compilerOptions: {
2472
3363
  rootDir: ".."
@@ -2481,7 +3372,7 @@ Found ${errorCount} error${suffix}.`;
2481
3372
  resolve12(tscConfigPath),
2482
3373
  "--incremental",
2483
3374
  "--tsBuildInfoFile",
2484
- join11(cacheDir, "tsc.tsbuildinfo"),
3375
+ join12(cacheDir, "tsc.tsbuildinfo"),
2485
3376
  "--pretty"
2486
3377
  ]));
2487
3378
  }, buildSvelteCheck = async (cacheDir, svelteDir) => {
@@ -2490,7 +3381,7 @@ Found ${errorCount} error${suffix}.`;
2490
3381
  console.error("\x1B[31m\u2717\x1B[0m svelte-check is required for Svelte type checking. Install it: bun add -d svelte-check");
2491
3382
  process.exit(1);
2492
3383
  }
2493
- const svelteTsconfigPath = join11(cacheDir, "tsconfig.svelte-check.json");
3384
+ const svelteTsconfigPath = join12(cacheDir, "tsconfig.svelte-check.json");
2494
3385
  await writeFile(svelteTsconfigPath, JSON.stringify({
2495
3386
  extends: resolve12("tsconfig.json"),
2496
3387
  files: ABSOLUTE_TYPECHECK_FILES,
@@ -2567,7 +3458,7 @@ init_constants();
2567
3458
  init_startupBanner();
2568
3459
  var {$: $2, env } = globalThis.Bun;
2569
3460
  import { spawn as nodeSpawn } from "child_process";
2570
- import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
3461
+ import { createWriteStream, existsSync as existsSync5, readFileSync as readFileSync7 } from "fs";
2571
3462
  import { resolve as resolve3 } from "path";
2572
3463
 
2573
3464
  // src/cli/interactive.ts
@@ -2840,6 +3731,7 @@ var createInteractiveHandler = (actions) => {
2840
3731
  // src/cli/scripts/dev.ts
2841
3732
  init_telemetryEvent();
2842
3733
  init_buildDirectoryLock();
3734
+ init_instanceRegistry();
2843
3735
  init_loadConfig();
2844
3736
 
2845
3737
  // src/utils/resolveDevPort.ts
@@ -2882,6 +3774,7 @@ var resolveDevPort = async (requestedPort, options = {}) => {
2882
3774
  init_utils();
2883
3775
  var cliTag = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`;
2884
3776
  var DEFAULT_PORT_RANGE = 10;
3777
+ var ANSI_LOG_REGEX = new RegExp(`${String.fromCharCode(ANSI_ESCAPE_CODE)}\\[[0-?]*[ -/]*[@-~]`, "g");
2885
3778
  var confirmPrompt = (message, defaultYes = true) => {
2886
3779
  const { promise, resolve: resolvePrompt } = Promise.withResolvers();
2887
3780
  let selected = defaultYes;
@@ -2986,6 +3879,37 @@ var dev = async (serverEntry, configPath2) => {
2986
3879
  console.log(cliTag("\x1B[33m", `Port ${resolvedDev.port} is in use, trying another one... \u2192 http://${displayHost}:${port}/`));
2987
3880
  }
2988
3881
  updateLockMetadata(buildDirectory, { port });
3882
+ const instancePid = process.pid;
3883
+ const instanceLogFile = instanceLogPath(instancePid);
3884
+ const relaunchCommand = [
3885
+ process.execPath,
3886
+ process.argv[1] ?? "",
3887
+ "dev",
3888
+ serverEntry,
3889
+ ...configPath2 ? ["--config", configPath2] : []
3890
+ ].filter((part) => part.length > 0);
3891
+ registerInstance({
3892
+ command: relaunchCommand,
3893
+ configPath: configPath2 ?? null,
3894
+ controllerPid: instancePid,
3895
+ cwd: process.cwd(),
3896
+ frameworks: [],
3897
+ host: resolvedDev.host,
3898
+ https: httpsEnabled,
3899
+ logFile: instanceLogFile,
3900
+ name: resolveProjectName(process.cwd()),
3901
+ pid: instancePid,
3902
+ port,
3903
+ ppid: process.ppid,
3904
+ source: "dev",
3905
+ startedAt: new Date().toISOString()
3906
+ });
3907
+ const instanceLog = createWriteStream(instanceLogFile, { flags: "w" });
3908
+ const writeInstanceLog = (text) => {
3909
+ try {
3910
+ instanceLog.write(text.replace(ANSI_LOG_REGEX, ""));
3911
+ } catch {}
3912
+ };
2989
3913
  const usesDocker = existsSync5(resolve3(COMPOSE_PATH));
2990
3914
  const scripts = usesDocker ? await readDbScripts() : null;
2991
3915
  if (scripts)
@@ -3059,6 +3983,7 @@ var dev = async (serverEntry, configPath2) => {
3059
3983
  console.log(cliTag("\x1B[36m", `Port changed in config \u2014 switching to http://${displayHost}:${probe.port}/`));
3060
3984
  port = probe.port;
3061
3985
  updateLockMetadata(buildDirectory, { port });
3986
+ updateInstance(instancePid, { port });
3062
3987
  }
3063
3988
  resolvedDev = dev2;
3064
3989
  }
@@ -3069,7 +3994,7 @@ var dev = async (serverEntry, configPath2) => {
3069
3994
  for (const name of candidates) {
3070
3995
  let text;
3071
3996
  try {
3072
- text = readFileSync6(resolve3(process.cwd(), name), "utf8");
3997
+ text = readFileSync7(resolve3(process.cwd(), name), "utf8");
3073
3998
  } catch {
3074
3999
  continue;
3075
4000
  }
@@ -3099,6 +4024,7 @@ var dev = async (serverEntry, configPath2) => {
3099
4024
  env: {
3100
4025
  ...process.env,
3101
4026
  ...readDotenvFiles(),
4027
+ ABSOLUTE_INSTANCE_MANAGED: "1",
3102
4028
  FORCE_COLOR: "1",
3103
4029
  NODE_ENV: "development",
3104
4030
  ABSOLUTE_PORT: String(port),
@@ -3115,6 +4041,7 @@ var dev = async (serverEntry, configPath2) => {
3115
4041
  if (serverReady)
3116
4042
  interactive?.clearPrompt();
3117
4043
  dest.write(chunk);
4044
+ writeInstanceLog(chunk.toString());
3118
4045
  handleChunk(chunk);
3119
4046
  });
3120
4047
  };
@@ -3140,7 +4067,7 @@ var dev = async (serverEntry, configPath2) => {
3140
4067
  };
3141
4068
  try {
3142
4069
  const { watch } = await import("fs");
3143
- const { dirname: dirname3, join: join5 } = await import("path");
4070
+ const { dirname: dirname3, join: join6 } = await import("path");
3144
4071
  const absServerEntry = resolve3(serverEntry);
3145
4072
  const serverEntryDir = dirname3(absServerEntry);
3146
4073
  const ROOT_RESTART_DENY = new Set([
@@ -3173,13 +4100,13 @@ var dev = async (serverEntry, configPath2) => {
3173
4100
  if (now - last < 100)
3174
4101
  return;
3175
4102
  recentlyHandled.set(filename, now);
3176
- scheduleServerRestart(join5(serverEntryDir, filename));
4103
+ scheduleServerRestart(join6(serverEntryDir, filename));
3177
4104
  };
3178
4105
  const recoveryScan = async () => {
3179
4106
  let entries;
3180
4107
  try {
3181
- const { readdirSync } = await import("fs");
3182
- entries = readdirSync(serverEntryDir, { withFileTypes: true });
4108
+ const { readdirSync: readdirSync2 } = await import("fs");
4109
+ entries = readdirSync2(serverEntryDir, { withFileTypes: true });
3183
4110
  } catch {
3184
4111
  return;
3185
4112
  }
@@ -3192,7 +4119,7 @@ var dev = async (serverEntry, configPath2) => {
3192
4119
  continue;
3193
4120
  let st;
3194
4121
  try {
3195
- st = statSync(join5(serverEntryDir, entry.name));
4122
+ st = statSync(join6(serverEntryDir, entry.name));
3196
4123
  } catch {
3197
4124
  continue;
3198
4125
  }
@@ -3235,6 +4162,7 @@ var dev = async (serverEntry, configPath2) => {
3235
4162
  cfg.angularDirectory && "angular"
3236
4163
  ].filter((val) => Boolean(val));
3237
4164
  } catch {}
4165
+ updateInstance(instancePid, { frameworks });
3238
4166
  sendTelemetryEvent("dev:start", { entry: serverEntry, frameworks });
3239
4167
  const killChildTree = (signal) => {
3240
4168
  const childPid = serverProcess.pid;
@@ -3273,6 +4201,10 @@ var dev = async (serverEntry, configPath2) => {
3273
4201
  });
3274
4202
  if (scripts)
3275
4203
  await stopDatabase(scripts);
4204
+ try {
4205
+ instanceLog.end();
4206
+ } catch {}
4207
+ deregisterInstance(instancePid);
3276
4208
  process.exit(exitCode);
3277
4209
  };
3278
4210
  const restartServer = async () => {
@@ -3451,7 +4383,7 @@ var dev = async (serverEntry, configPath2) => {
3451
4383
  };
3452
4384
 
3453
4385
  // src/cli/scripts/eslint.ts
3454
- import { existsSync as existsSync6, readFileSync as readFileSync7, rmSync as rmSync2 } from "fs";
4386
+ import { existsSync as existsSync6, readFileSync as readFileSync8, rmSync as rmSync2 } from "fs";
3455
4387
  import { resolve as resolve4 } from "path";
3456
4388
  var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache";
3457
4389
  var getCacheLocation = () => process.env.ABSOLUTE_ESLINT_CACHE?.trim() || DEFAULT_CACHE_LOCATION;
@@ -3608,7 +4540,7 @@ var checkForMisplacedIgnores = () => {
3608
4540
  return;
3609
4541
  let source;
3610
4542
  try {
3611
- source = readFileSync7(configPath2, "utf-8");
4543
+ source = readFileSync8(configPath2, "utf-8");
3612
4544
  } catch {
3613
4545
  return;
3614
4546
  }
@@ -3697,7 +4629,7 @@ var eslint = async (args) => {
3697
4629
  init_constants();
3698
4630
  init_utils();
3699
4631
  import { execSync as execSync2 } from "child_process";
3700
- import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
4632
+ import { existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
3701
4633
  import { arch as arch2, cpus, platform as platform3, totalmem, version } from "os";
3702
4634
  import { resolve as resolve5 } from "path";
3703
4635
  var bold = (str) => `\x1B[1m${str}\x1B[0m`;
@@ -3719,7 +4651,7 @@ var getPackageVersion = (packageName) => {
3719
4651
  const pkgPath = __require.resolve(`${packageName}/package.json`, {
3720
4652
  paths: [process.cwd()]
3721
4653
  });
3722
- const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
4654
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
3723
4655
  const ver = pkg.version;
3724
4656
  return ver;
3725
4657
  } catch {
@@ -3741,7 +4673,7 @@ var getAbsoluteVersion = () => {
3741
4673
  return getPackageVersion("@absolutejs/absolute");
3742
4674
  };
3743
4675
  var readPackageVersion = (pkgPath) => {
3744
- const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
4676
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
3745
4677
  const ver = pkg.version;
3746
4678
  return ver;
3747
4679
  };
@@ -3843,7 +4775,7 @@ var info = () => {
3843
4775
  // src/cli/cache.ts
3844
4776
  init_constants();
3845
4777
  import { mkdir } from "fs/promises";
3846
- import { join as join5 } from "path";
4778
+ import { join as join6 } from "path";
3847
4779
  var {Glob } = globalThis.Bun;
3848
4780
  var CACHE_DIR = ".absolutejs";
3849
4781
  var MAX_FILES_PER_BATCH = 200;
@@ -3895,7 +4827,7 @@ var hashFiles = async (paths) => {
3895
4827
  };
3896
4828
  var loadCache = async (tool) => {
3897
4829
  try {
3898
- const path = join5(CACHE_DIR, `${tool}.cache.json`);
4830
+ const path = join6(CACHE_DIR, `${tool}.cache.json`);
3899
4831
  const data = await Bun.file(path).json();
3900
4832
  const result = data;
3901
4833
  return result;
@@ -3942,7 +4874,7 @@ var runTool = async (adapter, args) => {
3942
4874
  };
3943
4875
  var saveCache = async (tool, data) => {
3944
4876
  await mkdir(CACHE_DIR, { recursive: true });
3945
- const path = join5(CACHE_DIR, `${tool}.cache.json`);
4877
+ const path = join6(CACHE_DIR, `${tool}.cache.json`);
3946
4878
  await Bun.write(path, JSON.stringify(data, null, "\t"));
3947
4879
  };
3948
4880
 
@@ -3979,13 +4911,14 @@ var prettier = async (args) => {
3979
4911
  // src/cli/scripts/start.ts
3980
4912
  init_constants();
3981
4913
  init_getDurationString();
4914
+ init_instanceRegistry();
3982
4915
  init_loadConfig();
3983
4916
  init_startupBanner();
3984
4917
  init_telemetryEvent();
3985
4918
  init_utils();
3986
4919
  var {env: env2 } = globalThis.Bun;
3987
- import { existsSync as existsSync8, readFileSync as readFileSync10, rmSync as rmSync3 } from "fs";
3988
- import { basename, join as join8, resolve as resolve7 } from "path";
4920
+ import { existsSync as existsSync8, readFileSync as readFileSync11, rmSync as rmSync3 } from "fs";
4921
+ import { basename as basename2, join as join9, resolve as resolve7 } from "path";
3989
4922
  var cliTag2 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`;
3990
4923
  var resolvePackageVersion = (candidates) => {
3991
4924
  for (const candidate of candidates) {
@@ -3998,7 +4931,7 @@ var resolvePackageVersion = (candidates) => {
3998
4931
  };
3999
4932
  var readPackageVersion2 = (candidate) => {
4000
4933
  try {
4001
- const pkg = JSON.parse(readFileSync10(candidate, "utf-8"));
4934
+ const pkg = JSON.parse(readFileSync11(candidate, "utf-8"));
4002
4935
  if (pkg.name !== "@absolutejs/absolute")
4003
4936
  return null;
4004
4937
  const ver = pkg.version;
@@ -4062,6 +4995,7 @@ var prerenderStaticPages = async (outputPath, prerenderPort, resolvedOutdir, sta
4062
4995
  killStaleProcesses(prerenderPort);
4063
4996
  const result = await prerenderWithServer2(outputPath, prerenderPort, resolvedOutdir, staticConfig, {
4064
4997
  ABSOLUTE_BUILD_DIR: resolvedOutdir,
4998
+ ABSOLUTE_INSTANCE_MANAGED: "1",
4065
4999
  ABSOLUTE_VERSION: absoluteVersion,
4066
5000
  FORCE_COLOR: "0",
4067
5001
  NODE_ENV: "production",
@@ -4077,7 +5011,7 @@ var prerenderStaticPages = async (outputPath, prerenderPort, resolvedOutdir, sta
4077
5011
  var start = async (serverEntry, outdir, configPath2) => {
4078
5012
  const port = Number(env2.PORT) || DEFAULT_PORT;
4079
5013
  killStaleProcesses(port);
4080
- const entryName = basename(serverEntry).replace(/\.[^.]+$/, "");
5014
+ const entryName = basename2(serverEntry).replace(/\.[^.]+$/, "");
4081
5015
  const resolvedOutdir = resolve7(outdir ?? "dist");
4082
5016
  const absoluteVersion = resolvePackageVersion([
4083
5017
  resolve7(import.meta.dir, "..", "..", "..", "package.json"),
@@ -4104,7 +5038,7 @@ var start = async (serverEntry, outdir, configPath2) => {
4104
5038
  if (!build)
4105
5039
  throw new Error("Could not locate build module");
4106
5040
  await build(buildConfig);
4107
- rmSync3(join8(resolvedOutdir, "_prerendered"), {
5041
+ rmSync3(join9(resolvedOutdir, "_prerendered"), {
4108
5042
  force: true,
4109
5043
  recursive: true
4110
5044
  });
@@ -4217,9 +5151,9 @@ var start = async (serverEntry, outdir, configPath2) => {
4217
5151
  process.exit(1);
4218
5152
  }
4219
5153
  if (existsSync8(resolve7(resolvedOutdir, "angular", "vendor", "server"))) {
4220
- const { readdirSync } = await import("fs");
5154
+ const { readdirSync: readdirSync2 } = await import("fs");
4221
5155
  const vendorDir = resolve7(resolvedOutdir, "angular", "vendor", "server");
4222
- const vendorEntries = readdirSync(vendorDir).filter((f) => f.endsWith(".js"));
5156
+ const vendorEntries = readdirSync2(vendorDir).filter((f) => f.endsWith(".js"));
4223
5157
  const angularServerVendorPaths = {};
4224
5158
  const { relative: pathRelative, dirname: pathDirname } = await import("path");
4225
5159
  for (const file of vendorEntries) {
@@ -4266,6 +5200,7 @@ var start = async (serverEntry, outdir, configPath2) => {
4266
5200
  ...process.env,
4267
5201
  ABSOLUTE_BUILD_DIR: resolvedOutdir,
4268
5202
  ABSOLUTE_BUILD_DURATION: String(Math.round(totalDuration)),
5203
+ ABSOLUTE_INSTANCE_MANAGED: "1",
4269
5204
  ABSOLUTE_VERSION: absoluteVersion,
4270
5205
  FORCE_COLOR: "1",
4271
5206
  NODE_ENV: "production",
@@ -4275,10 +5210,35 @@ var start = async (serverEntry, outdir, configPath2) => {
4275
5210
  stdin: "inherit",
4276
5211
  stdout: "inherit"
4277
5212
  });
5213
+ const relaunchCommand = [
5214
+ process.execPath,
5215
+ process.argv[1] ?? "",
5216
+ "start",
5217
+ serverEntry,
5218
+ ...outdir ? ["--outdir", outdir] : [],
5219
+ ...configPath2 ? ["--config", configPath2] : []
5220
+ ].filter((part) => part.length > 0);
5221
+ registerInstance({
5222
+ command: relaunchCommand,
5223
+ configPath: configPath2 ?? null,
5224
+ controllerPid: process.pid,
5225
+ cwd: process.cwd(),
5226
+ frameworks,
5227
+ host: env2.ABSOLUTE_HOST ?? env2.HOST ?? "localhost",
5228
+ https: env2.ABSOLUTE_HTTPS === "true",
5229
+ logFile: null,
5230
+ name: resolveProjectName(process.cwd()),
5231
+ pid: process.pid,
5232
+ port,
5233
+ ppid: process.ppid,
5234
+ source: "start",
5235
+ startedAt: new Date().toISOString()
5236
+ });
4278
5237
  const cleanup = async (exitCode2 = 0) => {
4279
5238
  if (cleaning)
4280
5239
  return;
4281
5240
  cleaning = true;
5241
+ deregisterInstance(process.pid);
4282
5242
  sendTelemetryEvent("start:session-duration", {
4283
5243
  duration: Math.round((Date.now() - sessionStart) / MILLISECONDS_IN_A_SECOND),
4284
5244
  entry: serverEntry
@@ -4311,27 +5271,24 @@ var start = async (serverEntry, outdir, configPath2) => {
4311
5271
  init_constants();
4312
5272
  init_loadConfig();
4313
5273
  init_getDurationString();
5274
+ init_instanceRegistry();
4314
5275
  import {
4315
5276
  appendFileSync,
4316
5277
  existsSync as existsSync9,
4317
- mkdirSync as mkdirSync5,
4318
- readdirSync,
4319
- readFileSync as readFileSync11,
4320
- unlinkSync as unlinkSync2,
4321
- writeFileSync as writeFileSync3
5278
+ mkdirSync as mkdirSync6,
5279
+ readdirSync as readdirSync2,
5280
+ readFileSync as readFileSync12,
5281
+ unlinkSync as unlinkSync3,
5282
+ writeFileSync as writeFileSync4
4322
5283
  } from "fs";
4323
5284
  import { createConnection } from "net";
4324
5285
  import { resolve as resolve8 } from "path";
4325
5286
 
4326
5287
  // src/cli/workspaceTui.ts
4327
5288
  init_constants();
5289
+ init_tuiPrimitives();
4328
5290
  init_getDurationString();
4329
- import { openSync as openSync2 } from "fs";
4330
- import { ReadStream as ReadStream2 } from "tty";
4331
5291
  var MAX_LOG_ENTRIES = 400;
4332
- var ESCAPE = "\x1B";
4333
- var ANSI_REGEX = new RegExp(`${String.fromCharCode(ANSI_ESCAPE_CODE)}\\[[0-?]*[ -/]*[@-~]`, "g");
4334
- var ANSI_ESCAPE_PREFIX = `${ESCAPE}[`;
4335
5292
  var SHORTCUTS2 = new Map([
4336
5293
  ["c", "clear"],
4337
5294
  ["h", "help"],
@@ -4340,15 +5297,6 @@ var SHORTCUTS2 = new Map([
4340
5297
  ["q", "quit"],
4341
5298
  ["r", "restart"]
4342
5299
  ]);
4343
- var colors = {
4344
- bold: "\x1B[1m",
4345
- cyan: "\x1B[36m",
4346
- dim: "\x1B[2m",
4347
- green: "\x1B[32m",
4348
- red: "\x1B[31m",
4349
- reset: "\x1B[0m",
4350
- yellow: "\x1B[33m"
4351
- };
4352
5300
  var helpLines = [
4353
5301
  "Hotkeys",
4354
5302
  " h Toggle help",
@@ -4367,108 +5315,6 @@ var helpLines = [
4367
5315
  " Press Esc to exit shell mode or dismiss help.",
4368
5316
  " Use \u2191 and \u2193 to recall prior shell commands."
4369
5317
  ];
4370
- var trySetRawMode2 = () => {
4371
- if (typeof process.stdin.setRawMode !== "function") {
4372
- return null;
4373
- }
4374
- try {
4375
- process.stdin.setRawMode(true);
4376
- } catch {
4377
- return null;
4378
- }
4379
- return process.stdin;
4380
- };
4381
- var openTtyStream2 = () => {
4382
- const fromStdin = trySetRawMode2();
4383
- if (fromStdin) {
4384
- return fromStdin;
4385
- }
4386
- try {
4387
- const ttyStream = new ReadStream2(openSync2("/dev/tty", "r"));
4388
- ttyStream.setRawMode(true);
4389
- return ttyStream;
4390
- } catch {
4391
- return null;
4392
- }
4393
- };
4394
- var stripAnsi = (value) => value.replace(ANSI_REGEX, "");
4395
- var visibleLength = (value) => stripAnsi(value).length;
4396
- var truncateText = (value, width) => {
4397
- if (width <= 0) {
4398
- return "";
4399
- }
4400
- if (value.length <= width) {
4401
- return value;
4402
- }
4403
- if (width <= 1) {
4404
- return value.slice(0, width);
4405
- }
4406
- return `${value.slice(0, width - 1)}\u2026`;
4407
- };
4408
- var padLine = (value, width) => {
4409
- const plainLength = visibleLength(value);
4410
- if (plainLength >= width) {
4411
- return value;
4412
- }
4413
- return `${value}${" ".repeat(width - plainLength)}`;
4414
- };
4415
- var appendRightEdge = (value, width, marker) => {
4416
- if (width <= 0) {
4417
- return "";
4418
- }
4419
- return `${padLine(value, Math.max(0, width - 1))}${marker}`;
4420
- };
4421
- var splitLongWord = (word, width) => {
4422
- const parts = [];
4423
- for (let index = 0;index < word.length; index += width) {
4424
- parts.push(word.slice(index, index + width));
4425
- }
4426
- return parts;
4427
- };
4428
- var appendWrappedWord = (lines, current, word, width) => {
4429
- if (current.length === 0) {
4430
- if (word.length <= width)
4431
- return word;
4432
- lines.push(...splitLongWord(word, width));
4433
- return "";
4434
- }
4435
- const next = `${current} ${word}`;
4436
- if (next.length <= width)
4437
- return next;
4438
- lines.push(current);
4439
- if (word.length <= width)
4440
- return word;
4441
- lines.push(...splitLongWord(word, width));
4442
- return "";
4443
- };
4444
- var wrapLine = (line, width) => {
4445
- if (line.length === 0)
4446
- return [""];
4447
- if (line.length <= width)
4448
- return [line];
4449
- const lines = [];
4450
- let current = "";
4451
- for (const word of line.split(/\s+/)) {
4452
- current = appendWrappedWord(lines, current, word, width);
4453
- }
4454
- if (current.length > 0)
4455
- lines.push(current);
4456
- return lines;
4457
- };
4458
- var wrapText = (value, width) => {
4459
- if (width <= 0) {
4460
- return [""];
4461
- }
4462
- const lines = value.split(`
4463
- `).flatMap((rawLine) => wrapLine(rawLine.trimEnd(), width));
4464
- return lines.length > 0 ? lines : [""];
4465
- };
4466
- var formatTimestamp2 = () => new Date().toLocaleTimeString([], {
4467
- hour: "numeric",
4468
- hour12: true,
4469
- minute: "2-digit",
4470
- second: "2-digit"
4471
- });
4472
5318
  var getStatusColor = (status2) => {
4473
5319
  if (status2 === "ready")
4474
5320
  return colors.green;
@@ -4528,12 +5374,6 @@ var getVisibleLogContent = (contentLines, logHeight, logScrollOffset) => {
4528
5374
  const start2 = Math.max(0, end - logHeight);
4529
5375
  return contentLines.slice(start2, end);
4530
5376
  };
4531
- var isPartialEscapeSequence = (value) => {
4532
- if (!value.startsWith(ANSI_ESCAPE_PREFIX)) {
4533
- return false;
4534
- }
4535
- return Array.from(value.slice(ANSI_ESCAPE_PREFIX.length)).every((char) => char >= "0" && char <= "9");
4536
- };
4537
5377
  var createWorkspaceTui = ({
4538
5378
  actions,
4539
5379
  headless: headlessOption,
@@ -5004,10 +5844,10 @@ var stripAnsi2 = (value) => value.replace(ANSI_REGEX2, "");
5004
5844
  var sanitizeLogFileName = (value) => value.replace(/[^a-zA-Z0-9._-]/g, "_") || "unknown";
5005
5845
  var createWorkspaceLogSink = (appendLog) => {
5006
5846
  const logDirectory = resolve8(".absolutejs", "workspace", "logs");
5007
- mkdirSync5(logDirectory, { recursive: true });
5008
- readdirSync(logDirectory).filter((file) => file.endsWith(".log")).forEach((file) => unlinkSync2(resolve8(logDirectory, file)));
5009
- writeFileSync3(resolve8(logDirectory, "all.log"), "");
5010
- writeFileSync3(resolve8(logDirectory, "workspace.log"), "");
5847
+ mkdirSync6(logDirectory, { recursive: true });
5848
+ readdirSync2(logDirectory).filter((file) => file.endsWith(".log")).forEach((file) => unlinkSync3(resolve8(logDirectory, file)));
5849
+ writeFileSync4(resolve8(logDirectory, "all.log"), "");
5850
+ writeFileSync4(resolve8(logDirectory, "workspace.log"), "");
5011
5851
  const initializedSources = new Set(["workspace"]);
5012
5852
  const writeLog = (source, message, level) => {
5013
5853
  const cleanMessage = stripAnsi2(message).trimEnd();
@@ -5019,7 +5859,7 @@ var createWorkspaceLogSink = (appendLog) => {
5019
5859
  `;
5020
5860
  const sourceFile = resolve8(logDirectory, `${sanitizeLogFileName(source)}.log`);
5021
5861
  if (!initializedSources.has(source)) {
5022
- writeFileSync3(sourceFile, "");
5862
+ writeFileSync4(sourceFile, "");
5023
5863
  initializedSources.add(source);
5024
5864
  }
5025
5865
  appendFileSync(sourceFile, line);
@@ -5035,7 +5875,7 @@ var createWorkspaceLogSink = (appendLog) => {
5035
5875
  };
5036
5876
  var readPackageVersion3 = (candidate) => {
5037
5877
  try {
5038
- const pkg = JSON.parse(readFileSync11(candidate, "utf-8"));
5878
+ const pkg = JSON.parse(readFileSync12(candidate, "utf-8"));
5039
5879
  if (pkg.name !== "@absolutejs/absolute") {
5040
5880
  return null;
5041
5881
  }
@@ -5407,6 +6247,7 @@ var resolveAbsoluteServiceConfigPath = (service, cwd, options) => {
5407
6247
  var resolveService = (name, service, workspaceEnv, options) => {
5408
6248
  const cwd = resolve8(service.cwd ?? ".");
5409
6249
  const envVars = Object.assign(getDefinedProcessEnv(), workspaceEnv, service.port ? { PORT: String(service.port) } : {}, service.env, {
6250
+ ABSOLUTE_INSTANCE_MANAGED: "1",
5410
6251
  ABSOLUTE_WORKSPACE_MANAGED: "1",
5411
6252
  ABSOLUTE_WORKSPACE_SERVICE_NAME: name,
5412
6253
  ABSOLUTE_WORKSPACE_SERVICE_VISIBILITY: getVisibility(service),
@@ -5512,6 +6353,7 @@ var workspace = async (subcommand, options) => {
5512
6353
  try {
5513
6354
  service.process.kill();
5514
6355
  } catch {}
6356
+ deregisterInstance(service.process.pid);
5515
6357
  };
5516
6358
  const runShutdownHookSafely = async (service) => {
5517
6359
  try {
@@ -5654,6 +6496,22 @@ var workspace = async (subcommand, options) => {
5654
6496
  resolved
5655
6497
  };
5656
6498
  running.push(runningService);
6499
+ registerInstance({
6500
+ command: [],
6501
+ configPath: resolved.configPath ?? null,
6502
+ controllerPid: process.pid,
6503
+ cwd: resolved.cwd,
6504
+ frameworks: [],
6505
+ host: getServicePublicHost(resolved.service),
6506
+ https: getServiceProtocol(resolved.service) === "https",
6507
+ logFile: resolve8(workspaceLogs.logDirectory, `${sanitizeLogFileName(name)}.log`),
6508
+ name,
6509
+ pid: processHandle.pid,
6510
+ port: resolved.service.port ?? null,
6511
+ ppid: process.pid,
6512
+ source: "workspace",
6513
+ startedAt: new Date().toISOString()
6514
+ });
5657
6515
  processHandle.exited.then(handleServiceExit.bind(null, runningService));
5658
6516
  await waitForReady(resolved);
5659
6517
  const startedAt = serviceBootStartedAt.get(name);
@@ -5810,6 +6668,10 @@ if (command === "dev") {
5810
6668
  } else if (command === "prettier") {
5811
6669
  sendTelemetryEvent("cli:command", { command });
5812
6670
  await prettier(args);
6671
+ } else if (command === "ls" || command === "list" || command === "ps") {
6672
+ sendTelemetryEvent("cli:command", { command: "ls" });
6673
+ const { runList: runList2 } = await Promise.resolve().then(() => (init_list(), exports_list));
6674
+ await runList2(args);
5813
6675
  } else if (command === "info") {
5814
6676
  sendTelemetryEvent("cli:command", { command });
5815
6677
  info();
@@ -5847,6 +6709,7 @@ if (command === "dev") {
5847
6709
  console.error(" config [--port n] Open the unified config UI (ESLint, tsconfig, Prettier)");
5848
6710
  console.error(" eslint Run ESLint (cached)");
5849
6711
  console.error(" info Print system info for bug reports");
6712
+ console.error(" ls [--watch] [--json] List/manage running servers (alias: list)");
5850
6713
  console.error(" prettier Run Prettier check (cached)");
5851
6714
  console.error(" typecheck Run type checkers for all frameworks");
5852
6715
  console.error(" telemetry Manage anonymous telemetry");