@absolutejs/absolute 0.19.0-beta.1016 → 0.19.0-beta.1018

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 (43) 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 +2 -2
  11. package/dist/build.js.map +3 -3
  12. package/dist/cli/index.js +1215 -303
  13. package/dist/index.js +99 -9
  14. package/dist/index.js.map +6 -5
  15. package/dist/islands/index.js +2 -2
  16. package/dist/islands/index.js.map +3 -3
  17. package/dist/react/components/index.js +2 -2
  18. package/dist/react/components/index.js.map +3 -3
  19. package/dist/react/index.js +2 -2
  20. package/dist/react/index.js.map +3 -3
  21. package/dist/react/server.js +2 -2
  22. package/dist/react/server.js.map +3 -3
  23. package/dist/src/angular/components/constants.d.ts +13 -0
  24. package/dist/src/cli/instanceStatus.d.ts +21 -0
  25. package/dist/src/cli/listTui.d.ts +1 -0
  26. package/dist/src/cli/scripts/list.d.ts +1 -0
  27. package/dist/src/cli/tuiPrimitives.d.ts +14 -0
  28. package/dist/src/constants.d.ts +13 -0
  29. package/dist/src/utils/instanceRegistry.d.ts +13 -0
  30. package/dist/svelte/index.js +2 -2
  31. package/dist/svelte/index.js.map +3 -3
  32. package/dist/svelte/server.js +2 -2
  33. package/dist/svelte/server.js.map +3 -3
  34. package/dist/types/cli.d.ts +39 -0
  35. package/dist/vue/components/Image.js +2 -2
  36. package/dist/vue/components/Image.js.map +3 -3
  37. package/dist/vue/components/index.js +2 -2
  38. package/dist/vue/components/index.js.map +3 -3
  39. package/dist/vue/index.js +2 -2
  40. package/dist/vue/index.js.map +3 -3
  41. package/dist/vue/server.js +2 -2
  42. package/dist/vue/server.js.map +3 -3
  43. 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,126 @@ 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 = [
506
+ "compiled",
507
+ "dev",
508
+ "standalone",
509
+ "start",
510
+ "workspace"
511
+ ];
512
+ });
513
+
394
514
  // src/utils/loadConfig.ts
395
515
  import { resolve } from "path";
396
516
  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 +615,11 @@ var init_loadConfig = __esm(() => {
495
615
  // src/cli/utils.ts
496
616
  var {$ } = globalThis.Bun;
497
617
  import { execSync } from "child_process";
498
- import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
618
+ import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
499
619
  import { resolve as resolve2 } from "path";
500
620
  var COMPOSE_PATH = "db/docker-compose.db.yml", DEFAULT_SERVER_ENTRY = "src/backend/server.ts", isWSLEnvironment = () => {
501
621
  try {
502
- const release = readFileSync4("/proc/version", "utf-8");
622
+ const release = readFileSync5("/proc/version", "utf-8");
503
623
  return /microsoft|wsl/i.test(release);
504
624
  } catch {
505
625
  return false;
@@ -532,6 +652,32 @@ var COMPOSE_PATH = "db/docker-compose.db.yml", DEFAULT_SERVER_ENTRY = "src/backe
532
652
  return;
533
653
  }
534
654
  console.log(`\x1B[2m${formatTimestamp()}\x1B[0m \x1B[33m[cli]\x1B[0m \x1B[33m${message}\x1B[0m`);
655
+ }, openUrlInBrowser = (url, onError) => {
656
+ if (process.env.ABSOLUTE_NO_OPEN)
657
+ return false;
658
+ const { platform: platform2 } = process;
659
+ const isWSL = platform2 === "linux" && isWSLEnvironment();
660
+ let command;
661
+ if (isWSL) {
662
+ command = "cmd.exe";
663
+ } else if (platform2 === "darwin") {
664
+ command = "open";
665
+ } else if (platform2 === "win32") {
666
+ command = "start";
667
+ } else {
668
+ command = "xdg-open";
669
+ }
670
+ const commandArgs = isWSL ? ["/c", "start", url] : [url];
671
+ try {
672
+ Bun.spawn([command, ...commandArgs], {
673
+ stderr: "ignore",
674
+ stdout: "ignore"
675
+ });
676
+ return true;
677
+ } catch {
678
+ onError?.(`Could not open browser automatically. Visit ${url}`);
679
+ return false;
680
+ }
535
681
  }, printHelp = (subject = "server") => {
536
682
  const title = subject === "workspace" ? "workspace" : subject;
537
683
  console.log("");
@@ -592,15 +738,15 @@ __export(exports_devCert, {
592
738
  import {
593
739
  copyFileSync,
594
740
  existsSync as existsSync4,
595
- mkdirSync as mkdirSync3,
596
- readFileSync as readFileSync5,
741
+ mkdirSync as mkdirSync4,
742
+ readFileSync as readFileSync6,
597
743
  rmSync
598
744
  } from "fs";
599
745
  import { platform as platform2 } from "os";
600
- import { join as join4 } from "path";
746
+ import { join as join5 } from "path";
601
747
  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
748
  try {
603
- const certPem = readFileSync5(CERT_PATH, "utf-8");
749
+ const certPem = readFileSync6(CERT_PATH, "utf-8");
604
750
  const proc = Bun.spawnSync(["openssl", "x509", "-enddate", "-noout"], {
605
751
  stdin: new TextEncoder().encode(certPem)
606
752
  });
@@ -669,7 +815,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, devLog = (msg) => c
669
815
  generateSelfSigned();
670
816
  }
671
817
  }, ensureDevCert = () => {
672
- mkdirSync3(CERT_DIR, { recursive: true });
818
+ mkdirSync4(CERT_DIR, { recursive: true });
673
819
  if (hasCert()) {
674
820
  return { cert: CERT_PATH, key: KEY_PATH };
675
821
  }
@@ -689,8 +835,8 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, devLog = (msg) => c
689
835
  return null;
690
836
  try {
691
837
  return {
692
- cert: readFileSync5(paths.cert, "utf-8"),
693
- key: readFileSync5(paths.key, "utf-8")
838
+ cert: readFileSync6(paths.cert, "utf-8"),
839
+ key: readFileSync6(paths.key, "utf-8")
694
840
  };
695
841
  } catch {
696
842
  return null;
@@ -786,7 +932,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, devLog = (msg) => c
786
932
  if (platform2() !== "linux")
787
933
  return false;
788
934
  try {
789
- return /microsoft|wsl/i.test(readFileSync5("/proc/version", "utf-8"));
935
+ return /microsoft|wsl/i.test(readFileSync6("/proc/version", "utf-8"));
790
936
  } catch {
791
937
  return false;
792
938
  }
@@ -809,13 +955,13 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, devLog = (msg) => c
809
955
  const caRoot = mkcertCaRoot();
810
956
  if (!caRoot)
811
957
  return false;
812
- const rootCa = join4(caRoot, "rootCA.pem");
958
+ const rootCa = join5(caRoot, "rootCA.pem");
813
959
  if (!existsSync4(rootCa))
814
960
  return false;
815
961
  const winTemp = windowsTempDir();
816
962
  if (!winTemp)
817
963
  return false;
818
- const staged = join4(winTemp, "absolutejs-mkcert-rootCA.crt");
964
+ const staged = join5(winTemp, "absolutejs-mkcert-rootCA.crt");
819
965
  try {
820
966
  copyFileSync(rootCa, staged);
821
967
  } catch {
@@ -851,7 +997,7 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, devLog = (msg) => c
851
997
  devLog("Trusted the local CA in the Windows store \u2014 Chrome/Edge on Windows now accept dev HTTPS");
852
998
  } else {
853
999
  const caRoot = mkcertCaRoot();
854
- const hint = caRoot ? toWindowsPath(join4(caRoot, "rootCA.pem")) : null;
1000
+ const hint = caRoot ? toWindowsPath(join5(caRoot, "rootCA.pem")) : null;
855
1001
  devWarn("Could not auto-trust the local CA on Windows; Windows browsers may warn.");
856
1002
  if (hint) {
857
1003
  console.log(` Run in PowerShell: Import-Certificate -FilePath "${hint}" -CertStoreLocation Cert:\\CurrentUser\\Root`);
@@ -860,16 +1006,16 @@ var CERT_DIR, CERT_PATH, KEY_PATH, CERT_VALIDITY_DAYS = 365, devLog = (msg) => c
860
1006
  }
861
1007
  rmSync(CERT_PATH, { force: true });
862
1008
  rmSync(KEY_PATH, { force: true });
863
- mkdirSync3(CERT_DIR, { recursive: true });
1009
+ mkdirSync4(CERT_DIR, { recursive: true });
864
1010
  generateWithMkcert();
865
1011
  console.log("");
866
1012
  devLog("mkcert installed \u2014 HTTPS certificates are now locally trusted");
867
1013
  return true;
868
1014
  };
869
1015
  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");
1016
+ CERT_DIR = join5(process.cwd(), ".absolutejs");
1017
+ CERT_PATH = join5(CERT_DIR, "cert.pem");
1018
+ KEY_PATH = join5(CERT_DIR, "key.pem");
873
1019
  });
874
1020
 
875
1021
  // src/core/prerender.ts
@@ -882,15 +1028,15 @@ __export(exports_prerender, {
882
1028
  prerender: () => prerender,
883
1029
  PRERENDER_BYPASS_HEADER: () => PRERENDER_BYPASS_HEADER
884
1030
  });
885
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync9 } from "fs";
886
- import { join as join6 } from "path";
1031
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync10 } from "fs";
1032
+ import { join as join7 } from "path";
887
1033
  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
1034
  const metaPath = htmlPath.replace(/\.html$/, ".meta");
889
1035
  await Bun.write(metaPath, String(Date.now()));
890
1036
  }, readTimestamp = (htmlPath) => {
891
1037
  const metaPath = htmlPath.replace(/\.html$/, ".meta");
892
1038
  try {
893
- const content = readFileSync9(metaPath, "utf-8");
1039
+ const content = readFileSync10(metaPath, "utf-8");
894
1040
  return Number(content) || 0;
895
1041
  } catch {
896
1042
  return 0;
@@ -949,7 +1095,7 @@ var SERVER_OUTPUT_LIMIT = 4000, STARTUP_POLL_INTERVAL_MS = 100, DEFAULT_STARTUP_
949
1095
  return false;
950
1096
  const html = await res.text();
951
1097
  const fileName = routeToFilename(route);
952
- const filePath = join6(prerenderDir, fileName);
1098
+ const filePath = join7(prerenderDir, fileName);
953
1099
  await Bun.write(filePath, html);
954
1100
  await writeTimestamp(filePath);
955
1101
  return true;
@@ -975,14 +1121,14 @@ var SERVER_OUTPUT_LIMIT = 4000, STARTUP_POLL_INTERVAL_MS = 100, DEFAULT_STARTUP_
975
1121
  }
976
1122
  const html = await res.text();
977
1123
  const fileName = routeToFilename(route);
978
- const filePath = join6(prerenderDir, fileName);
1124
+ const filePath = join7(prerenderDir, fileName);
979
1125
  await Bun.write(filePath, html);
980
1126
  await writeTimestamp(filePath);
981
1127
  result.routes.set(route, filePath);
982
1128
  log?.(` Pre-rendered ${route} \u2192 ${fileName} (${html.length} bytes)`);
983
1129
  }, prerender = async (port, outDir, staticConfig, log) => {
984
- const prerenderDir = join6(outDir, "_prerendered");
985
- mkdirSync4(prerenderDir, { recursive: true });
1130
+ const prerenderDir = join7(outDir, "_prerendered");
1131
+ mkdirSync5(prerenderDir, { recursive: true });
986
1132
  const baseUrl = `http://localhost:${port}`;
987
1133
  let routes;
988
1134
  if (staticConfig.routes === "all") {
@@ -1138,7 +1284,7 @@ var init_nativeRewrite = __esm(() => {
1138
1284
 
1139
1285
  // src/build/rewriteImportsPlugin.ts
1140
1286
  import { readdir } from "fs/promises";
1141
- import { join as join7 } from "path";
1287
+ import { join as join8 } from "path";
1142
1288
  var escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), jsRewriteImports = (content, replacements) => {
1143
1289
  let result = content;
1144
1290
  for (const [specifier, webPath] of replacements) {
@@ -1216,7 +1362,7 @@ ${content}`;
1216
1362
  const entries = await readdir(dir);
1217
1363
  for (const entry of entries) {
1218
1364
  if (entry.endsWith(".js"))
1219
- allFiles.push(join7(dir, entry));
1365
+ allFiles.push(join8(dir, entry));
1220
1366
  }
1221
1367
  } catch {}
1222
1368
  }
@@ -1270,86 +1416,837 @@ var rewriteImports = async (outputPaths, vendorPaths) => {
1270
1416
  return;
1271
1417
  throw err;
1272
1418
  }
1273
- const rewritten = rewriteImportsInContent(original, vendorPaths);
1274
- if (rewritten === original)
1419
+ const rewritten = rewriteImportsInContent(original, vendorPaths);
1420
+ if (rewritten === original)
1421
+ return;
1422
+ try {
1423
+ await Bun.write(filePath, rewritten);
1424
+ } catch (err) {
1425
+ const code = err.code;
1426
+ if (code === "ENOENT")
1427
+ return;
1428
+ throw err;
1429
+ }
1430
+ }));
1431
+ }, rewriteVendorDirectories2;
1432
+ var init_rewriteImports = __esm(() => {
1433
+ init_rewriteImportsPlugin();
1434
+ rewriteVendorDirectories2 = rewriteVendorDirectories;
1435
+ });
1436
+
1437
+ // src/cli/tuiPrimitives.ts
1438
+ import { openSync as openSync2 } from "fs";
1439
+ import { ReadStream as ReadStream2 } from "tty";
1440
+ var ANSI_REGEX, trySetRawMode2 = () => {
1441
+ if (typeof process.stdin.setRawMode !== "function") {
1442
+ return null;
1443
+ }
1444
+ try {
1445
+ process.stdin.setRawMode(true);
1446
+ } catch {
1447
+ return null;
1448
+ }
1449
+ return process.stdin;
1450
+ }, splitLongWord = (word, width) => {
1451
+ const parts = [];
1452
+ for (let index = 0;index < word.length; index += width) {
1453
+ parts.push(word.slice(index, index + width));
1454
+ }
1455
+ return parts;
1456
+ }, appendWrappedWord = (lines, current, word, width) => {
1457
+ if (current.length === 0) {
1458
+ if (word.length <= width)
1459
+ return word;
1460
+ lines.push(...splitLongWord(word, width));
1461
+ return "";
1462
+ }
1463
+ const next = `${current} ${word}`;
1464
+ if (next.length <= width)
1465
+ return next;
1466
+ lines.push(current);
1467
+ if (word.length <= width)
1468
+ return word;
1469
+ lines.push(...splitLongWord(word, width));
1470
+ return "";
1471
+ }, wrapLine = (line, width) => {
1472
+ if (line.length === 0)
1473
+ return [""];
1474
+ if (line.length <= width)
1475
+ return [line];
1476
+ const lines = [];
1477
+ let current = "";
1478
+ for (const word of line.split(/\s+/)) {
1479
+ current = appendWrappedWord(lines, current, word, width);
1480
+ }
1481
+ if (current.length > 0)
1482
+ lines.push(current);
1483
+ return lines;
1484
+ }, ANSI_ESCAPE_PREFIX = "\x1B[", colors, ESCAPE = "\x1B", appendRightEdge = (value, width, marker) => {
1485
+ if (width <= 0) {
1486
+ return "";
1487
+ }
1488
+ return `${padLine(value, Math.max(0, width - 1))}${marker}`;
1489
+ }, formatTimestamp2 = () => new Date().toLocaleTimeString([], {
1490
+ hour: "numeric",
1491
+ hour12: true,
1492
+ minute: "2-digit",
1493
+ second: "2-digit"
1494
+ }), isPartialEscapeSequence = (value) => {
1495
+ if (!value.startsWith(ANSI_ESCAPE_PREFIX)) {
1496
+ return false;
1497
+ }
1498
+ return Array.from(value.slice(ANSI_ESCAPE_PREFIX.length)).every((char) => char >= "0" && char <= "9");
1499
+ }, openTtyStream2 = () => {
1500
+ const fromStdin = trySetRawMode2();
1501
+ if (fromStdin) {
1502
+ return fromStdin;
1503
+ }
1504
+ try {
1505
+ const ttyStream = new ReadStream2(openSync2("/dev/tty", "r"));
1506
+ ttyStream.setRawMode(true);
1507
+ return ttyStream;
1508
+ } catch {
1509
+ return null;
1510
+ }
1511
+ }, padLine = (value, width) => {
1512
+ const plainLength = visibleLength(value);
1513
+ if (plainLength >= width) {
1514
+ return value;
1515
+ }
1516
+ return `${value}${" ".repeat(width - plainLength)}`;
1517
+ }, stripAnsi = (value) => value.replace(ANSI_REGEX, ""), truncateText = (value, width) => {
1518
+ if (width <= 0) {
1519
+ return "";
1520
+ }
1521
+ if (value.length <= width) {
1522
+ return value;
1523
+ }
1524
+ if (width <= 1) {
1525
+ return value.slice(0, width);
1526
+ }
1527
+ return `${value.slice(0, width - 1)}\u2026`;
1528
+ }, visibleLength = (value) => value.replace(ANSI_REGEX, "").length, wrapText = (value, width) => {
1529
+ if (width <= 0) {
1530
+ return [""];
1531
+ }
1532
+ const lines = value.split(`
1533
+ `).flatMap((rawLine) => wrapLine(rawLine.trimEnd(), width));
1534
+ return lines.length > 0 ? lines : [""];
1535
+ };
1536
+ var init_tuiPrimitives = __esm(() => {
1537
+ init_constants();
1538
+ ANSI_REGEX = new RegExp(`${String.fromCharCode(ANSI_ESCAPE_CODE)}\\[[0-?]*[ -/]*[@-~]`, "g");
1539
+ colors = {
1540
+ bold: "\x1B[1m",
1541
+ cyan: "\x1B[36m",
1542
+ dim: "\x1B[2m",
1543
+ green: "\x1B[32m",
1544
+ red: "\x1B[31m",
1545
+ reset: "\x1B[0m",
1546
+ yellow: "\x1B[33m"
1547
+ };
1548
+ });
1549
+
1550
+ // src/cli/scripts/build.ts
1551
+ var exports_build = {};
1552
+ __export(exports_build, {
1553
+ build: () => build
1554
+ });
1555
+ import { resolve as resolve9 } from "path";
1556
+ var cliTag3 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`, tryImportBuild2 = async (candidate) => {
1557
+ try {
1558
+ const mod = await import(candidate);
1559
+ const buildFn = mod.build;
1560
+ return buildFn;
1561
+ } catch {
1562
+ return null;
1563
+ }
1564
+ }, resolveBuildModule2 = async (candidates) => {
1565
+ const [candidate, ...remaining] = candidates;
1566
+ if (!candidate) {
1567
+ return;
1568
+ }
1569
+ const mod = await tryImportBuild2(candidate);
1570
+ if (mod) {
1571
+ return mod;
1572
+ }
1573
+ return resolveBuildModule2(remaining);
1574
+ }, build = async (outdir, configPath2) => {
1575
+ const resolvedOutdir = resolve9(outdir ?? "build");
1576
+ const buildStart = performance.now();
1577
+ process.stdout.write(cliTag3("\x1B[36m", "Building assets"));
1578
+ const buildConfig = await loadConfig(configPath2);
1579
+ buildConfig.buildDirectory = resolvedOutdir;
1580
+ buildConfig.mode = "production";
1581
+ try {
1582
+ const buildApp = await resolveBuildModule2([
1583
+ resolve9(import.meta.dir, "..", "..", "core", "build"),
1584
+ resolve9(import.meta.dir, "..", "build")
1585
+ ]);
1586
+ if (!buildApp)
1587
+ throw new Error("Could not locate build module");
1588
+ await buildApp(buildConfig);
1589
+ } catch (err) {
1590
+ sendTelemetryEvent("build:error", {
1591
+ durationMs: Math.round(performance.now() - buildStart)
1592
+ });
1593
+ console.error(cliTag3("\x1B[31m", "Build step failed."));
1594
+ console.error(err);
1595
+ process.exit(1);
1596
+ }
1597
+ sendTelemetryEvent("build:complete", {
1598
+ durationMs: Math.round(performance.now() - buildStart)
1599
+ });
1600
+ console.log(` \x1B[2m(${getDurationString(performance.now() - buildStart)})\x1B[0m`);
1601
+ };
1602
+ var init_build = __esm(() => {
1603
+ init_getDurationString();
1604
+ init_loadConfig();
1605
+ init_startupBanner();
1606
+ init_telemetryEvent();
1607
+ });
1608
+
1609
+ // src/cli/instanceStatus.ts
1610
+ import { createConnection as createConnection2 } from "net";
1611
+ var displayHost = (host) => host === "0.0.0.0" || host === "::" ? "localhost" : host, probePort = (host, port) => {
1612
+ const { promise, resolve: resolve10 } = Promise.withResolvers();
1613
+ const socket = createConnection2({ host: displayHost(host), port });
1614
+ const timeout = setTimeout(() => {
1615
+ socket.destroy();
1616
+ resolve10(false);
1617
+ }, INSTANCE_PROBE_TIMEOUT_MS);
1618
+ socket.once("connect", () => {
1619
+ clearTimeout(timeout);
1620
+ socket.end();
1621
+ resolve10(true);
1622
+ });
1623
+ socket.once("error", () => {
1624
+ clearTimeout(timeout);
1625
+ socket.destroy();
1626
+ resolve10(false);
1627
+ });
1628
+ return promise;
1629
+ }, probeStatus = async (record) => {
1630
+ if (record.port === null) {
1631
+ return "starting";
1632
+ }
1633
+ const reachable = await probePort(record.host, record.port);
1634
+ return reachable ? "ready" : "starting";
1635
+ }, enrichInstances = async (records) => {
1636
+ const now = Date.now();
1637
+ const statuses = await Promise.all(records.map(probeStatus));
1638
+ return records.map((record, index) => ({
1639
+ ...record,
1640
+ status: statuses[index] ?? "starting",
1641
+ uptimeMs: Math.max(0, now - Date.parse(record.startedAt)),
1642
+ url: instanceUrl(record)
1643
+ }));
1644
+ }, instanceUrl = (record) => {
1645
+ if (record.port === null)
1646
+ return null;
1647
+ return `${record.https ? "https" : "http"}://${displayHost(record.host)}:${record.port}/`;
1648
+ };
1649
+ var init_instanceStatus = __esm(() => {
1650
+ init_constants();
1651
+ });
1652
+
1653
+ // src/cli/listTui.ts
1654
+ var exports_listTui = {};
1655
+ __export(exports_listTui, {
1656
+ runListTui: () => runListTui
1657
+ });
1658
+ import { spawn } from "child_process";
1659
+ import { closeSync, fstatSync, openSync as openSync3, readSync } from "fs";
1660
+ var TUI_HEADERS, STATUS_INDEX = 5, URL_INDEX = 6, helpLines2, statusLevelColor = (level) => {
1661
+ if (level === "error")
1662
+ return colors.red;
1663
+ if (level === "warn")
1664
+ return colors.yellow;
1665
+ if (level === "success")
1666
+ return colors.green;
1667
+ return colors.cyan;
1668
+ }, statusColor = (status2) => {
1669
+ if (status2 === "ready")
1670
+ return colors.green;
1671
+ if (status2 === "starting")
1672
+ return colors.yellow;
1673
+ return colors.dim;
1674
+ }, instanceRowCells = (instance) => [
1675
+ instance.name,
1676
+ instance.source,
1677
+ instance.port === null ? "-" : String(instance.port),
1678
+ String(instance.pid),
1679
+ getDurationString(instance.uptimeMs),
1680
+ instance.status,
1681
+ instance.url ?? "-"
1682
+ ], columnWidths = (allCells) => TUI_HEADERS.map((header, index) => Math.max(visibleLength(header), ...allCells.map((cells) => visibleLength(cells[index] ?? "")))), layoutWidths = (allCells, width) => {
1683
+ const natural = columnWidths(allCells);
1684
+ const gaps = (TUI_HEADERS.length - 1) * LIST_TUI_COLUMN_GAP;
1685
+ const available = width - LIST_TUI_MARKER_WIDTH - gaps;
1686
+ const fixed = natural.reduce((sum, value, index) => index === URL_INDEX ? sum : sum + value, 0);
1687
+ const urlWidth = Math.max(LIST_TUI_MIN_URL_WIDTH, available - fixed);
1688
+ return natural.map((value, index) => index === URL_INDEX ? Math.min(value, urlWidth) : value);
1689
+ }, openReadFd = (path) => {
1690
+ try {
1691
+ return openSync3(path, "r");
1692
+ } catch {
1693
+ return null;
1694
+ }
1695
+ }, readLogTail = (path) => {
1696
+ if (!path)
1697
+ return [];
1698
+ const descriptor = openReadFd(path);
1699
+ if (descriptor === null)
1700
+ return [];
1701
+ try {
1702
+ const { size } = fstatSync(descriptor);
1703
+ const start2 = Math.max(0, size - LIST_LOG_TAIL_MAX_BYTES);
1704
+ const length = size - start2;
1705
+ const buffer = Buffer.alloc(length);
1706
+ readSync(descriptor, buffer, 0, length, start2);
1707
+ return buffer.toString("utf-8").split(`
1708
+ `).filter((line) => line.trim().length > 0);
1709
+ } finally {
1710
+ closeSync(descriptor);
1711
+ }
1712
+ }, driveListTui = async (terminal) => {
1713
+ const { promise, resolve: resolveExit } = Promise.withResolvers();
1714
+ let instances = [];
1715
+ let selectedIndex = 0;
1716
+ let mode = "list";
1717
+ let helpVisible = false;
1718
+ let portBuffer = "";
1719
+ let statusMessage = null;
1720
+ let statusTimer = null;
1721
+ let renderTimer = null;
1722
+ let refreshTimer = null;
1723
+ let escapeBuffer = "";
1724
+ let escapeTimer = null;
1725
+ let disposed = false;
1726
+ let logScrollOffset = 0;
1727
+ let lastLogLineCount = 0;
1728
+ let lastLogViewportHeight = 0;
1729
+ const selectedInstance = () => instances[selectedIndex];
1730
+ const scheduleRender = () => {
1731
+ if (disposed || renderTimer)
1732
+ return;
1733
+ renderTimer = setTimeout(() => {
1734
+ renderTimer = null;
1735
+ render();
1736
+ }, LIST_TUI_RENDER_DEBOUNCE_MS);
1737
+ };
1738
+ const setStatus = (text, level) => {
1739
+ statusMessage = { level, text };
1740
+ if (statusTimer)
1741
+ clearTimeout(statusTimer);
1742
+ statusTimer = setTimeout(() => {
1743
+ statusMessage = null;
1744
+ scheduleRender();
1745
+ }, LIST_TUI_STATUS_MESSAGE_TIMEOUT_MS);
1746
+ scheduleRender();
1747
+ };
1748
+ const refresh = async () => {
1749
+ const previousPid = selectedInstance()?.pid;
1750
+ instances = await enrichInstances(listLiveInstances());
1751
+ const foundIndex = previousPid === undefined ? 0 : instances.findIndex((instance) => instance.pid === previousPid);
1752
+ selectedIndex = foundIndex >= 0 ? foundIndex : Math.min(selectedIndex, Math.max(0, instances.length - 1));
1753
+ scheduleRender();
1754
+ };
1755
+ const signalPid = (pid, signal) => {
1756
+ try {
1757
+ process.kill(-pid, signal);
1758
+ return;
1759
+ } catch {}
1760
+ try {
1761
+ process.kill(pid, signal);
1762
+ } catch {}
1763
+ };
1764
+ const stopSelected = () => {
1765
+ const instance = selectedInstance();
1766
+ if (!instance)
1767
+ return;
1768
+ signalPid(instance.controllerPid, "SIGTERM");
1769
+ const message = instance.source === "workspace" ? `Stopping ${instance.name}'s workspace (pid ${instance.controllerPid}) \u2014 all its services` : `Stopped ${instance.name} (pid ${instance.controllerPid})`;
1770
+ setStatus(message, "success");
1771
+ refresh();
1772
+ };
1773
+ const restartSelected = () => {
1774
+ const instance = selectedInstance();
1775
+ if (!instance)
1776
+ return;
1777
+ if (instance.source === "workspace") {
1778
+ setStatus(`${instance.name} is managed by its workspace \u2014 restart the workspace itself.`, "warn");
1779
+ return;
1780
+ }
1781
+ const [command, ...commandArgs] = instance.command;
1782
+ if (!command) {
1783
+ setStatus(`Cannot restart ${instance.name}: no launch command recorded.`, "warn");
1784
+ return;
1785
+ }
1786
+ signalPid(instance.controllerPid, "SIGTERM");
1787
+ const child = spawn(command, commandArgs, {
1788
+ cwd: instance.cwd,
1789
+ detached: true,
1790
+ stdio: "ignore"
1791
+ });
1792
+ child.unref();
1793
+ setStatus(`Restarting ${instance.name}\u2026`, "info");
1794
+ refresh();
1795
+ };
1796
+ const openSelected = () => {
1797
+ const instance = selectedInstance();
1798
+ if (!instance)
1799
+ return;
1800
+ if (!instance.url) {
1801
+ setStatus(`${instance.name} has no URL yet.`, "warn");
1802
+ return;
1803
+ }
1804
+ openUrlInBrowser(instance.url, (message) => setStatus(message, "warn"));
1805
+ setStatus(`Opening ${instance.url}`, "info");
1806
+ };
1807
+ const stopAll = () => {
1808
+ const count = instances.length;
1809
+ instances.forEach((instance) => signalPid(instance.pid, "SIGTERM"));
1810
+ setStatus(`Stopped ${count} server${count === 1 ? "" : "s"}.`, "success");
1811
+ refresh();
1812
+ };
1813
+ const freePort = (value) => {
1814
+ const port = Number(value);
1815
+ if (!Number.isInteger(port) || port <= 0) {
1816
+ setStatus(`Invalid port: ${value}`, "error");
1817
+ return;
1818
+ }
1819
+ let killed = false;
1820
+ killStaleProcesses(port, (message) => {
1821
+ killed = true;
1822
+ setStatus(message, "warn");
1823
+ });
1824
+ if (!killed)
1825
+ setStatus(`Nothing is listening on port ${port}.`, "info");
1826
+ refresh();
1827
+ };
1828
+ const moveSelection = (direction) => {
1829
+ if (instances.length === 0)
1830
+ return;
1831
+ const delta = direction === "up" ? UNFOUND_INDEX : 1;
1832
+ selectedIndex = Math.max(0, Math.min(instances.length - 1, selectedIndex + delta));
1833
+ logScrollOffset = 0;
1834
+ scheduleRender();
1835
+ };
1836
+ const scrollLogs = (direction) => {
1837
+ const maxOffset = Math.max(0, lastLogLineCount - lastLogViewportHeight);
1838
+ const pageSize = Math.max(1, lastLogViewportHeight - 1);
1839
+ if (direction === "up") {
1840
+ logScrollOffset = Math.min(maxOffset, logScrollOffset + 1);
1841
+ } else if (direction === "down") {
1842
+ logScrollOffset = Math.max(0, logScrollOffset - 1);
1843
+ } else if (direction === "pageUp") {
1844
+ logScrollOffset = Math.min(maxOffset, logScrollOffset + pageSize);
1845
+ } else {
1846
+ logScrollOffset = Math.max(0, logScrollOffset - pageSize);
1847
+ }
1848
+ scheduleRender();
1849
+ };
1850
+ const dispose = () => {
1851
+ if (disposed)
1852
+ return;
1853
+ disposed = true;
1854
+ if (renderTimer)
1855
+ clearTimeout(renderTimer);
1856
+ if (statusTimer)
1857
+ clearTimeout(statusTimer);
1858
+ if (escapeTimer)
1859
+ clearTimeout(escapeTimer);
1860
+ if (refreshTimer)
1861
+ clearInterval(refreshTimer);
1862
+ process.stdout.off("resize", onResize);
1863
+ terminal.off("data", onData);
1864
+ if (terminal.setRawMode)
1865
+ terminal.setRawMode(false);
1866
+ if (terminal !== process.stdin)
1867
+ terminal.destroy();
1868
+ process.stdout.write("\x1B[?25h\x1B[?1049l");
1869
+ };
1870
+ const quit = () => {
1871
+ dispose();
1872
+ resolveExit();
1873
+ };
1874
+ const listActions = new Map([
1875
+ ["f", () => enterPortMode()],
1876
+ ["h", () => toggleHelp()],
1877
+ ["j", () => moveSelection("down")],
1878
+ ["k", () => moveSelection("up")],
1879
+ ["o", () => openSelected()],
1880
+ ["q", () => quit()],
1881
+ ["r", () => restartSelected()],
1882
+ ["s", () => stopSelected()],
1883
+ ["x", () => enterConfirmMode()],
1884
+ ["?", () => toggleHelp()]
1885
+ ]);
1886
+ const toggleHelp = () => {
1887
+ helpVisible = !helpVisible;
1888
+ scheduleRender();
1889
+ };
1890
+ const enterPortMode = () => {
1891
+ mode = "port";
1892
+ portBuffer = "";
1893
+ scheduleRender();
1894
+ };
1895
+ const enterConfirmMode = () => {
1896
+ if (instances.length === 0)
1897
+ return;
1898
+ mode = "confirm";
1899
+ scheduleRender();
1900
+ };
1901
+ const handleListChar = (char) => {
1902
+ const action = listActions.get(char.toLowerCase());
1903
+ if (action)
1904
+ action();
1905
+ };
1906
+ const handlePortChar = (char) => {
1907
+ if (char === "\r" || char === `
1908
+ `) {
1909
+ const value = portBuffer;
1910
+ mode = "list";
1911
+ portBuffer = "";
1912
+ freePort(value);
1913
+ return;
1914
+ }
1915
+ if (char === "\x7F" || char === "\b") {
1916
+ portBuffer = portBuffer.slice(0, EXCLUDE_LAST_OFFSET);
1917
+ scheduleRender();
1918
+ return;
1919
+ }
1920
+ if (char >= "0" && char <= "9") {
1921
+ portBuffer += char;
1922
+ scheduleRender();
1923
+ }
1924
+ };
1925
+ const handleConfirmChar = (char) => {
1926
+ mode = "list";
1927
+ if (char.toLowerCase() === "y") {
1928
+ stopAll();
1929
+ return;
1930
+ }
1931
+ scheduleRender();
1932
+ };
1933
+ const clearEscapeTimer = () => {
1934
+ if (!escapeTimer)
1935
+ return;
1936
+ clearTimeout(escapeTimer);
1937
+ escapeTimer = null;
1938
+ };
1939
+ const onBareEscape = () => {
1940
+ if (helpVisible) {
1941
+ helpVisible = false;
1942
+ } else if (mode !== "list") {
1943
+ mode = "list";
1944
+ portBuffer = "";
1945
+ }
1946
+ scheduleRender();
1947
+ };
1948
+ const armEscapeTimer = () => {
1949
+ clearEscapeTimer();
1950
+ escapeTimer = setTimeout(() => {
1951
+ escapeTimer = null;
1952
+ escapeBuffer = "";
1953
+ onBareEscape();
1954
+ }, LIST_TUI_ESCAPE_SEQUENCE_TIMEOUT_MS);
1955
+ };
1956
+ const resetEscape = () => {
1957
+ clearEscapeTimer();
1958
+ escapeBuffer = "";
1959
+ };
1960
+ const handleEscapeSequence = (char) => {
1961
+ escapeBuffer += char;
1962
+ if (escapeBuffer === `${ESCAPE}[`) {
1963
+ armEscapeTimer();
1964
+ return;
1965
+ }
1966
+ if (escapeBuffer === `${ESCAPE}[A`) {
1967
+ resetEscape();
1968
+ moveSelection("up");
1969
+ return;
1970
+ }
1971
+ if (escapeBuffer === `${ESCAPE}[B`) {
1972
+ resetEscape();
1973
+ moveSelection("down");
1974
+ return;
1975
+ }
1976
+ if (escapeBuffer === `${ESCAPE}[5~`) {
1977
+ resetEscape();
1978
+ scrollLogs("pageUp");
1979
+ return;
1980
+ }
1981
+ if (escapeBuffer === `${ESCAPE}[6~`) {
1982
+ resetEscape();
1983
+ scrollLogs("pageDown");
1984
+ return;
1985
+ }
1986
+ if (isPartialEscapeSequence(escapeBuffer)) {
1987
+ armEscapeTimer();
1988
+ return;
1989
+ }
1990
+ resetEscape();
1991
+ onBareEscape();
1992
+ };
1993
+ const handleChar = (char) => {
1994
+ if (char === "\x03") {
1995
+ quit();
1996
+ return;
1997
+ }
1998
+ if (escapeBuffer) {
1999
+ handleEscapeSequence(char);
2000
+ return;
2001
+ }
2002
+ if (char === ESCAPE) {
2003
+ escapeBuffer = ESCAPE;
2004
+ armEscapeTimer();
2005
+ return;
2006
+ }
2007
+ if (mode === "port") {
2008
+ handlePortChar(char);
2009
+ return;
2010
+ }
2011
+ if (mode === "confirm") {
2012
+ handleConfirmChar(char);
2013
+ return;
2014
+ }
2015
+ handleListChar(char);
2016
+ };
2017
+ const onData = (chunk) => {
2018
+ for (const char of chunk.toString()) {
2019
+ handleChar(char);
2020
+ }
2021
+ };
2022
+ const onResize = () => {
2023
+ scheduleRender();
2024
+ };
2025
+ const titleLine = (width) => {
2026
+ const label = `${instances.length} live`;
2027
+ const left = `${colors.cyan}${colors.bold}ABSOLUTEJS${colors.reset} ${colors.dim}running servers${colors.reset} ${colors.bold}${label}${colors.reset}`;
2028
+ const right = `${colors.dim}${formatTimestamp2()}${colors.reset}`;
2029
+ const gap = Math.max(1, width - visibleLength(left) - visibleLength(right));
2030
+ return `${left}${" ".repeat(gap)}${right}`;
2031
+ };
2032
+ const dividerLine = (width) => `${colors.dim}${"\u2500".repeat(Math.max(width, 1))}${colors.reset}`;
2033
+ const colorizeCell = (cell, index, status2, isSelected, widths) => {
2034
+ const padded = padLine(truncateText(cell, widths[index] ?? 0), widths[index] ?? 0);
2035
+ if (index === STATUS_INDEX) {
2036
+ return `${statusColor(status2)}${padded}${colors.reset}`;
2037
+ }
2038
+ if (index === 0 && isSelected) {
2039
+ return `${colors.cyan}${colors.bold}${padded}${colors.reset}`;
2040
+ }
2041
+ return padded;
2042
+ };
2043
+ const renderInstanceRow = (instance, widths, isSelected) => {
2044
+ const body = instanceRowCells(instance).map((cell, index) => colorizeCell(cell, index, instance.status, isSelected, widths)).join(" ".repeat(LIST_TUI_COLUMN_GAP));
2045
+ const marker = isSelected ? `${colors.cyan}\u276F${colors.reset}` : " ";
2046
+ return `${marker} ${body}`;
2047
+ };
2048
+ const pushInstanceRows = (rows, width) => {
2049
+ if (instances.length === 0) {
2050
+ rows.push(padLine(`${colors.dim}No servers running. Start one with \`absolute dev\`.${colors.reset}`, width));
1275
2051
  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
2052
  }
1284
- }));
1285
- }, rewriteVendorDirectories2;
1286
- var init_rewriteImports = __esm(() => {
1287
- init_rewriteImportsPlugin();
1288
- rewriteVendorDirectories2 = rewriteVendorDirectories;
2053
+ const allCells = instances.map(instanceRowCells);
2054
+ const widths = layoutWidths(allCells, width);
2055
+ const header = TUI_HEADERS.map((label, index) => padLine(label, widths[index] ?? 0)).join(" ".repeat(LIST_TUI_COLUMN_GAP));
2056
+ rows.push(padLine(` ${colors.dim}${header}${colors.reset}`, width));
2057
+ instances.forEach((instance, index) => {
2058
+ rows.push(padLine(renderInstanceRow(instance, widths, index === selectedIndex), width));
2059
+ });
2060
+ };
2061
+ const logContentLines = (width) => {
2062
+ if (helpVisible)
2063
+ return helpLines2;
2064
+ const instance = selectedInstance();
2065
+ if (!instance) {
2066
+ return [`${colors.dim}No server selected.${colors.reset}`];
2067
+ }
2068
+ const lines = readLogTail(instance.logFile);
2069
+ if (lines.length === 0) {
2070
+ return [`${colors.dim}No output yet.${colors.reset}`];
2071
+ }
2072
+ return lines.map((line) => truncateText(stripAnsi(line), Math.max(1, width - 1)));
2073
+ };
2074
+ const pushLogRows = (rows, width, logHeight) => {
2075
+ const contentLines = logContentLines(width);
2076
+ lastLogLineCount = contentLines.length;
2077
+ lastLogViewportHeight = logHeight;
2078
+ const end = helpVisible ? Math.min(contentLines.length, logHeight) : Math.max(0, contentLines.length - logScrollOffset);
2079
+ const start2 = helpVisible ? 0 : Math.max(0, end - logHeight);
2080
+ const visible = contentLines.slice(start2, end);
2081
+ visible.forEach((line) => rows.push(padLine(line, width)));
2082
+ for (let index = visible.length;index < logHeight; index++) {
2083
+ rows.push(" ".repeat(width));
2084
+ }
2085
+ };
2086
+ const footerLine = (width) => {
2087
+ if (helpVisible) {
2088
+ return padLine(`${colors.dim}esc or ? closes help${colors.reset}`, width);
2089
+ }
2090
+ if (mode === "port") {
2091
+ return padLine(`${colors.yellow}free port:${colors.reset} ${portBuffer}\u258C ${colors.dim}enter to kill \xB7 esc to cancel${colors.reset}`, width);
2092
+ }
2093
+ if (mode === "confirm") {
2094
+ return padLine(`${colors.yellow}Stop ALL ${instances.length} servers? ${colors.reset}${colors.bold}y${colors.reset}${colors.dim}/N${colors.reset}`, width);
2095
+ }
2096
+ 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";
2097
+ return padLine(`${colors.dim}${truncateText(hint, width)}${colors.reset}`, width);
2098
+ };
2099
+ const statusLine = (width) => {
2100
+ if (!statusMessage) {
2101
+ return padLine(`${colors.dim}live \xB7 refreshing every ${LIST_WATCH_REFRESH_MS}ms${colors.reset}`, width);
2102
+ }
2103
+ return padLine(`${statusLevelColor(statusMessage.level)}${statusMessage.text}${colors.reset}`, width);
2104
+ };
2105
+ const render = () => {
2106
+ if (disposed)
2107
+ return;
2108
+ const width = process.stdout.columns ?? LIST_TUI_DEFAULT_WIDTH;
2109
+ const height = process.stdout.rows ?? LIST_TUI_DEFAULT_HEIGHT;
2110
+ const rows = [];
2111
+ rows.push(padLine(titleLine(width), width));
2112
+ rows.push(dividerLine(width));
2113
+ pushInstanceRows(rows, width);
2114
+ rows.push(dividerLine(width));
2115
+ const instance = selectedInstance();
2116
+ const logTitle = helpVisible || !instance ? "logs" : `logs \xB7 ${instance.name}${instance.frameworks.length > 0 ? ` \xB7 ${instance.frameworks.join(", ")}` : ""}`;
2117
+ rows.push(padLine(`${colors.bold}${logTitle}${colors.reset}`, width));
2118
+ const fixedHeight = rows.length + LIST_TUI_FOOTER_LINE_COUNT + 1;
2119
+ const logHeight = Math.max(height - fixedHeight, LIST_TUI_MIN_LOG_HEIGHT);
2120
+ pushLogRows(rows, width, logHeight);
2121
+ rows.push(dividerLine(width));
2122
+ rows.push(statusLine(width));
2123
+ rows.push(footerLine(width));
2124
+ const screen = rows.slice(0, height).map((line) => `\x1B[2K${line}`).join(`
2125
+ `);
2126
+ process.stdout.write(`\x1B[H${screen}\x1B[?25l`);
2127
+ };
2128
+ process.on("SIGINT", quit);
2129
+ process.on("SIGTERM", quit);
2130
+ process.stdout.write("\x1B[?1049h\x1B[2J\x1B[H\x1B[?25l");
2131
+ terminal.resume();
2132
+ terminal.on("data", onData);
2133
+ process.stdout.on("resize", onResize);
2134
+ refreshTimer = setInterval(() => {
2135
+ refresh();
2136
+ }, LIST_WATCH_REFRESH_MS);
2137
+ await refresh();
2138
+ render();
2139
+ await promise;
2140
+ }, runListTui = async () => {
2141
+ const input = openTtyStream2();
2142
+ if (!input) {
2143
+ process.stdout.write("Interactive ls requires a TTY. Run `absolute ls` for a snapshot.\n");
2144
+ return;
2145
+ }
2146
+ await driveListTui(input);
2147
+ };
2148
+ var init_listTui = __esm(() => {
2149
+ init_constants();
2150
+ init_getDurationString();
2151
+ init_instanceRegistry();
2152
+ init_instanceStatus();
2153
+ init_tuiPrimitives();
2154
+ init_utils();
2155
+ TUI_HEADERS = [
2156
+ "NAME",
2157
+ "SOURCE",
2158
+ "PORT",
2159
+ "PID",
2160
+ "UPTIME",
2161
+ "STATUS",
2162
+ "URL"
2163
+ ];
2164
+ helpLines2 = [
2165
+ "Hotkeys",
2166
+ " \u2191/\u2193 or j/k Select a server",
2167
+ " s Stop the selected server",
2168
+ " r Restart the selected server",
2169
+ " o Open the selected server in the browser",
2170
+ " f Free a port (kill whatever is listening on it)",
2171
+ " x Stop every listed server",
2172
+ " PgUp/PgDn Scroll the log pane",
2173
+ " ? or h Toggle this help",
2174
+ " q Quit (servers keep running)"
2175
+ ];
1289
2176
  });
1290
2177
 
1291
- // src/cli/scripts/build.ts
1292
- var exports_build = {};
1293
- __export(exports_build, {
1294
- build: () => build
2178
+ // src/cli/scripts/list.ts
2179
+ var exports_list = {};
2180
+ __export(exports_list, {
2181
+ runList: () => runList
1295
2182
  });
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) {
2183
+ var TABLE_HEADERS, statusColor2 = (status2) => {
2184
+ if (status2 === "ready")
2185
+ return colors.green;
2186
+ if (status2 === "starting")
2187
+ return colors.yellow;
2188
+ return colors.dim;
2189
+ }, instanceCells = (instance) => [
2190
+ instance.name,
2191
+ instance.source,
2192
+ instance.port === null ? "-" : String(instance.port),
2193
+ String(instance.pid),
2194
+ getDurationString(instance.uptimeMs),
2195
+ `${statusColor2(instance.status)}${instance.status}${colors.reset}`,
2196
+ instance.url ?? "-"
2197
+ ], 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) => {
2198
+ if (instances.length === 0) {
2199
+ process.stdout.write(`${colors.dim}No AbsoluteJS servers are running. Start one with \`absolute dev\`.${colors.reset}
2200
+ `);
1308
2201
  return;
1309
2202
  }
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);
2203
+ const rows = instances.map(instanceCells);
2204
+ const widths = columnWidths2(rows);
2205
+ process.stdout.write(`${colors.dim}${renderRow(TABLE_HEADERS, widths)}${colors.reset}
2206
+ `);
2207
+ for (const cells of rows) {
2208
+ process.stdout.write(`${renderRow(cells, widths)}
2209
+ `);
1337
2210
  }
1338
- sendTelemetryEvent("build:complete", {
1339
- durationMs: Math.round(performance.now() - buildStart)
2211
+ }, runList = async (args) => {
2212
+ process.stdout.on("error", (error) => {
2213
+ if (error instanceof Error && "code" in error && error.code === "EPIPE") {
2214
+ process.exit(0);
2215
+ }
1340
2216
  });
1341
- console.log(` \x1B[2m(${getDurationString(performance.now() - buildStart)})\x1B[0m`);
2217
+ if (args.includes("--watch") || args.includes("-w")) {
2218
+ const { runListTui: runListTui2 } = await Promise.resolve().then(() => (init_listTui(), exports_listTui));
2219
+ await runListTui2();
2220
+ return;
2221
+ }
2222
+ const instances = await enrichInstances(listLiveInstances());
2223
+ if (args.includes("--json")) {
2224
+ process.stdout.write(`${JSON.stringify(instances, null, 2)}
2225
+ `);
2226
+ return;
2227
+ }
2228
+ printInstanceTable(instances);
1342
2229
  };
1343
- var init_build = __esm(() => {
2230
+ var init_list = __esm(() => {
2231
+ init_constants();
1344
2232
  init_getDurationString();
1345
- init_loadConfig();
1346
- init_startupBanner();
1347
- init_telemetryEvent();
2233
+ init_instanceRegistry();
2234
+ init_instanceStatus();
2235
+ init_tuiPrimitives();
2236
+ TABLE_HEADERS = [
2237
+ "NAME",
2238
+ "SOURCE",
2239
+ "PORT",
2240
+ "PID",
2241
+ "UPTIME",
2242
+ "STATUS",
2243
+ "URL"
2244
+ ];
1348
2245
  });
1349
2246
 
1350
2247
  // 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";
2248
+ import { copyFileSync as copyFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, statSync } from "fs";
2249
+ import { basename as basename3, dirname as dirname3, join as join10, resolve as resolve10 } from "path";
1353
2250
  var createExternalAssetPlugin = (outDir, userSourceRoots = []) => ({
1354
2251
  name: "absolute-external-asset",
1355
2252
  setup(bld) {
@@ -1374,10 +2271,10 @@ var createExternalAssetPlugin = (outDir, userSourceRoots = []) => ({
1374
2271
  continue;
1375
2272
  if (!statSync(assetPath).isFile())
1376
2273
  continue;
1377
- const targetPath = join9(outDir, basename2(assetPath));
2274
+ const targetPath = join10(outDir, basename3(assetPath));
1378
2275
  if (existsSync10(targetPath))
1379
2276
  continue;
1380
- mkdirSync6(dirname3(targetPath), { recursive: true });
2277
+ mkdirSync7(dirname3(targetPath), { recursive: true });
1381
2278
  copyFileSync2(assetPath, targetPath);
1382
2279
  }
1383
2280
  return;
@@ -1396,15 +2293,15 @@ var {env: env3 } = globalThis.Bun;
1396
2293
  import {
1397
2294
  cpSync,
1398
2295
  existsSync as existsSync11,
1399
- mkdirSync as mkdirSync7,
1400
- readdirSync as readdirSync2,
1401
- readFileSync as readFileSync12,
2296
+ mkdirSync as mkdirSync8,
2297
+ readdirSync as readdirSync3,
2298
+ readFileSync as readFileSync13,
1402
2299
  rmSync as rmSync4,
1403
2300
  statSync as statSync2,
1404
- unlinkSync as unlinkSync3,
1405
- writeFileSync as writeFileSync4
2301
+ unlinkSync as unlinkSync4,
2302
+ writeFileSync as writeFileSync5
1406
2303
  } from "fs";
1407
- import { basename as basename3, dirname as dirname4, join as join10, relative, resolve as resolve11 } from "path";
2304
+ import { basename as basename4, dirname as dirname4, join as join11, relative, resolve as resolve11 } from "path";
1408
2305
  var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`, compileBanner = (version2) => {
1409
2306
  const resolvedVersion = version2 || "unknown";
1410
2307
  console.log("");
@@ -1412,14 +2309,14 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1412
2309
  console.log("");
1413
2310
  }, collectFiles2 = (dir) => {
1414
2311
  const result = [];
1415
- let pending = readdirSync2(dir, { withFileTypes: true });
2312
+ let pending = readdirSync3(dir, { withFileTypes: true });
1416
2313
  while (pending.length > 0) {
1417
2314
  const entry = pending.pop();
1418
2315
  if (!entry)
1419
2316
  continue;
1420
- const fullPath = join10(entry.parentPath, entry.name);
2317
+ const fullPath = join11(entry.parentPath, entry.name);
1421
2318
  if (entry.isDirectory())
1422
- pending = pending.concat(readdirSync2(fullPath, { withFileTypes: true }));
2319
+ pending = pending.concat(readdirSync3(fullPath, { withFileTypes: true }));
1423
2320
  else
1424
2321
  result.push(fullPath);
1425
2322
  }
@@ -1432,16 +2329,16 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1432
2329
  return `./${parts.join("/")}`;
1433
2330
  }, collectProjectSourceFiles = (dir) => {
1434
2331
  const result = [];
1435
- let pending = readdirSync2(dir, { withFileTypes: true });
2332
+ let pending = readdirSync3(dir, { withFileTypes: true });
1436
2333
  while (pending.length > 0) {
1437
2334
  const entry = pending.pop();
1438
2335
  if (!entry)
1439
2336
  continue;
1440
- const fullPath = join10(entry.parentPath, entry.name);
2337
+ const fullPath = join11(entry.parentPath, entry.name);
1441
2338
  if (entry.isDirectory()) {
1442
2339
  if (SERVER_RUNTIME_SCAN_SKIP_DIRS.has(entry.name))
1443
2340
  continue;
1444
- pending = pending.concat(readdirSync2(fullPath, { withFileTypes: true }));
2341
+ pending = pending.concat(readdirSync3(fullPath, { withFileTypes: true }));
1445
2342
  } else if (hasSourceExtension(fullPath)) {
1446
2343
  result.push(fullPath);
1447
2344
  }
@@ -1460,11 +2357,11 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1460
2357
  if (copied.has(assetTarget))
1461
2358
  return;
1462
2359
  copied.add(assetTarget);
1463
- mkdirSync7(dirname4(assetTarget), { recursive: true });
2360
+ mkdirSync8(dirname4(assetTarget), { recursive: true });
1464
2361
  cpSync(assetSource, assetTarget, { force: true });
1465
2362
  };
1466
2363
  for (const filePath of collectProjectSourceFiles(process.cwd())) {
1467
- const source = readFileSync12(filePath, "utf-8");
2364
+ const source = readFileSync13(filePath, "utf-8");
1468
2365
  SERVER_RUNTIME_ASSET_RE.lastIndex = 0;
1469
2366
  let match;
1470
2367
  while ((match = SERVER_RUNTIME_ASSET_RE.exec(source)) !== null) {
@@ -1493,7 +2390,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1493
2390
  }
1494
2391
  }, readPackageVersion4 = (candidate) => {
1495
2392
  try {
1496
- const pkg = JSON.parse(readFileSync12(candidate, "utf-8"));
2393
+ const pkg = JSON.parse(readFileSync13(candidate, "utf-8"));
1497
2394
  if (pkg.name !== "@absolutejs/absolute")
1498
2395
  return null;
1499
2396
  const ver = pkg.version;
@@ -1552,7 +2449,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1552
2449
  return true;
1553
2450
  }, tryReadNodePackageJson = (packageDir) => {
1554
2451
  try {
1555
- return JSON.parse(readFileSync12(join10(packageDir, "package.json"), "utf-8"));
2452
+ return JSON.parse(readFileSync13(join11(packageDir, "package.json"), "utf-8"));
1556
2453
  } catch {
1557
2454
  return null;
1558
2455
  }
@@ -1564,7 +2461,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1564
2461
  if (!pkg)
1565
2462
  return;
1566
2463
  seen.add(specifier);
1567
- const destDir = join10(outdir, "node_modules", ...specifier.split("/"));
2464
+ const destDir = join11(outdir, "node_modules", ...specifier.split("/"));
1568
2465
  rmSync4(destDir, { force: true, recursive: true });
1569
2466
  cpSync(srcDir, destDir, {
1570
2467
  filter(source) {
@@ -1587,7 +2484,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1587
2484
  if (!buildConfig.angularDirectory)
1588
2485
  return;
1589
2486
  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}`) : [];
2487
+ const angularPackages = existsSync11(angularScopeDir) ? readdirSync3(angularScopeDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).filter((entry) => entry.name !== "compiler-cli").map((entry) => `@angular/${entry.name}`) : [];
1591
2488
  const roots = new Set([...angularPackages, "rxjs", "tslib", "typescript"]);
1592
2489
  const seen = new Set;
1593
2490
  for (const specifier of roots) {
@@ -1604,16 +2501,16 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1604
2501
  }
1605
2502
  copyAngularRuntimePackages(buildConfig, outdir);
1606
2503
  }, collectRuntimePackageSpecifiers = (distDir) => {
1607
- const nodeModulesDir = join10(distDir, "node_modules");
2504
+ const nodeModulesDir = join11(distDir, "node_modules");
1608
2505
  if (!existsSync11(nodeModulesDir))
1609
2506
  return [];
1610
2507
  const specifiers = [];
1611
- for (const entry of readdirSync2(nodeModulesDir, { withFileTypes: true })) {
2508
+ for (const entry of readdirSync3(nodeModulesDir, { withFileTypes: true })) {
1612
2509
  if (!entry.isDirectory())
1613
2510
  continue;
1614
2511
  if (entry.name.startsWith("@")) {
1615
- const scopeDir = join10(nodeModulesDir, entry.name);
1616
- for (const scopedEntry of readdirSync2(scopeDir, {
2512
+ const scopeDir = join11(nodeModulesDir, entry.name);
2513
+ for (const scopedEntry of readdirSync3(scopeDir, {
1617
2514
  withFileTypes: true
1618
2515
  })) {
1619
2516
  if (scopedEntry.isDirectory()) {
@@ -1644,18 +2541,18 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1644
2541
  const packageSpecifier = packageSpecifiers.find((root) => specifier === root || specifier.startsWith(`${root}/`));
1645
2542
  if (!packageSpecifier)
1646
2543
  return null;
1647
- const packageDir = join10(distDir, "node_modules", ...packageSpecifier.split("/"));
2544
+ const packageDir = join11(distDir, "node_modules", ...packageSpecifier.split("/"));
1648
2545
  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");
2546
+ const subPackageDir = subpath ? join11(packageDir, ...subpath.slice(1).split("/")) : null;
2547
+ const resolvedPackageDir = subPackageDir && existsSync11(join11(subPackageDir, "package.json")) ? subPackageDir : packageDir;
2548
+ const packageJsonPath = join11(resolvedPackageDir, "package.json");
1652
2549
  if (!existsSync11(packageJsonPath))
1653
2550
  return null;
1654
- const pkg = JSON.parse(readFileSync12(packageJsonPath, "utf-8"));
2551
+ const pkg = JSON.parse(readFileSync13(packageJsonPath, "utf-8"));
1655
2552
  const exportKey = resolvedPackageDir === subPackageDir ? "." : subpath ? `.${subpath}` : ".";
1656
2553
  const rootExport = pkg.exports?.[exportKey];
1657
2554
  const entry = pickExportEntry(rootExport) ?? (resolvedPackageDir === subPackageDir || !subpath ? pkg.module ?? pkg.main ?? "index.js" : `.${subpath}`);
1658
- return join10(resolvedPackageDir, entry);
2555
+ return join11(resolvedPackageDir, entry);
1659
2556
  }, 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
2557
  try {
1661
2558
  return statSync2(filePath).isFile();
@@ -1668,13 +2565,13 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1668
2565
  const candidates = [
1669
2566
  candidate,
1670
2567
  ...RUNTIME_JS_EXTENSIONS.map((extension) => `${candidate}${extension}`),
1671
- ...RUNTIME_JS_EXTENSIONS.map((extension) => join10(candidate, `index${extension}`))
2568
+ ...RUNTIME_JS_EXTENSIONS.map((extension) => join11(candidate, `index${extension}`))
1672
2569
  ];
1673
2570
  return candidates.find((filePath) => isRuntimeJsFile(filePath) && isFile(filePath)) ?? null;
1674
2571
  }, findContainingRuntimePackageDir = (filePath) => {
1675
2572
  let dir = dirname4(filePath);
1676
2573
  while (dir !== dirname4(dir)) {
1677
- if (isNodeModulesPath(dir) && existsSync11(join10(dir, "package.json"))) {
2574
+ if (isNodeModulesPath(dir) && existsSync11(join11(dir, "package.json"))) {
1678
2575
  return dir;
1679
2576
  }
1680
2577
  dir = dirname4(dir);
@@ -1690,7 +2587,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1690
2587
  const entry = pickExportEntry(pkg?.imports?.[specifier]);
1691
2588
  if (!entry)
1692
2589
  return null;
1693
- return join10(packageDir, entry);
2590
+ return join11(packageDir, entry);
1694
2591
  }, collectRuntimeRewriteRoots = (distDir) => collectFiles2(distDir).filter((filePath) => isRuntimeJsFile(filePath) && !isNodeModulesPath(filePath)), rewriteRuntimeModuleSpecifiers = (distDir) => {
1695
2592
  const packageSpecifiers = collectRuntimePackageSpecifiers(distDir);
1696
2593
  if (packageSpecifiers.length === 0)
@@ -1709,7 +2606,7 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1709
2606
  if (!filePath || seen.has(filePath))
1710
2607
  continue;
1711
2608
  seen.add(filePath);
1712
- const source = readFileSync12(filePath, "utf-8");
2609
+ const source = readFileSync13(filePath, "utf-8");
1713
2610
  const rewritten = source.replace(MODULE_SPECIFIER_RE, (match, prefix, quote, specifier) => {
1714
2611
  if (typeof specifier === "string" && specifier.startsWith(".")) {
1715
2612
  enqueue(resolveRuntimeJsFile(resolve11(dirname4(filePath), specifier)));
@@ -1727,12 +2624,12 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1727
2624
  return `${prefix}${quote}${ensureRelativeModuleSpecifier(filePath, target)}${quote}`;
1728
2625
  });
1729
2626
  if (rewritten !== source) {
1730
- writeFileSync4(filePath, rewritten);
2627
+ writeFileSync5(filePath, rewritten);
1731
2628
  }
1732
2629
  }
1733
2630
  }, generateEntrypoint = (distDir, serverEntry, prerenderMap, version2, buildConfig) => {
1734
2631
  const allFiles = collectFiles2(distDir);
1735
- const serverBundleName = `${basename3(serverEntry).replace(/\.[^.]+$/, "")}.js`;
2632
+ const serverBundleName = `${basename4(serverEntry).replace(/\.[^.]+$/, "")}.js`;
1736
2633
  const embeddedSkip = new Set(["_compile_entrypoint.ts"]);
1737
2634
  const assetSkip = new Set([
1738
2635
  serverBundleName,
@@ -1786,10 +2683,10 @@ var cliTag4 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[c
1786
2683
  ${imports.join(`
1787
2684
  `)}
1788
2685
 
1789
- import { existsSync } from "node:fs";
2686
+ import { existsSync, mkdirSync, unlinkSync } from "node:fs";
1790
2687
  import { mkdir } from "node:fs/promises";
1791
- import { dirname, join } from "node:path";
1792
- import { tmpdir } from "node:os";
2688
+ import { basename, dirname, join } from "node:path";
2689
+ import { homedir, tmpdir } from "node:os";
1793
2690
  import { createHash } from "node:crypto";
1794
2691
  import { readFileSync, writeFileSync } from "node:fs";
1795
2692
  import { pathToFileURL } from "node:url";
@@ -1986,6 +2883,49 @@ const server = Bun.serve({
1986
2883
  },
1987
2884
  });
1988
2885
 
2886
+ // Register in the global instance registry so 'absolute ls' can see this
2887
+ // compiled binary. Best-effort; never blocks startup. Dead entries are pruned
2888
+ // on read, so a hard kill that skips the exit handler is harmless.
2889
+ try {
2890
+ const absInstancesDir = join(homedir(), ".absolutejs", "instances");
2891
+ mkdirSync(absInstancesDir, { recursive: true });
2892
+ const absInstanceFile = join(absInstancesDir, process.pid + ".json");
2893
+ writeFileSync(
2894
+ absInstanceFile,
2895
+ JSON.stringify(
2896
+ {
2897
+ // Bun sets process.argv[0] to "bun" in a compiled binary;
2898
+ // process.execPath is the real standalone executable path.
2899
+ command: process.execPath ? [process.execPath] : [],
2900
+ configPath: null,
2901
+ controllerPid: process.pid,
2902
+ cwd: process.cwd(),
2903
+ frameworks: [],
2904
+ host: "localhost",
2905
+ https: false,
2906
+ logFile: null,
2907
+ name: basename(process.execPath || "compiled"),
2908
+ pid: process.pid,
2909
+ port: server.port,
2910
+ ppid: process.ppid,
2911
+ source: "compiled",
2912
+ startedAt: new Date().toISOString()
2913
+ },
2914
+ null,
2915
+ 2
2916
+ )
2917
+ );
2918
+ process.on("exit", () => {
2919
+ try {
2920
+ unlinkSync(absInstanceFile);
2921
+ } catch {
2922
+ /* already gone */
2923
+ }
2924
+ });
2925
+ } catch {
2926
+ /* registry is best-effort */
2927
+ }
2928
+
1989
2929
  const assetCount = Object.keys(ASSETS).length;
1990
2930
  const pageCount = Object.keys(PAGES).length;
1991
2931
  console.log(\`
@@ -2115,7 +3055,7 @@ console.log(\`
2115
3055
  }, compileUnlocked = async (serverEntry, resolvedOutdir, outfile, configPath2) => {
2116
3056
  const prerenderPort = Number(env3.COMPILE_PORT) || Number(env3.PORT) || DEFAULT_PORT + 1;
2117
3057
  killStaleProcesses(prerenderPort);
2118
- const entryName = basename3(serverEntry).replace(/\.[^.]+$/, "");
3058
+ const entryName = basename4(serverEntry).replace(/\.[^.]+$/, "");
2119
3059
  const resolvedOutfile = resolve11(outfile ?? "compiled-server");
2120
3060
  const absoluteVersion = resolvePackageVersion3([
2121
3061
  resolve11(import.meta.dir, "..", "..", "..", "package.json"),
@@ -2181,7 +3121,7 @@ console.log(\`
2181
3121
  }
2182
3122
  if (existsSync11(resolve11(resolvedOutdir, "angular", "vendor", "server"))) {
2183
3123
  const vendorDir = resolve11(resolvedOutdir, "angular", "vendor", "server");
2184
- const vendorEntries = readdirSync2(vendorDir).filter((f) => f.endsWith(".js"));
3124
+ const vendorEntries = readdirSync3(vendorDir).filter((f) => f.endsWith(".js"));
2185
3125
  const angularServerVendorPaths = {};
2186
3126
  for (const file of vendorEntries) {
2187
3127
  const stem = file.replace(/\.js$/, "");
@@ -2201,7 +3141,7 @@ console.log(\`
2201
3141
  copyServerRuntimeAssetReferences(resolvedOutdir);
2202
3142
  const prerenderStart = performance.now();
2203
3143
  process.stdout.write(cliTag4("\x1B[36m", "Pre-rendering pages"));
2204
- rmSync4(join10(resolvedOutdir, "_prerendered"), {
3144
+ rmSync4(join11(resolvedOutdir, "_prerendered"), {
2205
3145
  force: true,
2206
3146
  recursive: true
2207
3147
  });
@@ -2220,9 +3160,9 @@ console.log(\`
2220
3160
  const compileStart = performance.now();
2221
3161
  process.stdout.write(cliTag4("\x1B[36m", "Compiling standalone executable"));
2222
3162
  const entrypointCode = generateEntrypoint(resolvedOutdir, serverEntry, prerenderMap, absoluteVersion, buildConfig);
2223
- const entrypointPath = join10(resolvedOutdir, "_compile_entrypoint.ts");
3163
+ const entrypointPath = join11(resolvedOutdir, "_compile_entrypoint.ts");
2224
3164
  await Bun.write(entrypointPath, entrypointCode);
2225
- mkdirSync7(dirname4(resolvedOutfile), { recursive: true });
3165
+ mkdirSync8(dirname4(resolvedOutfile), { recursive: true });
2226
3166
  const result = await Bun.build({
2227
3167
  compile: { outfile: resolvedOutfile },
2228
3168
  define: { "process.env.NODE_ENV": '"production"' },
@@ -2244,13 +3184,13 @@ console.log(\`
2244
3184
  }
2245
3185
  console.log(` \x1B[2m(${getDurationString(performance.now() - compileStart)})\x1B[0m`);
2246
3186
  try {
2247
- unlinkSync3(entrypointPath);
3187
+ unlinkSync4(entrypointPath);
2248
3188
  } catch {}
2249
3189
  const BYTES_PER_MB = 1048576;
2250
3190
  const size = (Bun.file(resolvedOutfile).size / BYTES_PER_MB).toFixed(0);
2251
3191
  const totalDuration = getDurationString(performance.now() - totalStart);
2252
3192
  console.log(cliTag4("\x1B[32m", `Compiled to ${resolvedOutfile} (${size}MB) in ${totalDuration}`));
2253
- console.log(cliTag4("\x1B[2m", `Run with: ./${basename3(resolvedOutfile)}`));
3193
+ console.log(cliTag4("\x1B[2m", `Run with: ./${basename4(resolvedOutfile)}`));
2254
3194
  sendTelemetryEvent("compile:complete", {
2255
3195
  durationMs: Math.round(performance.now() - totalStart),
2256
3196
  entry: serverEntry,
@@ -2314,8 +3254,8 @@ var exports_typecheck = {};
2314
3254
  __export(exports_typecheck, {
2315
3255
  typecheck: () => typecheck
2316
3256
  });
2317
- import { resolve as resolve12, join as join11 } from "path";
2318
- import { existsSync as existsSync12, readFileSync as readFileSync13 } from "fs";
3257
+ import { resolve as resolve12, join as join12 } from "path";
3258
+ import { existsSync as existsSync12, readFileSync as readFileSync14 } from "fs";
2319
3259
  import { mkdir as mkdir2, writeFile } from "fs/promises";
2320
3260
  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
3261
  if (!existsSync12(resolveConfigPath(configPath2))) {
@@ -2394,7 +3334,7 @@ Found ${errorCount} error${suffix}.`;
2394
3334
  return candidates.find((candidate) => existsSync12(candidate)) ?? candidates[0];
2395
3335
  }, ABSOLUTE_TYPECHECK_FILES, readProjectTsconfig = () => {
2396
3336
  try {
2397
- return JSON.parse(readFileSync13(resolve12("tsconfig.json"), "utf-8"));
3337
+ return JSON.parse(readFileSync14(resolve12("tsconfig.json"), "utf-8"));
2398
3338
  } catch {
2399
3339
  return {};
2400
3340
  }
@@ -2422,7 +3362,7 @@ Found ${errorCount} error${suffix}.`;
2422
3362
  console.error("\x1B[31m\u2717\x1B[0m vue-tsc is required for Vue type checking. Install it: bun add -d vue-tsc");
2423
3363
  process.exit(1);
2424
3364
  }
2425
- const vueTsconfigPath = join11(cacheDir, "tsconfig.vue-check.json");
3365
+ const vueTsconfigPath = join12(cacheDir, "tsconfig.vue-check.json");
2426
3366
  return writeFile(vueTsconfigPath, JSON.stringify({
2427
3367
  compilerOptions: {
2428
3368
  rootDir: ".."
@@ -2437,7 +3377,7 @@ Found ${errorCount} error${suffix}.`;
2437
3377
  resolve12(vueTsconfigPath),
2438
3378
  "--incremental",
2439
3379
  "--tsBuildInfoFile",
2440
- join11(cacheDir, "vue-tsc.tsbuildinfo"),
3380
+ join12(cacheDir, "vue-tsc.tsbuildinfo"),
2441
3381
  "--pretty"
2442
3382
  ]));
2443
3383
  }, buildAngularCheck = async (cacheDir, angularDir) => {
@@ -2446,7 +3386,7 @@ Found ${errorCount} error${suffix}.`;
2446
3386
  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
3387
  process.exit(1);
2448
3388
  }
2449
- const angularTsconfigPath = join11(cacheDir, "tsconfig.angular-check.json");
3389
+ const angularTsconfigPath = join12(cacheDir, "tsconfig.angular-check.json");
2450
3390
  await writeFile(angularTsconfigPath, JSON.stringify({
2451
3391
  angularCompilerOptions: {
2452
3392
  strictTemplates: true
@@ -2466,7 +3406,7 @@ Found ${errorCount} error${suffix}.`;
2466
3406
  console.error("\x1B[31m\u2717\x1B[0m typescript is required for type checking. Install it: bun add -d typescript");
2467
3407
  process.exit(1);
2468
3408
  }
2469
- const tscConfigPath = join11(cacheDir, "tsconfig.typecheck.json");
3409
+ const tscConfigPath = join12(cacheDir, "tsconfig.typecheck.json");
2470
3410
  return writeFile(tscConfigPath, JSON.stringify({
2471
3411
  compilerOptions: {
2472
3412
  rootDir: ".."
@@ -2481,7 +3421,7 @@ Found ${errorCount} error${suffix}.`;
2481
3421
  resolve12(tscConfigPath),
2482
3422
  "--incremental",
2483
3423
  "--tsBuildInfoFile",
2484
- join11(cacheDir, "tsc.tsbuildinfo"),
3424
+ join12(cacheDir, "tsc.tsbuildinfo"),
2485
3425
  "--pretty"
2486
3426
  ]));
2487
3427
  }, buildSvelteCheck = async (cacheDir, svelteDir) => {
@@ -2490,7 +3430,7 @@ Found ${errorCount} error${suffix}.`;
2490
3430
  console.error("\x1B[31m\u2717\x1B[0m svelte-check is required for Svelte type checking. Install it: bun add -d svelte-check");
2491
3431
  process.exit(1);
2492
3432
  }
2493
- const svelteTsconfigPath = join11(cacheDir, "tsconfig.svelte-check.json");
3433
+ const svelteTsconfigPath = join12(cacheDir, "tsconfig.svelte-check.json");
2494
3434
  await writeFile(svelteTsconfigPath, JSON.stringify({
2495
3435
  extends: resolve12("tsconfig.json"),
2496
3436
  files: ABSOLUTE_TYPECHECK_FILES,
@@ -2567,7 +3507,7 @@ init_constants();
2567
3507
  init_startupBanner();
2568
3508
  var {$: $2, env } = globalThis.Bun;
2569
3509
  import { spawn as nodeSpawn } from "child_process";
2570
- import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
3510
+ import { createWriteStream, existsSync as existsSync5, readFileSync as readFileSync7 } from "fs";
2571
3511
  import { resolve as resolve3 } from "path";
2572
3512
 
2573
3513
  // src/cli/interactive.ts
@@ -2840,6 +3780,7 @@ var createInteractiveHandler = (actions) => {
2840
3780
  // src/cli/scripts/dev.ts
2841
3781
  init_telemetryEvent();
2842
3782
  init_buildDirectoryLock();
3783
+ init_instanceRegistry();
2843
3784
  init_loadConfig();
2844
3785
 
2845
3786
  // src/utils/resolveDevPort.ts
@@ -2882,6 +3823,7 @@ var resolveDevPort = async (requestedPort, options = {}) => {
2882
3823
  init_utils();
2883
3824
  var cliTag = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`;
2884
3825
  var DEFAULT_PORT_RANGE = 10;
3826
+ var ANSI_LOG_REGEX = new RegExp(`${String.fromCharCode(ANSI_ESCAPE_CODE)}\\[[0-?]*[ -/]*[@-~]`, "g");
2885
3827
  var confirmPrompt = (message, defaultYes = true) => {
2886
3828
  const { promise, resolve: resolvePrompt } = Promise.withResolvers();
2887
3829
  let selected = defaultYes;
@@ -2986,6 +3928,37 @@ var dev = async (serverEntry, configPath2) => {
2986
3928
  console.log(cliTag("\x1B[33m", `Port ${resolvedDev.port} is in use, trying another one... \u2192 http://${displayHost}:${port}/`));
2987
3929
  }
2988
3930
  updateLockMetadata(buildDirectory, { port });
3931
+ const instancePid = process.pid;
3932
+ const instanceLogFile = instanceLogPath(instancePid);
3933
+ const relaunchCommand = [
3934
+ process.execPath,
3935
+ process.argv[1] ?? "",
3936
+ "dev",
3937
+ serverEntry,
3938
+ ...configPath2 ? ["--config", configPath2] : []
3939
+ ].filter((part) => part.length > 0);
3940
+ registerInstance({
3941
+ command: relaunchCommand,
3942
+ configPath: configPath2 ?? null,
3943
+ controllerPid: instancePid,
3944
+ cwd: process.cwd(),
3945
+ frameworks: [],
3946
+ host: resolvedDev.host,
3947
+ https: httpsEnabled,
3948
+ logFile: instanceLogFile,
3949
+ name: resolveProjectName(process.cwd()),
3950
+ pid: instancePid,
3951
+ port,
3952
+ ppid: process.ppid,
3953
+ source: "dev",
3954
+ startedAt: new Date().toISOString()
3955
+ });
3956
+ const instanceLog = createWriteStream(instanceLogFile, { flags: "w" });
3957
+ const writeInstanceLog = (text) => {
3958
+ try {
3959
+ instanceLog.write(text.replace(ANSI_LOG_REGEX, ""));
3960
+ } catch {}
3961
+ };
2989
3962
  const usesDocker = existsSync5(resolve3(COMPOSE_PATH));
2990
3963
  const scripts = usesDocker ? await readDbScripts() : null;
2991
3964
  if (scripts)
@@ -3059,6 +4032,7 @@ var dev = async (serverEntry, configPath2) => {
3059
4032
  console.log(cliTag("\x1B[36m", `Port changed in config \u2014 switching to http://${displayHost}:${probe.port}/`));
3060
4033
  port = probe.port;
3061
4034
  updateLockMetadata(buildDirectory, { port });
4035
+ updateInstance(instancePid, { port });
3062
4036
  }
3063
4037
  resolvedDev = dev2;
3064
4038
  }
@@ -3069,7 +4043,7 @@ var dev = async (serverEntry, configPath2) => {
3069
4043
  for (const name of candidates) {
3070
4044
  let text;
3071
4045
  try {
3072
- text = readFileSync6(resolve3(process.cwd(), name), "utf8");
4046
+ text = readFileSync7(resolve3(process.cwd(), name), "utf8");
3073
4047
  } catch {
3074
4048
  continue;
3075
4049
  }
@@ -3099,6 +4073,7 @@ var dev = async (serverEntry, configPath2) => {
3099
4073
  env: {
3100
4074
  ...process.env,
3101
4075
  ...readDotenvFiles(),
4076
+ ABSOLUTE_INSTANCE_MANAGED: "1",
3102
4077
  FORCE_COLOR: "1",
3103
4078
  NODE_ENV: "development",
3104
4079
  ABSOLUTE_PORT: String(port),
@@ -3115,6 +4090,7 @@ var dev = async (serverEntry, configPath2) => {
3115
4090
  if (serverReady)
3116
4091
  interactive?.clearPrompt();
3117
4092
  dest.write(chunk);
4093
+ writeInstanceLog(chunk.toString());
3118
4094
  handleChunk(chunk);
3119
4095
  });
3120
4096
  };
@@ -3140,7 +4116,7 @@ var dev = async (serverEntry, configPath2) => {
3140
4116
  };
3141
4117
  try {
3142
4118
  const { watch } = await import("fs");
3143
- const { dirname: dirname3, join: join5 } = await import("path");
4119
+ const { dirname: dirname3, join: join6 } = await import("path");
3144
4120
  const absServerEntry = resolve3(serverEntry);
3145
4121
  const serverEntryDir = dirname3(absServerEntry);
3146
4122
  const ROOT_RESTART_DENY = new Set([
@@ -3173,13 +4149,13 @@ var dev = async (serverEntry, configPath2) => {
3173
4149
  if (now - last < 100)
3174
4150
  return;
3175
4151
  recentlyHandled.set(filename, now);
3176
- scheduleServerRestart(join5(serverEntryDir, filename));
4152
+ scheduleServerRestart(join6(serverEntryDir, filename));
3177
4153
  };
3178
4154
  const recoveryScan = async () => {
3179
4155
  let entries;
3180
4156
  try {
3181
- const { readdirSync } = await import("fs");
3182
- entries = readdirSync(serverEntryDir, { withFileTypes: true });
4157
+ const { readdirSync: readdirSync2 } = await import("fs");
4158
+ entries = readdirSync2(serverEntryDir, { withFileTypes: true });
3183
4159
  } catch {
3184
4160
  return;
3185
4161
  }
@@ -3192,7 +4168,7 @@ var dev = async (serverEntry, configPath2) => {
3192
4168
  continue;
3193
4169
  let st;
3194
4170
  try {
3195
- st = statSync(join5(serverEntryDir, entry.name));
4171
+ st = statSync(join6(serverEntryDir, entry.name));
3196
4172
  } catch {
3197
4173
  continue;
3198
4174
  }
@@ -3235,6 +4211,7 @@ var dev = async (serverEntry, configPath2) => {
3235
4211
  cfg.angularDirectory && "angular"
3236
4212
  ].filter((val) => Boolean(val));
3237
4213
  } catch {}
4214
+ updateInstance(instancePid, { frameworks });
3238
4215
  sendTelemetryEvent("dev:start", { entry: serverEntry, frameworks });
3239
4216
  const killChildTree = (signal) => {
3240
4217
  const childPid = serverProcess.pid;
@@ -3273,6 +4250,10 @@ var dev = async (serverEntry, configPath2) => {
3273
4250
  });
3274
4251
  if (scripts)
3275
4252
  await stopDatabase(scripts);
4253
+ try {
4254
+ instanceLog.end();
4255
+ } catch {}
4256
+ deregisterInstance(instancePid);
3276
4257
  process.exit(exitCode);
3277
4258
  };
3278
4259
  const restartServer = async () => {
@@ -3451,7 +4432,7 @@ var dev = async (serverEntry, configPath2) => {
3451
4432
  };
3452
4433
 
3453
4434
  // src/cli/scripts/eslint.ts
3454
- import { existsSync as existsSync6, readFileSync as readFileSync7, rmSync as rmSync2 } from "fs";
4435
+ import { existsSync as existsSync6, readFileSync as readFileSync8, rmSync as rmSync2 } from "fs";
3455
4436
  import { resolve as resolve4 } from "path";
3456
4437
  var DEFAULT_CACHE_LOCATION = ".absolutejs/eslint-cache";
3457
4438
  var getCacheLocation = () => process.env.ABSOLUTE_ESLINT_CACHE?.trim() || DEFAULT_CACHE_LOCATION;
@@ -3608,7 +4589,7 @@ var checkForMisplacedIgnores = () => {
3608
4589
  return;
3609
4590
  let source;
3610
4591
  try {
3611
- source = readFileSync7(configPath2, "utf-8");
4592
+ source = readFileSync8(configPath2, "utf-8");
3612
4593
  } catch {
3613
4594
  return;
3614
4595
  }
@@ -3697,7 +4678,7 @@ var eslint = async (args) => {
3697
4678
  init_constants();
3698
4679
  init_utils();
3699
4680
  import { execSync as execSync2 } from "child_process";
3700
- import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
4681
+ import { existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
3701
4682
  import { arch as arch2, cpus, platform as platform3, totalmem, version } from "os";
3702
4683
  import { resolve as resolve5 } from "path";
3703
4684
  var bold = (str) => `\x1B[1m${str}\x1B[0m`;
@@ -3719,7 +4700,7 @@ var getPackageVersion = (packageName) => {
3719
4700
  const pkgPath = __require.resolve(`${packageName}/package.json`, {
3720
4701
  paths: [process.cwd()]
3721
4702
  });
3722
- const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
4703
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
3723
4704
  const ver = pkg.version;
3724
4705
  return ver;
3725
4706
  } catch {
@@ -3741,7 +4722,7 @@ var getAbsoluteVersion = () => {
3741
4722
  return getPackageVersion("@absolutejs/absolute");
3742
4723
  };
3743
4724
  var readPackageVersion = (pkgPath) => {
3744
- const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
4725
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
3745
4726
  const ver = pkg.version;
3746
4727
  return ver;
3747
4728
  };
@@ -3843,7 +4824,7 @@ var info = () => {
3843
4824
  // src/cli/cache.ts
3844
4825
  init_constants();
3845
4826
  import { mkdir } from "fs/promises";
3846
- import { join as join5 } from "path";
4827
+ import { join as join6 } from "path";
3847
4828
  var {Glob } = globalThis.Bun;
3848
4829
  var CACHE_DIR = ".absolutejs";
3849
4830
  var MAX_FILES_PER_BATCH = 200;
@@ -3895,7 +4876,7 @@ var hashFiles = async (paths) => {
3895
4876
  };
3896
4877
  var loadCache = async (tool) => {
3897
4878
  try {
3898
- const path = join5(CACHE_DIR, `${tool}.cache.json`);
4879
+ const path = join6(CACHE_DIR, `${tool}.cache.json`);
3899
4880
  const data = await Bun.file(path).json();
3900
4881
  const result = data;
3901
4882
  return result;
@@ -3942,7 +4923,7 @@ var runTool = async (adapter, args) => {
3942
4923
  };
3943
4924
  var saveCache = async (tool, data) => {
3944
4925
  await mkdir(CACHE_DIR, { recursive: true });
3945
- const path = join5(CACHE_DIR, `${tool}.cache.json`);
4926
+ const path = join6(CACHE_DIR, `${tool}.cache.json`);
3946
4927
  await Bun.write(path, JSON.stringify(data, null, "\t"));
3947
4928
  };
3948
4929
 
@@ -3979,13 +4960,14 @@ var prettier = async (args) => {
3979
4960
  // src/cli/scripts/start.ts
3980
4961
  init_constants();
3981
4962
  init_getDurationString();
4963
+ init_instanceRegistry();
3982
4964
  init_loadConfig();
3983
4965
  init_startupBanner();
3984
4966
  init_telemetryEvent();
3985
4967
  init_utils();
3986
4968
  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";
4969
+ import { existsSync as existsSync8, readFileSync as readFileSync11, rmSync as rmSync3 } from "fs";
4970
+ import { basename as basename2, join as join9, resolve as resolve7 } from "path";
3989
4971
  var cliTag2 = (color, message) => `\x1B[2m${formatTimestamp()}\x1B[0m ${color}[cli]\x1B[0m ${color}${message}\x1B[0m`;
3990
4972
  var resolvePackageVersion = (candidates) => {
3991
4973
  for (const candidate of candidates) {
@@ -3998,7 +4980,7 @@ var resolvePackageVersion = (candidates) => {
3998
4980
  };
3999
4981
  var readPackageVersion2 = (candidate) => {
4000
4982
  try {
4001
- const pkg = JSON.parse(readFileSync10(candidate, "utf-8"));
4983
+ const pkg = JSON.parse(readFileSync11(candidate, "utf-8"));
4002
4984
  if (pkg.name !== "@absolutejs/absolute")
4003
4985
  return null;
4004
4986
  const ver = pkg.version;
@@ -4062,6 +5044,7 @@ var prerenderStaticPages = async (outputPath, prerenderPort, resolvedOutdir, sta
4062
5044
  killStaleProcesses(prerenderPort);
4063
5045
  const result = await prerenderWithServer2(outputPath, prerenderPort, resolvedOutdir, staticConfig, {
4064
5046
  ABSOLUTE_BUILD_DIR: resolvedOutdir,
5047
+ ABSOLUTE_INSTANCE_MANAGED: "1",
4065
5048
  ABSOLUTE_VERSION: absoluteVersion,
4066
5049
  FORCE_COLOR: "0",
4067
5050
  NODE_ENV: "production",
@@ -4077,7 +5060,7 @@ var prerenderStaticPages = async (outputPath, prerenderPort, resolvedOutdir, sta
4077
5060
  var start = async (serverEntry, outdir, configPath2) => {
4078
5061
  const port = Number(env2.PORT) || DEFAULT_PORT;
4079
5062
  killStaleProcesses(port);
4080
- const entryName = basename(serverEntry).replace(/\.[^.]+$/, "");
5063
+ const entryName = basename2(serverEntry).replace(/\.[^.]+$/, "");
4081
5064
  const resolvedOutdir = resolve7(outdir ?? "dist");
4082
5065
  const absoluteVersion = resolvePackageVersion([
4083
5066
  resolve7(import.meta.dir, "..", "..", "..", "package.json"),
@@ -4104,7 +5087,7 @@ var start = async (serverEntry, outdir, configPath2) => {
4104
5087
  if (!build)
4105
5088
  throw new Error("Could not locate build module");
4106
5089
  await build(buildConfig);
4107
- rmSync3(join8(resolvedOutdir, "_prerendered"), {
5090
+ rmSync3(join9(resolvedOutdir, "_prerendered"), {
4108
5091
  force: true,
4109
5092
  recursive: true
4110
5093
  });
@@ -4217,9 +5200,9 @@ var start = async (serverEntry, outdir, configPath2) => {
4217
5200
  process.exit(1);
4218
5201
  }
4219
5202
  if (existsSync8(resolve7(resolvedOutdir, "angular", "vendor", "server"))) {
4220
- const { readdirSync } = await import("fs");
5203
+ const { readdirSync: readdirSync2 } = await import("fs");
4221
5204
  const vendorDir = resolve7(resolvedOutdir, "angular", "vendor", "server");
4222
- const vendorEntries = readdirSync(vendorDir).filter((f) => f.endsWith(".js"));
5205
+ const vendorEntries = readdirSync2(vendorDir).filter((f) => f.endsWith(".js"));
4223
5206
  const angularServerVendorPaths = {};
4224
5207
  const { relative: pathRelative, dirname: pathDirname } = await import("path");
4225
5208
  for (const file of vendorEntries) {
@@ -4266,6 +5249,7 @@ var start = async (serverEntry, outdir, configPath2) => {
4266
5249
  ...process.env,
4267
5250
  ABSOLUTE_BUILD_DIR: resolvedOutdir,
4268
5251
  ABSOLUTE_BUILD_DURATION: String(Math.round(totalDuration)),
5252
+ ABSOLUTE_INSTANCE_MANAGED: "1",
4269
5253
  ABSOLUTE_VERSION: absoluteVersion,
4270
5254
  FORCE_COLOR: "1",
4271
5255
  NODE_ENV: "production",
@@ -4275,10 +5259,35 @@ var start = async (serverEntry, outdir, configPath2) => {
4275
5259
  stdin: "inherit",
4276
5260
  stdout: "inherit"
4277
5261
  });
5262
+ const relaunchCommand = [
5263
+ process.execPath,
5264
+ process.argv[1] ?? "",
5265
+ "start",
5266
+ serverEntry,
5267
+ ...outdir ? ["--outdir", outdir] : [],
5268
+ ...configPath2 ? ["--config", configPath2] : []
5269
+ ].filter((part) => part.length > 0);
5270
+ registerInstance({
5271
+ command: relaunchCommand,
5272
+ configPath: configPath2 ?? null,
5273
+ controllerPid: process.pid,
5274
+ cwd: process.cwd(),
5275
+ frameworks,
5276
+ host: env2.ABSOLUTE_HOST ?? env2.HOST ?? "localhost",
5277
+ https: env2.ABSOLUTE_HTTPS === "true",
5278
+ logFile: null,
5279
+ name: resolveProjectName(process.cwd()),
5280
+ pid: process.pid,
5281
+ port,
5282
+ ppid: process.ppid,
5283
+ source: "start",
5284
+ startedAt: new Date().toISOString()
5285
+ });
4278
5286
  const cleanup = async (exitCode2 = 0) => {
4279
5287
  if (cleaning)
4280
5288
  return;
4281
5289
  cleaning = true;
5290
+ deregisterInstance(process.pid);
4282
5291
  sendTelemetryEvent("start:session-duration", {
4283
5292
  duration: Math.round((Date.now() - sessionStart) / MILLISECONDS_IN_A_SECOND),
4284
5293
  entry: serverEntry
@@ -4311,27 +5320,24 @@ var start = async (serverEntry, outdir, configPath2) => {
4311
5320
  init_constants();
4312
5321
  init_loadConfig();
4313
5322
  init_getDurationString();
5323
+ init_instanceRegistry();
4314
5324
  import {
4315
5325
  appendFileSync,
4316
5326
  existsSync as existsSync9,
4317
- mkdirSync as mkdirSync5,
4318
- readdirSync,
4319
- readFileSync as readFileSync11,
4320
- unlinkSync as unlinkSync2,
4321
- writeFileSync as writeFileSync3
5327
+ mkdirSync as mkdirSync6,
5328
+ readdirSync as readdirSync2,
5329
+ readFileSync as readFileSync12,
5330
+ unlinkSync as unlinkSync3,
5331
+ writeFileSync as writeFileSync4
4322
5332
  } from "fs";
4323
5333
  import { createConnection } from "net";
4324
5334
  import { resolve as resolve8 } from "path";
4325
5335
 
4326
5336
  // src/cli/workspaceTui.ts
4327
5337
  init_constants();
5338
+ init_tuiPrimitives();
4328
5339
  init_getDurationString();
4329
- import { openSync as openSync2 } from "fs";
4330
- import { ReadStream as ReadStream2 } from "tty";
4331
5340
  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
5341
  var SHORTCUTS2 = new Map([
4336
5342
  ["c", "clear"],
4337
5343
  ["h", "help"],
@@ -4340,15 +5346,6 @@ var SHORTCUTS2 = new Map([
4340
5346
  ["q", "quit"],
4341
5347
  ["r", "restart"]
4342
5348
  ]);
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
5349
  var helpLines = [
4353
5350
  "Hotkeys",
4354
5351
  " h Toggle help",
@@ -4367,108 +5364,6 @@ var helpLines = [
4367
5364
  " Press Esc to exit shell mode or dismiss help.",
4368
5365
  " Use \u2191 and \u2193 to recall prior shell commands."
4369
5366
  ];
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
5367
  var getStatusColor = (status2) => {
4473
5368
  if (status2 === "ready")
4474
5369
  return colors.green;
@@ -4528,12 +5423,6 @@ var getVisibleLogContent = (contentLines, logHeight, logScrollOffset) => {
4528
5423
  const start2 = Math.max(0, end - logHeight);
4529
5424
  return contentLines.slice(start2, end);
4530
5425
  };
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
5426
  var createWorkspaceTui = ({
4538
5427
  actions,
4539
5428
  headless: headlessOption,
@@ -5004,10 +5893,10 @@ var stripAnsi2 = (value) => value.replace(ANSI_REGEX2, "");
5004
5893
  var sanitizeLogFileName = (value) => value.replace(/[^a-zA-Z0-9._-]/g, "_") || "unknown";
5005
5894
  var createWorkspaceLogSink = (appendLog) => {
5006
5895
  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"), "");
5896
+ mkdirSync6(logDirectory, { recursive: true });
5897
+ readdirSync2(logDirectory).filter((file) => file.endsWith(".log")).forEach((file) => unlinkSync3(resolve8(logDirectory, file)));
5898
+ writeFileSync4(resolve8(logDirectory, "all.log"), "");
5899
+ writeFileSync4(resolve8(logDirectory, "workspace.log"), "");
5011
5900
  const initializedSources = new Set(["workspace"]);
5012
5901
  const writeLog = (source, message, level) => {
5013
5902
  const cleanMessage = stripAnsi2(message).trimEnd();
@@ -5019,7 +5908,7 @@ var createWorkspaceLogSink = (appendLog) => {
5019
5908
  `;
5020
5909
  const sourceFile = resolve8(logDirectory, `${sanitizeLogFileName(source)}.log`);
5021
5910
  if (!initializedSources.has(source)) {
5022
- writeFileSync3(sourceFile, "");
5911
+ writeFileSync4(sourceFile, "");
5023
5912
  initializedSources.add(source);
5024
5913
  }
5025
5914
  appendFileSync(sourceFile, line);
@@ -5035,7 +5924,7 @@ var createWorkspaceLogSink = (appendLog) => {
5035
5924
  };
5036
5925
  var readPackageVersion3 = (candidate) => {
5037
5926
  try {
5038
- const pkg = JSON.parse(readFileSync11(candidate, "utf-8"));
5927
+ const pkg = JSON.parse(readFileSync12(candidate, "utf-8"));
5039
5928
  if (pkg.name !== "@absolutejs/absolute") {
5040
5929
  return null;
5041
5930
  }
@@ -5407,6 +6296,7 @@ var resolveAbsoluteServiceConfigPath = (service, cwd, options) => {
5407
6296
  var resolveService = (name, service, workspaceEnv, options) => {
5408
6297
  const cwd = resolve8(service.cwd ?? ".");
5409
6298
  const envVars = Object.assign(getDefinedProcessEnv(), workspaceEnv, service.port ? { PORT: String(service.port) } : {}, service.env, {
6299
+ ABSOLUTE_INSTANCE_MANAGED: "1",
5410
6300
  ABSOLUTE_WORKSPACE_MANAGED: "1",
5411
6301
  ABSOLUTE_WORKSPACE_SERVICE_NAME: name,
5412
6302
  ABSOLUTE_WORKSPACE_SERVICE_VISIBILITY: getVisibility(service),
@@ -5512,6 +6402,7 @@ var workspace = async (subcommand, options) => {
5512
6402
  try {
5513
6403
  service.process.kill();
5514
6404
  } catch {}
6405
+ deregisterInstance(service.process.pid);
5515
6406
  };
5516
6407
  const runShutdownHookSafely = async (service) => {
5517
6408
  try {
@@ -5654,6 +6545,22 @@ var workspace = async (subcommand, options) => {
5654
6545
  resolved
5655
6546
  };
5656
6547
  running.push(runningService);
6548
+ registerInstance({
6549
+ command: [],
6550
+ configPath: resolved.configPath ?? null,
6551
+ controllerPid: process.pid,
6552
+ cwd: resolved.cwd,
6553
+ frameworks: [],
6554
+ host: getServicePublicHost(resolved.service),
6555
+ https: getServiceProtocol(resolved.service) === "https",
6556
+ logFile: resolve8(workspaceLogs.logDirectory, `${sanitizeLogFileName(name)}.log`),
6557
+ name,
6558
+ pid: processHandle.pid,
6559
+ port: resolved.service.port ?? null,
6560
+ ppid: process.pid,
6561
+ source: "workspace",
6562
+ startedAt: new Date().toISOString()
6563
+ });
5657
6564
  processHandle.exited.then(handleServiceExit.bind(null, runningService));
5658
6565
  await waitForReady(resolved);
5659
6566
  const startedAt = serviceBootStartedAt.get(name);
@@ -5810,6 +6717,10 @@ if (command === "dev") {
5810
6717
  } else if (command === "prettier") {
5811
6718
  sendTelemetryEvent("cli:command", { command });
5812
6719
  await prettier(args);
6720
+ } else if (command === "ls" || command === "list" || command === "ps") {
6721
+ sendTelemetryEvent("cli:command", { command: "ls" });
6722
+ const { runList: runList2 } = await Promise.resolve().then(() => (init_list(), exports_list));
6723
+ await runList2(args);
5813
6724
  } else if (command === "info") {
5814
6725
  sendTelemetryEvent("cli:command", { command });
5815
6726
  info();
@@ -5847,6 +6758,7 @@ if (command === "dev") {
5847
6758
  console.error(" config [--port n] Open the unified config UI (ESLint, tsconfig, Prettier)");
5848
6759
  console.error(" eslint Run ESLint (cached)");
5849
6760
  console.error(" info Print system info for bug reports");
6761
+ console.error(" ls [--watch] [--json] List/manage running servers (alias: list)");
5850
6762
  console.error(" prettier Run Prettier check (cached)");
5851
6763
  console.error(" typecheck Run type checkers for all frameworks");
5852
6764
  console.error(" telemetry Manage anonymous telemetry");