pi-mega-compact 0.4.9 → 0.4.10

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.
@@ -651,33 +651,40 @@ export default function (pi) {
651
651
  // ---- Dashboard server commands ----------------------------------------
652
652
  const portFile = join(currentStateDir, "port.pid");
653
653
  const runnerFile = join(currentStateDir, "_dashboard-runner.mjs");
654
+ const launchLog = join(currentStateDir, "_dashboard-launch.log");
654
655
  // Whether the runner must be spawned with --experimental-strip-types (true only
655
656
  // when we fall back to the .ts source outside node_modules; false when using
656
657
  // the shipped compiled dist/extensions/dashboard-server.js).
657
658
  let dashboardNeedsStrip = false;
658
- /** Try to reach a running dashboard server. Returns { port, url } or null. */
659
- async function isServerRunning() {
660
- if (!existsSync(portFile))
661
- return null;
662
- try {
663
- const info = JSON.parse(readFileSync(portFile, "utf-8"));
664
- if (!info?.port)
665
- return null;
666
- const url = `http://localhost:${info.port}`; // guardrails-allow PREVENT-PI-004: localhost URL of the dashboard server this extension spawned
667
- // Quick liveness probe
668
- const res = await fetch(`${url}/api/snapshot`, { signal: AbortSignal.timeout(1500) }); // guardrails-allow PREVENT-PI-004: localhost probe to the dashboard server this extension spawned
669
- if (res.ok)
670
- return { port: info.port, url };
671
- }
672
- catch {
673
- // stale or unreachable — clean up
659
+ // The dashboard server binds 9320–9329 (TARGET_PORT..TARGET_PORT+PORT_RANGE-1
660
+ // in dashboard-server.js). Probe each for a live /api/snapshot so we can detect
661
+ // readiness even when port.pid landed in a different state dir than we poll.
662
+ async function findLivePort() {
663
+ for (let port = 9320; port <= 9329; port++) {
674
664
  try {
675
- unlinkSync(portFile);
665
+ const res = await fetch(`http://localhost:${port}/api/snapshot`, { signal: AbortSignal.timeout(800) }); // guardrails-allow PREVENT-PI-004: localhost liveness probe of the dashboard server this extension spawned
666
+ if (res.ok)
667
+ return port;
676
668
  }
677
- catch { /* ignore */ }
669
+ catch { /* not on this port — try next */ }
678
670
  }
679
671
  return null;
680
672
  }
673
+ /** Try to reach a running dashboard server. Returns { port, url } or null. */
674
+ async function isServerRunning() {
675
+ const port = await findLivePort();
676
+ if (!port) {
677
+ // Stale marker with no live server behind it — clean up.
678
+ if (existsSync(portFile)) {
679
+ try {
680
+ unlinkSync(portFile);
681
+ }
682
+ catch { /* ignore */ }
683
+ }
684
+ return null;
685
+ }
686
+ return { port, url: `http://localhost:${port}` }; // guardrails-allow PREVENT-PI-004: localhost URL of the dashboard server this extension spawned
687
+ }
681
688
  /**
682
689
  * Resolve the launchable dashboard-server module.
683
690
  *
@@ -718,11 +725,16 @@ export default function (pi) {
718
725
  return false;
719
726
  dashboardNeedsStrip = resolved.needsStripTypes;
720
727
  const script = [
721
- `import { launchDashboardServer } from ${JSON.stringify(resolved.entry)};`,
722
- `launchDashboardServer(${JSON.stringify(currentStateDir)}).catch(err => {`,
723
- ` console.error("[mega-compact] dashboard failed:", err);`,
728
+ `import { appendFileSync } from "node:fs";`,
729
+ `const __log = ${JSON.stringify(launchLog)};`,
730
+ `function __fail(err) {`,
731
+ ` const msg = "[mega-compact] dashboard failed: " + (err && err.stack ? err.stack : String(err));`,
732
+ ` try { appendFileSync(__log, msg + "\\n"); } catch { /* ignore */ }`,
733
+ ` console.error(msg);`,
724
734
  ` process.exit(1);`,
725
- `});`,
735
+ `}`,
736
+ `import { launchDashboardServer } from ${JSON.stringify(resolved.entry)};`,
737
+ `launchDashboardServer(${JSON.stringify(currentStateDir)}).catch(__fail);`,
726
738
  ].join("\n");
727
739
  writeFileSync(runnerFile, script);
728
740
  return true;
@@ -763,24 +775,26 @@ export default function (pi) {
763
775
  stdio: "ignore",
764
776
  });
765
777
  child.unref();
766
- // Poll for port.pid (up to 5 seconds)
767
- const deadline = Date.now() + 5_000;
768
- let port;
778
+ // Poll for a live server (port 9320–9329) instead of relying solely on the
779
+ // port.pid marker, which can land in a different state dir than the one we
780
+ // poll when a prior compact left currentStateDir pointing elsewhere.
781
+ const deadline = Date.now() + 6_000;
782
+ let port = null;
769
783
  while (Date.now() < deadline) {
770
- await new Promise((r) => setTimeout(r, 300));
771
- if (existsSync(portFile)) {
772
- try {
773
- const raw = JSON.parse(readFileSync(portFile, "utf-8"));
774
- if (raw?.port) {
775
- port = raw.port;
776
- break;
777
- }
778
- }
779
- catch { /* keep polling */ }
780
- }
784
+ await new Promise((resolve) => setTimeout(resolve, 300));
785
+ port = await findLivePort();
786
+ if (port)
787
+ break;
781
788
  }
782
789
  if (!port) {
783
- ctx.ui.notify("[mega-compact] dashboard server failed to start — check logs.");
790
+ let detail = "";
791
+ try {
792
+ const log = readFileSync(launchLog, "utf-8").trim();
793
+ if (log)
794
+ detail = ` — ${log.split("\n").slice(-3).join("; ")}`;
795
+ }
796
+ catch { /* no log yet */ }
797
+ ctx.ui.notify(`[mega-compact] dashboard server failed to start${detail}. See ${launchLog}`);
784
798
  return;
785
799
  }
786
800
  const url = `http://localhost:${port}`; // guardrails-allow PREVENT-PI-004: localhost URL of the dashboard server this extension spawned
@@ -790,26 +790,36 @@ export default function (pi: ExtensionAPI) {
790
790
 
791
791
  const portFile = join(currentStateDir, "port.pid");
792
792
  const runnerFile = join(currentStateDir, "_dashboard-runner.mjs");
793
+ const launchLog = join(currentStateDir, "_dashboard-launch.log");
793
794
  // Whether the runner must be spawned with --experimental-strip-types (true only
794
795
  // when we fall back to the .ts source outside node_modules; false when using
795
796
  // the shipped compiled dist/extensions/dashboard-server.js).
796
797
  let dashboardNeedsStrip = false;
797
798
 
799
+ // The dashboard server binds 9320–9329 (TARGET_PORT..TARGET_PORT+PORT_RANGE-1
800
+ // in dashboard-server.js). Probe each for a live /api/snapshot so we can detect
801
+ // readiness even when port.pid landed in a different state dir than we poll.
802
+ async function findLivePort(): Promise<number | null> {
803
+ for (let port = 9320; port <= 9329; port++) {
804
+ try {
805
+ const res = await fetch(`http://localhost:${port}/api/snapshot`, { signal: AbortSignal.timeout(800) }); // guardrails-allow PREVENT-PI-004: localhost liveness probe of the dashboard server this extension spawned
806
+ if (res.ok) return port;
807
+ } catch { /* not on this port — try next */ }
808
+ }
809
+ return null;
810
+ }
811
+
798
812
  /** Try to reach a running dashboard server. Returns { port, url } or null. */
799
813
  async function isServerRunning(): Promise<{ port: number; url: string } | null> {
800
- if (!existsSync(portFile)) return null;
801
- try {
802
- const info = JSON.parse(readFileSync(portFile, "utf-8"));
803
- if (!info?.port) return null;
804
- const url = `http://localhost:${info.port}`; // guardrails-allow PREVENT-PI-004: localhost URL of the dashboard server this extension spawned
805
- // Quick liveness probe
806
- const res = await fetch(`${url}/api/snapshot`, { signal: AbortSignal.timeout(1500) }); // guardrails-allow PREVENT-PI-004: localhost probe to the dashboard server this extension spawned
807
- if (res.ok) return { port: info.port, url };
808
- } catch {
809
- // stale or unreachable — clean up
810
- try { unlinkSync(portFile); } catch { /* ignore */ }
814
+ const port = await findLivePort();
815
+ if (!port) {
816
+ // Stale marker with no live server behind it — clean up.
817
+ if (existsSync(portFile)) {
818
+ try { unlinkSync(portFile); } catch { /* ignore */ }
819
+ }
820
+ return null;
811
821
  }
812
- return null;
822
+ return { port, url: `http://localhost:${port}` }; // guardrails-allow PREVENT-PI-004: localhost URL of the dashboard server this extension spawned
813
823
  }
814
824
 
815
825
  /**
@@ -850,11 +860,16 @@ export default function (pi: ExtensionAPI) {
850
860
  if (!resolved) return false;
851
861
  dashboardNeedsStrip = resolved.needsStripTypes;
852
862
  const script = [
853
- `import { launchDashboardServer } from ${JSON.stringify(resolved.entry)};`,
854
- `launchDashboardServer(${JSON.stringify(currentStateDir)}).catch(err => {`,
855
- ` console.error("[mega-compact] dashboard failed:", err);`,
863
+ `import { appendFileSync } from "node:fs";`,
864
+ `const __log = ${JSON.stringify(launchLog)};`,
865
+ `function __fail(err) {`,
866
+ ` const msg = "[mega-compact] dashboard failed: " + (err && err.stack ? err.stack : String(err));`,
867
+ ` try { appendFileSync(__log, msg + "\\n"); } catch { /* ignore */ }`,
868
+ ` console.error(msg);`,
856
869
  ` process.exit(1);`,
857
- `});`,
870
+ `}`,
871
+ `import { launchDashboardServer } from ${JSON.stringify(resolved.entry)};`,
872
+ `launchDashboardServer(${JSON.stringify(currentStateDir)}).catch(__fail);`,
858
873
  ].join("\n");
859
874
  writeFileSync(runnerFile, script);
860
875
  return true;
@@ -900,21 +915,24 @@ export default function (pi: ExtensionAPI) {
900
915
  });
901
916
  child.unref();
902
917
 
903
- // Poll for port.pid (up to 5 seconds)
904
- const deadline = Date.now() + 5_000;
905
- let port: number | undefined;
918
+ // Poll for a live server (port 9320–9329) instead of relying solely on the
919
+ // port.pid marker, which can land in a different state dir than the one we
920
+ // poll when a prior compact left currentStateDir pointing elsewhere.
921
+ const deadline = Date.now() + 6_000;
922
+ let port: number | null = null;
906
923
  while (Date.now() < deadline) {
907
- await new Promise((r) => setTimeout(r, 300));
908
- if (existsSync(portFile)) {
909
- try {
910
- const raw = JSON.parse(readFileSync(portFile, "utf-8"));
911
- if (raw?.port) { port = raw.port; break; }
912
- } catch { /* keep polling */ }
913
- }
924
+ await new Promise((resolve) => setTimeout(resolve, 300));
925
+ port = await findLivePort();
926
+ if (port) break;
914
927
  }
915
928
 
916
929
  if (!port) {
917
- ctx.ui.notify("[mega-compact] dashboard server failed to start — check logs.");
930
+ let detail = "";
931
+ try {
932
+ const log = readFileSync(launchLog, "utf-8").trim();
933
+ if (log) detail = ` — ${log.split("\n").slice(-3).join("; ")}`;
934
+ } catch { /* no log yet */ }
935
+ ctx.ui.notify(`[mega-compact] dashboard server failed to start${detail}. See ${launchLog}`);
918
936
  return;
919
937
  }
920
938
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mega-compact",
3
- "version": "0.4.9",
3
+ "version": "0.4.10",
4
4
  "description": "Layered, local, vector-backed context compressor for pi — supersede/collapse/cluster compaction with deduped inline recall.",
5
5
  "type": "module",
6
6
  "license": "BSD-2-Clause",