@raingor/pi-web-switch 0.6.0 → 0.6.1
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/package.json +1 -1
- package/server/pi-reader.ts +28 -15
package/package.json
CHANGED
package/server/pi-reader.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync, readdirSync, existsSync, statSync, unlinkSync, writeFileSync, mkdirSync, renameSync, chmodSync } from "fs";
|
|
2
2
|
import { homedir, platform } from "os";
|
|
3
3
|
import { join, resolve, dirname, relative, sep } from "path";
|
|
4
|
-
import { spawnSync } from "child_process";
|
|
4
|
+
import { spawnSync, spawn } from "child_process";
|
|
5
5
|
import { DatabaseSync } from "node:sqlite";
|
|
6
6
|
|
|
7
7
|
const PI_DIR = join(homedir(), ".pi", "agent");
|
|
@@ -1868,26 +1868,39 @@ export async function optimizeMemory(): Promise<OptimizeMemoryResult> {
|
|
|
1868
1868
|
? cfg.consolidationTimeoutMs
|
|
1869
1869
|
: 600000;
|
|
1870
1870
|
|
|
1871
|
+
// IMPORTANT: use async spawn, NOT spawnSync. This runs inside the Vite dev
|
|
1872
|
+
// server's Node process; a multi-minute spawnSync would block the event loop,
|
|
1873
|
+
// freeze the HMR WebSocket heartbeat, and make the browser's Vite client
|
|
1874
|
+
// force a full page reload when the socket reconnects. Async spawn keeps the
|
|
1875
|
+
// loop free so /api/pi/memory/status polling and HMR stay responsive.
|
|
1871
1876
|
try {
|
|
1872
|
-
const
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1877
|
+
const status2: { code: number | null; killed: boolean; error?: Error } = await new Promise((resolvePromise) => {
|
|
1878
|
+
const child = spawn(bin, args, { stdio: ["ignore", "ignore", "ignore"] });
|
|
1879
|
+
let settled = false;
|
|
1880
|
+
const finish = (r: { code: number | null; killed: boolean; error?: Error }) => {
|
|
1881
|
+
if (settled) return;
|
|
1882
|
+
settled = true;
|
|
1883
|
+
clearTimeout(killTimer);
|
|
1884
|
+
resolvePromise(r);
|
|
1885
|
+
};
|
|
1886
|
+
const killTimer = setTimeout(() => {
|
|
1887
|
+
child.kill("SIGKILL");
|
|
1888
|
+
finish({ code: null, killed: true });
|
|
1889
|
+
}, timeoutMs);
|
|
1890
|
+
child.on("error", (error) => finish({ code: null, killed: false, error }));
|
|
1891
|
+
child.on("close", (code) => finish({ code, killed: false }));
|
|
1876
1892
|
});
|
|
1893
|
+
|
|
1877
1894
|
const after = memoryFileSizes();
|
|
1878
1895
|
const freedBytes = Object.values(before).reduce((a, b) => a + b, 0)
|
|
1879
1896
|
- Object.values(after).reduce((a, b) => a + b, 0);
|
|
1880
|
-
if (
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
after,
|
|
1886
|
-
freedBytes,
|
|
1887
|
-
message: killed ? `consolidation timed out after ${Math.round(timeoutMs / 1000)}s` : String(out.error.message),
|
|
1888
|
-
};
|
|
1897
|
+
if (status2.error) {
|
|
1898
|
+
return { success: false, before, after, freedBytes, message: String(status2.error.message) };
|
|
1899
|
+
}
|
|
1900
|
+
if (status2.killed) {
|
|
1901
|
+
return { success: false, before, after, freedBytes, message: `consolidation timed out after ${Math.round(timeoutMs / 1000)}s` };
|
|
1889
1902
|
}
|
|
1890
|
-
return { success:
|
|
1903
|
+
return { success: status2.code === 0, before, after, freedBytes, message: status2.code === 0 ? undefined : `exit ${status2.code}` };
|
|
1891
1904
|
} catch (e) {
|
|
1892
1905
|
const after = memoryFileSizes();
|
|
1893
1906
|
return { success: false, before, after, freedBytes: 0, message: e instanceof Error ? e.message : String(e) };
|