@wrongstack/desktop 0.283.1 → 0.284.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main/main.js CHANGED
@@ -404,6 +404,7 @@ import { spawn } from "child_process";
404
404
  import { randomBytes } from "crypto";
405
405
  import { EventEmitter } from "events";
406
406
  import * as fs2 from "fs/promises";
407
+ import { existsSync } from "fs";
407
408
  import * as http from "http";
408
409
  import { createRequire } from "module";
409
410
  import * as net from "net";
@@ -588,6 +589,7 @@ var DesktopRuntimeManager = class extends EventEmitter {
588
589
  this.emitChanged();
589
590
  try {
590
591
  const entry = resolveWebUiEntry();
592
+ const distDir = resolveWebUiDistDir();
591
593
  const child = spawn(
592
594
  process.execPath,
593
595
  [
@@ -598,6 +600,8 @@ var DesktopRuntimeManager = class extends EventEmitter {
598
600
  String(httpPort),
599
601
  "--ws-port",
600
602
  String(wsPort),
603
+ "--dist-dir",
604
+ distDir,
601
605
  "--token",
602
606
  token,
603
607
  "--require-token"
@@ -628,9 +632,19 @@ var DesktopRuntimeManager = class extends EventEmitter {
628
632
  this.scheduleLogChanged(runtime);
629
633
  process.stderr.write(`[desktop:${runtime.id}] ${text}`);
630
634
  });
635
+ child.once("error", (err) => {
636
+ runtime.status = "error";
637
+ runtime.error = toErrorMessage(err);
638
+ runtime.child = null;
639
+ if (this.activeRuntimeId === runtime.id) {
640
+ this.activeRuntimeId = firstRunningRuntimeId(this.runtimes);
641
+ }
642
+ this.emitChanged();
643
+ });
631
644
  child.once("exit", (code, signal) => {
632
- runtime.status = runtime.status === "error" ? "error" : "stopped";
633
- runtime.error = runtime.status === "error" ? runtime.error : code === 0 ? void 0 : `Exited with ${signal ?? `code ${code ?? "unknown"}`}`;
645
+ if (runtime.status === "error") return;
646
+ runtime.status = "stopped";
647
+ runtime.error = code === 0 ? void 0 : `Exited with ${signal ?? `code ${code ?? "unknown"}`}`;
634
648
  runtime.child = null;
635
649
  if (this.activeRuntimeId === runtime.id) {
636
650
  this.activeRuntimeId = firstRunningRuntimeId(this.runtimes);
@@ -638,15 +652,25 @@ var DesktopRuntimeManager = class extends EventEmitter {
638
652
  this.emitChanged();
639
653
  });
640
654
  await waitForHttpReady(runtime.url, token, START_TIMEOUT_MS);
655
+ if (runtime.status !== "starting") {
656
+ throw new Error(runtime.error ?? "WebUI process exited during startup");
657
+ }
641
658
  runtime.status = "running";
642
659
  await this.persistWorkspaceState();
643
660
  this.emitChanged();
644
661
  return publicRuntime(runtime);
645
662
  } catch (err) {
646
- runtime.status = "error";
647
- runtime.error = toErrorMessage(err);
663
+ if (runtime.status !== "stopped" && runtime.status !== "error") {
664
+ runtime.status = "error";
665
+ }
666
+ if (runtime.error === void 0) {
667
+ runtime.error = toErrorMessage(err);
668
+ }
648
669
  await terminateProcessTree(runtime.child);
649
670
  runtime.child = null;
671
+ if (this.activeRuntimeId === runtime.id) {
672
+ this.activeRuntimeId = firstRunningRuntimeId(this.runtimes);
673
+ }
650
674
  this.emitChanged();
651
675
  throw err;
652
676
  }
@@ -692,7 +716,6 @@ var DesktopRuntimeManager = class extends EventEmitter {
692
716
  async unregisterProject(projectRoot) {
693
717
  const resolved = path2.resolve(projectRoot);
694
718
  this.registeredProjects = await removeGlobalProjectManifest(resolved);
695
- this.recentProjects = this.recentProjects.filter((project) => !samePath(project.root, resolved));
696
719
  await this.saveDesktopState();
697
720
  this.emitChanged();
698
721
  }
@@ -750,7 +773,7 @@ var DesktopRuntimeManager = class extends EventEmitter {
750
773
  openProjects
751
774
  );
752
775
  return {
753
- recentProjects: Array.isArray(parsed.recentProjects) ? parsed.recentProjects : [],
776
+ recentProjects: normalizeProjectEntries(parsed.recentProjects),
754
777
  openProjects,
755
778
  openProjectSessions,
756
779
  activeRuntimeId: normalizeRuntimeId(parsed.activeRuntimeId) ?? null,
@@ -1011,25 +1034,81 @@ function resolveWebUiEntry() {
1011
1034
  return path2.resolve(process.env["WRONGSTACK_WEBUI_ENTRY"]);
1012
1035
  }
1013
1036
  const require2 = createRequire(import.meta.url);
1037
+ try {
1038
+ const serverPkgPath = require2.resolve("@wrongstack/webui-server/package.json");
1039
+ const candidate = path2.join(path2.dirname(serverPkgPath), "dist", "server", "entry.js");
1040
+ if (existsSync(candidate)) return candidate;
1041
+ } catch {
1042
+ }
1014
1043
  const serverIndex = require2.resolve("@wrongstack/webui/server");
1015
1044
  return path2.join(path2.dirname(serverIndex), "entry.js");
1016
1045
  }
1046
+ function resolveWebUiDistDir() {
1047
+ if (process.env["WRONGSTACK_WEBUI_DIST"]) {
1048
+ return path2.resolve(process.env["WRONGSTACK_WEBUI_DIST"]);
1049
+ }
1050
+ const require2 = createRequire(import.meta.url);
1051
+ const serverIndex = require2.resolve("@wrongstack/webui/server");
1052
+ const candidate = path2.resolve(path2.dirname(serverIndex), "..");
1053
+ if (existsSync(candidate)) return candidate;
1054
+ throw new Error(
1055
+ `WebUI frontend assets not found at ${candidate}. Build @wrongstack/webui or set WRONGSTACK_WEBUI_DIST.`
1056
+ );
1057
+ }
1017
1058
  async function readGlobalProjectManifest() {
1018
1059
  const manifestFile = path2.join(wstackGlobalRoot2(), "projects.json");
1019
1060
  try {
1020
1061
  const raw = await fs2.readFile(manifestFile, "utf8");
1021
- const parsed = JSON.parse(raw);
1022
- const projects = Array.isArray(parsed.projects) ? parsed.projects : [];
1023
- return projects.filter((project) => typeof project?.root === "string" && project.root.trim()).map((project) => ({
1024
- ...project,
1025
- name: project.name || path2.basename(project.root) || project.root,
1026
- root: path2.resolve(project.root),
1027
- slug: project.slug || projectSlug(path2.resolve(project.root))
1028
- })).sort((a, b) => (b.lastSeen ?? b.createdAt ?? "").localeCompare(a.lastSeen ?? a.createdAt ?? "")).slice(0, 80);
1062
+ return normalizeProjectManifest(JSON.parse(raw));
1029
1063
  } catch {
1030
1064
  return [];
1031
1065
  }
1032
1066
  }
1067
+ function normalizeProjectManifest(value) {
1068
+ if (Array.isArray(value)) return normalizeProjectEntries(value).slice(0, 80);
1069
+ if (!value || typeof value !== "object") return [];
1070
+ const manifest = value;
1071
+ const source = Array.isArray(manifest.projects) ? manifest.projects : Array.isArray(manifest.recentProjects) ? manifest.recentProjects : Array.isArray(manifest.recents) ? manifest.recents : [];
1072
+ return normalizeProjectEntries(source).slice(0, 80);
1073
+ }
1074
+ function normalizeProjectEntries(value) {
1075
+ if (!Array.isArray(value)) return [];
1076
+ const seen = /* @__PURE__ */ new Set();
1077
+ const projects = [];
1078
+ for (const item of value) {
1079
+ const project = normalizeProjectEntry(item);
1080
+ if (!project) continue;
1081
+ const key = pathKey(project.root);
1082
+ if (seen.has(key)) continue;
1083
+ seen.add(key);
1084
+ projects.push(project);
1085
+ }
1086
+ return projects.sort(
1087
+ (a, b) => (b.lastSeen ?? b.createdAt ?? "").localeCompare(a.lastSeen ?? a.createdAt ?? "")
1088
+ );
1089
+ }
1090
+ function normalizeProjectEntry(value) {
1091
+ if (!value || typeof value !== "object") return null;
1092
+ const candidate = value;
1093
+ if (typeof candidate.root !== "string" || !candidate.root.trim()) return null;
1094
+ const root = path2.resolve(candidate.root);
1095
+ const name = typeof candidate.name === "string" && candidate.name.trim() ? candidate.name.trim() : path2.basename(root) || root;
1096
+ const entry = {
1097
+ name,
1098
+ root,
1099
+ slug: typeof candidate.slug === "string" && candidate.slug.trim() ? candidate.slug.trim() : projectSlug(root)
1100
+ };
1101
+ if (typeof candidate.lastSeen === "string" && candidate.lastSeen.trim()) {
1102
+ entry.lastSeen = candidate.lastSeen.trim();
1103
+ }
1104
+ if (typeof candidate.createdAt === "string" && candidate.createdAt.trim()) {
1105
+ entry.createdAt = candidate.createdAt.trim();
1106
+ }
1107
+ if (typeof candidate.lastWorkingDir === "string" && candidate.lastWorkingDir.trim()) {
1108
+ entry.lastWorkingDir = path2.resolve(candidate.lastWorkingDir);
1109
+ }
1110
+ return entry;
1111
+ }
1033
1112
  async function touchGlobalProjectManifest(entry) {
1034
1113
  const manifestFile = path2.join(wstackGlobalRoot2(), "projects.json");
1035
1114
  const projects = await readGlobalProjectManifest();