pi-sdk-web 0.5.17 → 0.5.18
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/cli.js +8 -7
- package/dist/log.js +28 -0
- package/dist/server.js +7 -6
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -16,6 +16,7 @@ import { getHeapStatistics } from "node:v8";
|
|
|
16
16
|
import { dirname, join } from "node:path";
|
|
17
17
|
import { findSessionByName, listSessions, loadBuiltinExtensions } from "./session.js";
|
|
18
18
|
import { PiWebServer } from "./server.js";
|
|
19
|
+
import { logError, logInfo } from "./log.js";
|
|
19
20
|
// ---------------------------------------------------------------------------
|
|
20
21
|
// Pretend to be Pi for in-process extensions.
|
|
21
22
|
//
|
|
@@ -90,11 +91,11 @@ function ensureHeapHeadroom() {
|
|
|
90
91
|
return false; // close enough already
|
|
91
92
|
const totalMb = totalmem() / (1024 * 1024);
|
|
92
93
|
if (totalMb < target * 2) {
|
|
93
|
-
|
|
94
|
+
logInfo(`heap: keeping the default limit (${heapLimitMb().toFixed(0)}MB) - ` +
|
|
94
95
|
`raising it to ${target}MB needs ~${target * 2}MB of RAM, host has ${totalMb.toFixed(0)}MB`);
|
|
95
96
|
return false;
|
|
96
97
|
}
|
|
97
|
-
|
|
98
|
+
logInfo(`heap: restarting with --max-old-space-size=${target} (current limit ${heapLimitMb().toFixed(0)}MB)`);
|
|
98
99
|
const child = spawn(process.execPath, [`--max-old-space-size=${target}`, fileURLToPath(import.meta.url), ...process.argv.slice(2)], { stdio: "inherit", env: { ...process.env, PI_WEB_HEAP_REEXEC: "1" } });
|
|
99
100
|
// Stay alive without acting on the signal ourselves: the child is in the
|
|
100
101
|
// same process group and runs its own graceful shutdown, and we exit with
|
|
@@ -103,7 +104,7 @@ function ensureHeapHeadroom() {
|
|
|
103
104
|
process.on("SIGTERM", () => { });
|
|
104
105
|
child.on("exit", (code, signal) => process.exit(signal ? 1 : (code ?? 1)));
|
|
105
106
|
child.on("error", (err) => {
|
|
106
|
-
|
|
107
|
+
logError(`heap: failed to restart with a larger heap: ${err.message}`);
|
|
107
108
|
process.exit(1);
|
|
108
109
|
});
|
|
109
110
|
return true;
|
|
@@ -145,7 +146,7 @@ async function cmdResume(name, port) {
|
|
|
145
146
|
}
|
|
146
147
|
catch {
|
|
147
148
|
// Session cwd no longer exists - keep current directory (same as pii)
|
|
148
|
-
|
|
149
|
+
logError(`Session cwd not found (${cwd}), keeping current directory`);
|
|
149
150
|
}
|
|
150
151
|
}
|
|
151
152
|
// Create services the way Pi's CLI does: extensions (including built-in
|
|
@@ -186,14 +187,14 @@ async function cmdResume(name, port) {
|
|
|
186
187
|
const { session } = runtime;
|
|
187
188
|
const server = new PiWebServer(runtime, { port });
|
|
188
189
|
await server.start();
|
|
189
|
-
|
|
190
|
-
|
|
190
|
+
logInfo(`server at http://127.0.0.1:${port}/ (session: ${info.name ?? info.id})`);
|
|
191
|
+
logInfo(`heap limit: ${heapLimitMb().toFixed(0)}MB`);
|
|
191
192
|
let shuttingDown = false;
|
|
192
193
|
const shutdown = async (signal) => {
|
|
193
194
|
if (shuttingDown)
|
|
194
195
|
return; // Repeated Ctrl+C must not re-enter teardown
|
|
195
196
|
shuttingDown = true;
|
|
196
|
-
|
|
197
|
+
logInfo(`${signal} received, shutting down...`);
|
|
197
198
|
try {
|
|
198
199
|
await server.stop();
|
|
199
200
|
}
|
package/dist/log.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timestamped logging for pi-web's own diagnostics.
|
|
3
|
+
*
|
|
4
|
+
* pi-web is a long-running server: its warnings (backpressure, memory
|
|
5
|
+
* watermarks, session issues) are read minutes to hours after the fact and
|
|
6
|
+
* mixed with other processes' output, so every line carries a local timestamp
|
|
7
|
+
* (`[2026-09-12 08:15:30] pi-web: …`).
|
|
8
|
+
*
|
|
9
|
+
* Only pi-web's own log lines go through here - command output (session
|
|
10
|
+
* lists, help text) stays plain so it can be piped or copied as-is.
|
|
11
|
+
*/
|
|
12
|
+
function timestamp(now) {
|
|
13
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
14
|
+
return (`${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ` +
|
|
15
|
+
`${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`);
|
|
16
|
+
}
|
|
17
|
+
/** Informational line (startup, status) on stdout. */
|
|
18
|
+
export function logInfo(message) {
|
|
19
|
+
console.log(`[${timestamp(new Date())}] ${message}`);
|
|
20
|
+
}
|
|
21
|
+
/** Diagnostic warning on stderr. */
|
|
22
|
+
export function logWarn(message) {
|
|
23
|
+
console.warn(`[${timestamp(new Date())}] ${message}`);
|
|
24
|
+
}
|
|
25
|
+
/** Error line on stderr. */
|
|
26
|
+
export function logError(message) {
|
|
27
|
+
console.error(`[${timestamp(new Date())}] ${message}`);
|
|
28
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -18,6 +18,7 @@ import { ModelRegistry, VERSION, } from "@earendil-works/pi-coding-agent";
|
|
|
18
18
|
import { WebSocket, WebSocketServer } from "ws";
|
|
19
19
|
import { listSessions } from "./session.js";
|
|
20
20
|
import { WebUIContext } from "./ui-context.js";
|
|
21
|
+
import { logError, logWarn } from "./log.js";
|
|
21
22
|
const DEFAULT_PORT = 4080;
|
|
22
23
|
// Static frontend: prefer the in-package copy (built by `npm run build` for
|
|
23
24
|
// global installs), fall back to the repo-root static/ during development.
|
|
@@ -202,7 +203,7 @@ export class PiWebServer {
|
|
|
202
203
|
process.chdir(cwd);
|
|
203
204
|
}
|
|
204
205
|
catch {
|
|
205
|
-
|
|
206
|
+
logError(`Session cwd not found (${cwd}), keeping current directory`);
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
209
|
this.broadcastState();
|
|
@@ -272,7 +273,7 @@ export class PiWebServer {
|
|
|
272
273
|
if (buffered > WS_BUFFER_HARD_LIMIT) {
|
|
273
274
|
// Far beyond reading - treat as gone. The browser reconnects and
|
|
274
275
|
// reloads, so no state is lost, and memory stays bounded.
|
|
275
|
-
|
|
276
|
+
logWarn(`pi-web: client not reading (${(buffered / 1048576).toFixed(0)}MB queued) - closing it; ` +
|
|
276
277
|
"the browser will reconnect and reload history");
|
|
277
278
|
this.backpressured.delete(client);
|
|
278
279
|
client.terminate();
|
|
@@ -281,14 +282,14 @@ export class PiWebServer {
|
|
|
281
282
|
if (droppable && buffered > WS_BUFFER_SOFT_LIMIT) {
|
|
282
283
|
if (!this.backpressured.has(client)) {
|
|
283
284
|
this.backpressured.add(client);
|
|
284
|
-
|
|
285
|
+
logWarn(`pi-web: client is behind (${(buffered / 1048576).toFixed(1)}MB queued) - ` +
|
|
285
286
|
"skipping stream deltas until it catches up");
|
|
286
287
|
}
|
|
287
288
|
continue;
|
|
288
289
|
}
|
|
289
290
|
if (this.backpressured.has(client) && buffered < WS_BUFFER_SOFT_LIMIT / 2) {
|
|
290
291
|
this.backpressured.delete(client);
|
|
291
|
-
|
|
292
|
+
logWarn("pi-web: client caught up - stream deltas resumed");
|
|
292
293
|
}
|
|
293
294
|
try {
|
|
294
295
|
client.send(message);
|
|
@@ -319,7 +320,7 @@ export class PiWebServer {
|
|
|
319
320
|
continue;
|
|
320
321
|
this.memoryLogged.add(threshold);
|
|
321
322
|
const queued = [...this.clients].map((c) => `${(c.bufferedAmount / 1048576).toFixed(1)}MB`).join(", ") || "no clients";
|
|
322
|
-
|
|
323
|
+
logWarn(`pi-web: memory ${rssMb.toFixed(0)}MB (heap ${(heapUsed / 1048576).toFixed(0)}/` +
|
|
323
324
|
`${(heapTotal / 1048576).toFixed(0)}MB), queued per client: ${queued}`);
|
|
324
325
|
}
|
|
325
326
|
}
|
|
@@ -430,7 +431,7 @@ export class PiWebServer {
|
|
|
430
431
|
// the largest single payload, so a client that never reads it must not be
|
|
431
432
|
// allowed to queue forever.
|
|
432
433
|
if (ws.bufferedAmount > WS_BUFFER_HARD_LIMIT) {
|
|
433
|
-
|
|
434
|
+
logWarn("pi-web: client not reading its initial snapshot - closing it");
|
|
434
435
|
ws.terminate();
|
|
435
436
|
return;
|
|
436
437
|
}
|