@rotriz/pi-web-ui 1.1.0 → 1.2.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.
package/extension.mjs CHANGED
@@ -1,9 +1,5 @@
1
1
  // pi-web-ui extension entry point for the Pi coding agent.
2
2
  // Registers the /web command and starts the HTTP/SSE gateway.
3
- //
4
- // When loaded as a Pi extension (via pi.extensions in package.json),
5
- // this module receives the Pi ExtensionAPI and bridges the host session
6
- // into the Web UI server.
7
3
 
8
4
  import { homedir } from "node:os";
9
5
  import { dirname, join } from "node:path";
@@ -33,6 +29,58 @@ function openBrowser(url) {
33
29
  exec(`${cmd} ${JSON.stringify(url)}`, () => {});
34
30
  }
35
31
 
32
+ // Resolve the pi-coding-agent module path from within Pi's process context
33
+ function findPiModulePath() {
34
+ try {
35
+ // When running inside Pi, the module is already loaded — find it via require.resolve or import.meta
36
+ const candidates = [];
37
+ // Check if pi's entry script reveals the path
38
+ const entry = process.argv[1] || "";
39
+ if (entry) {
40
+ const dir = dirname(entry);
41
+ candidates.push(join(dir, "..", "dist", "index.js"));
42
+ candidates.push(join(dir, "index.js"));
43
+ // Go up to find the package
44
+ let d = dir;
45
+ for (let i = 0; i < 5; i++) {
46
+ const candidate = join(d, "dist", "index.js");
47
+ if (existsSync(candidate)) candidates.push(candidate);
48
+ const pkgCandidate = join(d, "node_modules", "@earendil-works", "pi-coding-agent", "dist", "index.js");
49
+ if (existsSync(pkgCandidate)) candidates.push(pkgCandidate);
50
+ d = dirname(d);
51
+ }
52
+ }
53
+ // Check common global locations
54
+ const globalDirs = [
55
+ join(homedir(), ".local", "share", "fnm"),
56
+ "/usr/local/lib/node_modules",
57
+ "/usr/lib/node_modules",
58
+ join(homedir(), ".nvm", "versions", "node"),
59
+ join(homedir(), ".volta", "tools", "image", "packages"),
60
+ ];
61
+ for (const base of globalDirs) {
62
+ if (!existsSync(base)) continue;
63
+ // Recursively look for the package
64
+ const piPkg = "@earendil-works/pi-coding-agent/dist/index.js";
65
+ // Check direct path
66
+ const direct = join(base, piPkg);
67
+ if (existsSync(direct)) { candidates.push(direct); continue; }
68
+ // For version managers, search one level deep
69
+ }
70
+ // Also check NODE_PATH
71
+ if (process.env.NODE_PATH) {
72
+ for (const p of process.env.NODE_PATH.split(":")) {
73
+ const candidate = join(p, "@earendil-works", "pi-coding-agent", "dist", "index.js");
74
+ if (existsSync(candidate)) candidates.push(candidate);
75
+ }
76
+ }
77
+ for (const c of candidates) {
78
+ if (existsSync(c)) return c;
79
+ }
80
+ } catch {}
81
+ return null;
82
+ }
83
+
36
84
  export default function (pi) {
37
85
  let serverProcess = null;
38
86
  let origin = `http://localhost:${PORT}`;
@@ -46,7 +94,6 @@ export default function (pi) {
46
94
  })();
47
95
 
48
96
  function findNode() {
49
- // Use the same Node binary that's running pi
50
97
  return process.execPath;
51
98
  }
52
99
 
@@ -54,7 +101,7 @@ export default function (pi) {
54
101
  // Check if server is already running (from a previous session or manual start)
55
102
  try {
56
103
  const res = await httpGet(`${origin}/api/tabs`);
57
- if (res.ok) return; // Already running, reuse it
104
+ if (res.ok) return;
58
105
  } catch {}
59
106
 
60
107
  if (serverProcess && !serverProcess.killed) return;
@@ -62,14 +109,30 @@ export default function (pi) {
62
109
  if (!existsSync(serverPath)) {
63
110
  throw new Error(`pi-web-ui server not found at ${serverPath}`);
64
111
  }
112
+
113
+ // Resolve the pi module path and pass it to the server
114
+ const piModulePath = findPiModulePath();
115
+ const env = { ...process.env, PORT: String(PORT) };
116
+ if (piModulePath) env.PI_WEB_PI_MODULE = piModulePath;
117
+
118
+ // Also pass NODE_PATH so the subprocess can find global modules
119
+ const nodePath = [];
120
+ if (process.env.NODE_PATH) nodePath.push(process.env.NODE_PATH);
121
+ // Add the global node_modules from the current node binary
122
+ const nodeDir = dirname(dirname(process.execPath));
123
+ const globalNM = join(nodeDir, "lib", "node_modules");
124
+ if (existsSync(globalNM)) nodePath.push(globalNM);
125
+ if (nodePath.length) env.NODE_PATH = nodePath.join(":");
126
+
65
127
  serverProcess = execFile(findNode(), [serverPath], {
66
128
  cwd: process.cwd(),
67
- env: { ...process.env, PORT: String(PORT) },
129
+ env,
68
130
  stdio: "ignore",
69
131
  });
70
132
  serverProcess.unref();
71
133
  serverProcess.on("exit", () => { serverProcess = null; });
72
- // Wait for the server to be ready (up to 30 seconds for slow machines)
134
+
135
+ // Wait for the server to be ready (up to 30 seconds)
73
136
  for (let i = 0; i < 60; i++) {
74
137
  await new Promise((r) => setTimeout(r, 500));
75
138
  try {
@@ -89,7 +152,6 @@ export default function (pi) {
89
152
 
90
153
  // ── Lifecycle ────────────────────────────────────────────────────────
91
154
  pi.on("session_start", async (_event, ctx) => {
92
- // Start server in background — don't block Pi's session startup
93
155
  ensureServer().catch((err) => {
94
156
  ctx.ui?.notify?.(`pi-web-ui: ${err?.message ?? err}`, "warning");
95
157
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rotriz/pi-web-ui",
3
- "version": "1.1.0",
3
+ "version": "1.2.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,7 +19,7 @@ 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 → relative to pi entry
22
+ // Resolve the pi-coding-agent module: env override → bare specifier → NODE_PATH common global paths
23
23
  async function resolvePiModule() {
24
24
  const attempts = [];
25
25
  if (process.env.PI_WEB_PI_MODULE) attempts.push(process.env.PI_WEB_PI_MODULE);
@@ -30,13 +30,21 @@ async function resolvePiModule() {
30
30
  attempts.push(join(dir, "..", "dist", "index.js"));
31
31
  attempts.push(join(dir, "index.js"));
32
32
  }
33
- // Try global node_modules as last resort
34
- const globalPrefix = process.env.NODE_PATH?.split(":")[0];
35
- if (globalPrefix) attempts.push(join(globalPrefix, "@earendil-works/pi-coding-agent/dist/index.js"));
33
+ // Try NODE_PATH entries
34
+ if (process.env.NODE_PATH) {
35
+ for (const p of process.env.NODE_PATH.split(":")) {
36
+ attempts.push(join(p, "@earendil-works", "pi-coding-agent", "dist", "index.js"));
37
+ }
38
+ }
39
+ // Common global install locations
40
+ const nodeDir = dirname(dirname(process.execPath));
41
+ attempts.push(join(nodeDir, "lib", "node_modules", "@earendil-works", "pi-coding-agent", "dist", "index.js"));
42
+ // npm global prefix
43
+ attempts.push(join(homedir(), ".local", "lib", "node_modules", "@earendil-works", "pi-coding-agent", "dist", "index.js"));
36
44
  for (const attempt of attempts) {
37
45
  try { return await import(attempt); } catch {}
38
46
  }
39
- throw new Error("Could not resolve @earendil-works/pi-coding-agent. Install it globally or set PI_WEB_PI_MODULE.");
47
+ throw new Error("Could not resolve @earendil-works/pi-coding-agent. Install it globally or set PI_WEB_PI_MODULE env var.");
40
48
  }
41
49
  const pi = await resolvePiModule();
42
50