@rotriz/pi-web-ui 1.3.0 → 1.4.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.
Files changed (3) hide show
  1. package/extension.mjs +16 -1
  2. package/package.json +1 -1
  3. package/server.mjs +54 -25
package/extension.mjs CHANGED
@@ -20,11 +20,26 @@ export default function (pi) {
20
20
  if (startPromise) return startPromise;
21
21
  startPromise = (async () => {
22
22
  try {
23
+ // Make @earendil-works/pi-coding-agent resolvable by the server module.
24
+ // Since we're in Pi's process, the module is already loaded — expose it globally.
25
+ try {
26
+ const piMod = await import("@earendil-works/pi-coding-agent");
27
+ globalThis.__PI_MODULE__ = piMod;
28
+ } catch {
29
+ // Fallback: resolve relative to Pi's entry script
30
+ const entry = process.argv[1] || "";
31
+ if (entry) {
32
+ const { dirname, join } = await import("node:path");
33
+ const dir = dirname(entry);
34
+ for (const c of [join(dir, "..", "dist", "index.js"), join(dir, "index.js")]) {
35
+ try { globalThis.__PI_MODULE__ = await import(c); break; } catch {}
36
+ }
37
+ }
38
+ }
23
39
  const { startServer } = await import("./server.mjs");
24
40
  await startServer(PORT);
25
41
  serverStarted = true;
26
42
  } catch (err) {
27
- // Server may already be listening (port in use = already running)
28
43
  if (err?.code === "EADDRINUSE") {
29
44
  serverStarted = true;
30
45
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rotriz/pi-web-ui",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Web UI extension for the Pi coding agent — browser-based session management, git integration, and task tracking",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/server.mjs CHANGED
@@ -19,8 +19,17 @@ const PORT = process.env.PORT || 3123;
19
19
  const CWD = process.cwd();
20
20
  const HERE = dirname(fileURLToPath(import.meta.url));
21
21
 
22
- // Resolve the pi-coding-agent module: env override → bare specifier → NODE_PATH → common global paths
22
+ // ─── Pi module resolution ─────────────────────────────────────────────────
23
+ let _piModule = null;
24
+
25
+ export function initPiModule(mod) {
26
+ _piModule = mod;
27
+ }
28
+
23
29
  async function resolvePiModule() {
30
+ if (_piModule) return _piModule;
31
+ // Check if the extension pre-loaded the module via globalThis
32
+ if (globalThis.__PI_MODULE__) { _piModule = globalThis.__PI_MODULE__; return _piModule; }
24
33
  const attempts = [];
25
34
  if (process.env.PI_WEB_PI_MODULE) attempts.push(process.env.PI_WEB_PI_MODULE);
26
35
  attempts.push("@earendil-works/pi-coding-agent");
@@ -30,35 +39,51 @@ async function resolvePiModule() {
30
39
  attempts.push(join(dir, "..", "dist", "index.js"));
31
40
  attempts.push(join(dir, "index.js"));
32
41
  }
33
- // Try NODE_PATH entries
34
42
  if (process.env.NODE_PATH) {
35
43
  for (const p of process.env.NODE_PATH.split(":")) {
36
44
  attempts.push(join(p, "@earendil-works", "pi-coding-agent", "dist", "index.js"));
37
45
  }
38
46
  }
39
- // Common global install locations
40
47
  const nodeDir = dirname(dirname(process.execPath));
41
48
  attempts.push(join(nodeDir, "lib", "node_modules", "@earendil-works", "pi-coding-agent", "dist", "index.js"));
42
- // npm global prefix
43
49
  attempts.push(join(homedir(), ".local", "lib", "node_modules", "@earendil-works", "pi-coding-agent", "dist", "index.js"));
44
50
  for (const attempt of attempts) {
45
- try { return await import(attempt); } catch {}
51
+ try { _piModule = await import(attempt); return _piModule; } catch {}
46
52
  }
47
53
  throw new Error("Could not resolve @earendil-works/pi-coding-agent. Install it globally or set PI_WEB_PI_MODULE env var.");
48
54
  }
49
- const pi = await resolvePiModule();
50
55
 
51
- const {
52
- createAgentSessionRuntime,
53
- createAgentSessionFromServices,
54
- createAgentSessionServices,
55
- DefaultResourceLoader,
56
- SessionManager,
57
- SettingsManager,
58
- getAgentDir,
59
- } = pi;
56
+ // Only auto-initialize when run directly (node server.mjs)
57
+ const _isDirectRun = process.argv[1] && resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url));
58
+ if (_isDirectRun) {
59
+ await resolvePiModule();
60
+ } else if (globalThis.__PI_MODULE__) {
61
+ _piModule = globalThis.__PI_MODULE__;
62
+ }
63
+
64
+ let createAgentSessionRuntime, createAgentSessionFromServices, createAgentSessionServices,
65
+ DefaultResourceLoader, SessionManager, SettingsManager, getAgentDir;
66
+
67
+ if (_piModule) {
68
+ ({ createAgentSessionRuntime, createAgentSessionFromServices, createAgentSessionServices,
69
+ DefaultResourceLoader, SessionManager, SettingsManager, getAgentDir } = _piModule);
70
+ }
71
+
72
+ let AGENT_DIR = null;
73
+
74
+ let server = null;
75
+ let _booted = false;
76
+
77
+ async function boot() {
78
+ if (_booted) return;
79
+ if (!_piModule) {
80
+ if (globalThis.__PI_MODULE__) _piModule = globalThis.__PI_MODULE__;
81
+ else await resolvePiModule();
82
+ ({ createAgentSessionRuntime, createAgentSessionFromServices, createAgentSessionServices,
83
+ DefaultResourceLoader, SessionManager, SettingsManager, getAgentDir } = _piModule);
84
+ }
85
+ AGENT_DIR = getAgentDir();
60
86
 
61
- const AGENT_DIR = getAgentDir();
62
87
 
63
88
  const createRuntime = async ({ cwd, sessionManager, sessionStartEvent }) => {
64
89
  const services = await createAgentSessionServices({ cwd });
@@ -868,7 +893,7 @@ function readBody(req) {
868
893
  });
869
894
  }
870
895
 
871
- const server = createServer(async (req, res) => {
896
+ server = createServer(async (req, res) => {
872
897
  const url = new URL(req.url, `http://localhost:${PORT}`);
873
898
  const path = url.pathname;
874
899
 
@@ -1402,16 +1427,20 @@ const server = createServer(async (req, res) => {
1402
1427
  json(res, 500, { error: String(err?.stack ?? err) });
1403
1428
  }
1404
1429
  });
1405
- // When run directly (node server.mjs), start listening immediately.
1406
- // When imported as a module (by extension.mjs), export control functions.
1407
- const isDirectRun = process.argv[1] && resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url));
1408
1430
 
1409
- if (isDirectRun) {
1431
+ _booted = true;
1432
+ }
1433
+
1434
+ // When run directly (node server.mjs), boot and listen immediately.
1435
+ const _isDirect = process.argv[1] && resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url));
1436
+ if (_isDirect) {
1437
+ await boot();
1410
1438
  server.listen(PORT, () => console.log(`[pi-web-ui] listening on http://localhost:${PORT}`));
1411
1439
  }
1412
1440
 
1413
- export { server, PORT, shared };
1414
- export function startServer(port) {
1441
+ export { PORT };
1442
+ export async function startServer(port) {
1443
+ await boot();
1415
1444
  return new Promise((res, rej) => {
1416
1445
  server.listen(port || PORT, () => {
1417
1446
  console.log(`[pi-web-ui] listening on http://localhost:${port || PORT}`);
@@ -1420,6 +1449,6 @@ export function startServer(port) {
1420
1449
  server.on("error", rej);
1421
1450
  });
1422
1451
  }
1423
- export function stopServer() {
1424
- return new Promise((res) => server.close(() => res()));
1452
+ export async function stopServer() {
1453
+ if (server) return new Promise((res) => server.close(() => res()));
1425
1454
  }