pi-web-ui 0.15.6 → 0.16.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.
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* so reconnects just re-request a snapshot.
|
|
12
12
|
*/
|
|
13
13
|
import { spawn } from "node:child_process";
|
|
14
|
-
import { existsSync, readFileSync, statSync, writeFileSync, mkdirSync, } from "node:fs";
|
|
14
|
+
import { existsSync, readFileSync, statSync, writeFileSync, mkdirSync, watch, } from "node:fs";
|
|
15
15
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
17
|
import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, getAgentDir, SessionManager, } from "@earendil-works/pi-coding-agent";
|
|
@@ -728,6 +728,14 @@ export class ClientSession {
|
|
|
728
728
|
disposed = false;
|
|
729
729
|
/** pi-config readiness check, cached briefly so 60ms snapshots don't hit disk. */
|
|
730
730
|
piCheckCache = null;
|
|
731
|
+
/** fs.watch on the currently-listed directory — file changes push an instant
|
|
732
|
+
* refresh (`file_changed`) so the tree updates without waiting for the 10s
|
|
733
|
+
* poll. Only the listed directory is watched (one level); navigating
|
|
734
|
+
* re-watches the new target. fs.watch isn't available on every platform /
|
|
735
|
+
* filesystem — failures silently fall back to the poll. */
|
|
736
|
+
fsWatcher = null;
|
|
737
|
+
watchPath = null;
|
|
738
|
+
watchTimer = null;
|
|
731
739
|
constructor(clientId, cwd, agentDir, stateStore) {
|
|
732
740
|
this.clientId = clientId;
|
|
733
741
|
this.cwd = cwd;
|
|
@@ -829,8 +837,11 @@ export class ClientSession {
|
|
|
829
837
|
this.sinks.delete(send);
|
|
830
838
|
// No sockets left for this client — kill its terminals so processes don't
|
|
831
839
|
// survive a closed tab / dropped connection.
|
|
832
|
-
if (this.sinks.size === 0)
|
|
840
|
+
if (this.sinks.size === 0) {
|
|
833
841
|
this.terminals.killAll();
|
|
842
|
+
// No sockets → nobody to refresh; drop the dir watcher too.
|
|
843
|
+
this.unwatchDir();
|
|
844
|
+
}
|
|
834
845
|
}
|
|
835
846
|
/** Broadcast to every connected socket of this client. */
|
|
836
847
|
emit(msg) {
|
|
@@ -2432,6 +2443,9 @@ export class ClientSession {
|
|
|
2432
2443
|
// always use "/", but relative() returns "\\" on Windows.
|
|
2433
2444
|
const rel = rawRel.split(sep).join("/");
|
|
2434
2445
|
const { entries, truncated, error } = await readDirForUI(target, rel);
|
|
2446
|
+
// Watch the listed directory (only after a successful read — a missing
|
|
2447
|
+
// dir throws above and must not create a watcher on a phantom path).
|
|
2448
|
+
this.watchDir(target, rel);
|
|
2435
2449
|
if (error) {
|
|
2436
2450
|
// Windows-only: unreadable system dirs degrade to an empty list
|
|
2437
2451
|
// with a warning instead of a hard error — the panel stays usable.
|
|
@@ -2461,6 +2475,56 @@ export class ClientSession {
|
|
|
2461
2475
|
});
|
|
2462
2476
|
}
|
|
2463
2477
|
}
|
|
2478
|
+
/** Watch a directory for changes so the file panel refreshes instantly
|
|
2479
|
+
* instead of waiting for the 10s poll. Watches the directory exactly as
|
|
2480
|
+
* listed (one level); navigating re-watches the new target. fs.watch is
|
|
2481
|
+
* unavailable on some platforms/filesystems — failures silently fall back
|
|
2482
|
+
* to the poll. */
|
|
2483
|
+
watchDir(absPath, rel) {
|
|
2484
|
+
if (this.disposed || this.watchPath === rel)
|
|
2485
|
+
return;
|
|
2486
|
+
this.unwatchDir();
|
|
2487
|
+
this.watchPath = rel;
|
|
2488
|
+
try {
|
|
2489
|
+
// persistent: false — the watcher must not keep the process alive.
|
|
2490
|
+
this.fsWatcher = watch(absPath, { persistent: false }, () => {
|
|
2491
|
+
// Burst events (npm install, git ops, editor save→rename) are
|
|
2492
|
+
// debounced into a single refresh.
|
|
2493
|
+
if (this.watchTimer)
|
|
2494
|
+
return;
|
|
2495
|
+
this.watchTimer = setTimeout(() => {
|
|
2496
|
+
this.watchTimer = null;
|
|
2497
|
+
this.emit({ type: "file_changed", path: this.watchPath ?? "" });
|
|
2498
|
+
}, 400);
|
|
2499
|
+
});
|
|
2500
|
+
this.fsWatcher.on("error", () => {
|
|
2501
|
+
// Directory deleted / unsupported fs — stop watching; the poll (or
|
|
2502
|
+
// the next navigation) restores things.
|
|
2503
|
+
this.unwatchDir();
|
|
2504
|
+
});
|
|
2505
|
+
}
|
|
2506
|
+
catch {
|
|
2507
|
+
// fs.watch unsupported (some network mounts, containers) — poll covers it.
|
|
2508
|
+
this.fsWatcher = null;
|
|
2509
|
+
this.watchPath = null;
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
unwatchDir() {
|
|
2513
|
+
if (this.watchTimer) {
|
|
2514
|
+
clearTimeout(this.watchTimer);
|
|
2515
|
+
this.watchTimer = null;
|
|
2516
|
+
}
|
|
2517
|
+
if (this.fsWatcher) {
|
|
2518
|
+
try {
|
|
2519
|
+
this.fsWatcher.close();
|
|
2520
|
+
}
|
|
2521
|
+
catch {
|
|
2522
|
+
// already closed
|
|
2523
|
+
}
|
|
2524
|
+
this.fsWatcher = null;
|
|
2525
|
+
}
|
|
2526
|
+
this.watchPath = null;
|
|
2527
|
+
}
|
|
2464
2528
|
/** Read a workspace file for the preview panel (size-capped, binary-safe). */
|
|
2465
2529
|
async readFile(relPath) {
|
|
2466
2530
|
try {
|
|
@@ -2827,6 +2891,7 @@ export class ClientSession {
|
|
|
2827
2891
|
clearInterval(this.widgetsTimer);
|
|
2828
2892
|
this.widgetsTimer = null;
|
|
2829
2893
|
}
|
|
2894
|
+
this.unwatchDir();
|
|
2830
2895
|
this.webUi.dispose();
|
|
2831
2896
|
for (const conv of this.convs.values()) {
|
|
2832
2897
|
conv.unsubscribe?.();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-web-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Web chat interface for the pi coding agent, powered by the pi SDK (@earendil-works/pi-coding-agent) — one-command run, Docker/systemd/launchd deployable",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|