pi-web 0.5.0 → 0.6.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/README.md CHANGED
@@ -10,14 +10,21 @@ A web UI for the [pi coding agent](https://github.com/badlogic/pi-mono).
10
10
  npx -y pi-web@latest
11
11
  ```
12
12
 
13
- Then open [http://localhost:3100](http://localhost:3100) in your browser.
13
+ Then open [http://127.0.0.1:8192](http://127.0.0.1:8192) in your browser.
14
14
 
15
15
  ## Options
16
16
 
17
17
  ```
18
- --port <number> Port to listen on (default: 3100, env: PORT)
19
- --host <string> Host to bind to (default: localhost, env: HOST)
20
- --help Show help
18
+ --port <number> Port to listen on (default: 8192, env: PORT)
19
+ --host <string> Host to bind to (default: 127.0.0.1, env: HOST)
20
+ --agent <pi|omp> Agent backend profile (default: pi)
21
+ --help Show help
22
+ ```
23
+
24
+ To run against Oh My Pi, start with:
25
+
26
+ ```bash
27
+ npx -y pi-web@latest --agent omp
21
28
  ```
22
29
 
23
30
  ## Features
@@ -35,7 +42,8 @@ Then open [http://localhost:3100](http://localhost:3100) in your browser.
35
42
  git clone https://github.com/ravshansbox/pi-web
36
43
  cd pi-web
37
44
  npm install
38
- npm run dev
45
+ npm run dev:pi # Pi backend
46
+ npm run dev:omp # Oh My Pi backend
39
47
  ```
40
48
 
41
49
  Requires Node.js 22+.
@@ -14,9 +14,10 @@ pi-web - Web UI for the pi coding agent
14
14
  Usage: pi-web [options]
15
15
 
16
16
  Options:
17
- --port <number> Port to listen on (default: 8192, env: PORT)
18
- --host <string> Host to bind to (default: 127.0.0.1, env: HOST)
19
- -h, --help Show this help message
17
+ --port <number> Port to listen on (default: 8192, env: PORT)
18
+ --host <string> Host to bind to (default: 127.0.0.1, env: HOST)
19
+ --agent <pi|omp> Agent backend profile (default: pi)
20
+ -h, --help Show this help message
20
21
  `.trim());
21
22
  process.exit(0);
22
23
  }
@@ -30,9 +31,22 @@ function getArg(name) {
30
31
  return process.argv[idx + 1];
31
32
  return undefined;
32
33
  }
34
+ function parseAgent(value) {
35
+ const agent = (value || 'pi').toLowerCase();
36
+ if (agent === 'pi' || agent === 'omp')
37
+ return agent;
38
+ console.error(`invalid --agent value "${value}". expected "pi" or "omp"`);
39
+ process.exit(1);
40
+ }
41
+ function getAgentCommand(agent) {
42
+ return agent === 'omp'
43
+ ? 'npx -y @oh-my-pi/pi-coding-agent@latest'
44
+ : 'npx -y @mariozechner/pi-coding-agent@latest';
45
+ }
46
+ const AGENT = parseAgent(getArg('agent'));
33
47
  const PORT = parseInt(getArg('port') || process.env.PORT || '8192', 10);
34
48
  const HOST = getArg('host') || process.env.HOST || '127.0.0.1';
35
- const PI_CMD = process.env.PI_CMD || 'npx -y @mariozechner/pi-coding-agent@latest';
49
+ const AGENT_CMD = getAgentCommand(AGENT);
36
50
  const DEFAULT_IDLE_SESSION_TTL_MS = 60_000;
37
51
  const idleSessionTtlMsEnv = parseInt(process.env.PI_WEB_IDLE_SESSION_TTL_MS || '', 10);
38
52
  const IDLE_SESSION_TTL_MS = Number.isFinite(idleSessionTtlMsEnv) && idleSessionTtlMsEnv >= 0
@@ -106,7 +120,7 @@ const server = createServer((req, res) => {
106
120
  if (req.url?.startsWith('/api/sessions')) {
107
121
  const url = new URL(req.url, `http://${req.headers.host}`);
108
122
  const cwd = url.searchParams.get('cwd') || undefined;
109
- listSessions({ cwd, limit: 50 })
123
+ listSessions({ cwd, limit: 50, agent: AGENT })
110
124
  .then((data) => {
111
125
  res.writeHead(200, { 'Content-Type': 'application/json' });
112
126
  res.end(JSON.stringify(data));
@@ -140,7 +154,7 @@ const server = createServer((req, res) => {
140
154
  res.end(JSON.stringify({ error: 'cwd and filename parameters required' }));
141
155
  return;
142
156
  }
143
- const file = getSessionFilePath(cwd, filename);
157
+ const file = getSessionFilePath(cwd, filename, AGENT);
144
158
  if (req.method === 'DELETE') {
145
159
  try {
146
160
  unlinkSync(file);
@@ -272,10 +286,10 @@ function registerDiscoveredSessionKey(managed, event) {
272
286
  registerManagedSessionKey(managed, key);
273
287
  }
274
288
  function createManagedSession(cwd, sessionFile) {
275
- const sessionPath = sessionFile ? getSessionFilePath(cwd, sessionFile) : undefined;
289
+ const sessionPath = sessionFile ? getSessionFilePath(cwd, sessionFile, AGENT) : undefined;
276
290
  let managed = null;
277
291
  const rpc = new RpcSession({
278
- piCmd: PI_CMD,
292
+ piCmd: AGENT_CMD,
279
293
  cwd,
280
294
  sessionFile: sessionPath,
281
295
  onEvent: (event) => {
@@ -1,25 +1,40 @@
1
1
  import { readdir } from 'node:fs/promises';
2
- import { basename, join } from 'node:path';
2
+ import { basename, join, resolve } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
4
  import { createReadStream } from 'node:fs';
5
5
  import { createInterface } from 'node:readline';
6
- const SESSION_DIR = join(homedir(), '.pi', 'agent', 'sessions');
7
- function cwdToSessionDir(cwd) {
8
- return '-' + cwd.replace(/\//g, '-') + '--';
6
+ const HOME_DIR = resolve(homedir());
7
+ function getSessionDir(agent) {
8
+ const configDir = agent === 'omp' ? '.omp' : '.pi';
9
+ return join(HOME_DIR, configDir, 'agent', 'sessions');
9
10
  }
10
- export function getSessionFilePath(cwd, filename) {
11
- return join(SESSION_DIR, cwdToSessionDir(cwd), filename);
11
+ function cwdToSessionDir(cwd, agent) {
12
+ const normalisedCwd = resolve(cwd);
13
+ if (agent === 'omp') {
14
+ if (normalisedCwd === HOME_DIR ||
15
+ normalisedCwd.startsWith(`${HOME_DIR}/`) ||
16
+ normalisedCwd.startsWith(`${HOME_DIR}\\`)) {
17
+ const relative = normalisedCwd.slice(HOME_DIR.length).replace(/^[/\\]/, '');
18
+ return `-${relative.replace(/[/\\:]/g, '-')}`;
19
+ }
20
+ }
21
+ const encoded = normalisedCwd.replace(/^[/\\]/, '').replace(/[/\\:]/g, '-');
22
+ return `--${encoded}--`;
23
+ }
24
+ export function getSessionFilePath(cwd, filename, agent = 'pi') {
25
+ return join(getSessionDir(agent), cwdToSessionDir(cwd, agent), filename);
12
26
  }
13
27
  export async function listSessions(opts) {
14
- const { cwd, limit = 30 } = opts;
28
+ const { cwd, limit = 30, agent = 'pi' } = opts;
15
29
  const results = [];
30
+ const sessionDir = getSessionDir(agent);
16
31
  try {
17
- const cwdDirs = await readdir(SESSION_DIR, { withFileTypes: true });
32
+ const cwdDirs = await readdir(sessionDir, { withFileTypes: true });
18
33
  const targetDirs = cwd
19
- ? cwdDirs.filter((d) => d.isDirectory() && d.name === cwdToSessionDir(cwd))
34
+ ? cwdDirs.filter((d) => d.isDirectory() && d.name === cwdToSessionDir(cwd, agent))
20
35
  : cwdDirs.filter((d) => d.isDirectory());
21
36
  for (const dir of targetDirs) {
22
- const dirPath = join(SESSION_DIR, dir.name);
37
+ const dirPath = join(sessionDir, dir.name);
23
38
  let files;
24
39
  try {
25
40
  files = (await readdir(dirPath)).filter((f) => f.endsWith('.jsonl'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-web",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": "^24"
@@ -20,7 +20,9 @@
20
20
  "README.md"
21
21
  ],
22
22
  "scripts": {
23
- "dev": "concurrently \"vite build --watch\" \"tsc -w -p tsconfig.server.json\" \"node --watch build/server/server.js\"",
23
+ "dev": "npm run dev:pi",
24
+ "dev:pi": "concurrently \"vite build --watch\" \"tsc -w -p tsconfig.server.json\" \"node --watch build/server/server.js --agent pi\"",
25
+ "dev:omp": "concurrently \"vite build --watch\" \"tsc -w -p tsconfig.server.json\" \"node --watch build/server/server.js --agent omp\"",
24
26
  "build": "vite build && tsc -p tsconfig.server.json",
25
27
  "build:bin": "node scripts/generate-bin.mjs",
26
28
  "preview": "vite preview",