pi-web 0.14.2 → 0.14.3
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 +1 -9
- package/build/server/server.js +6 -20
- package/build/server/sessions.js +8 -17
- package/package.json +2 -4
package/README.md
CHANGED
|
@@ -19,16 +19,9 @@ For remote access, use the **recommended** Tailscale HTTPS setup below.
|
|
|
19
19
|
```
|
|
20
20
|
--port <number> Port to listen on (default: 8192)
|
|
21
21
|
--host <string> Host to bind to (default: 127.0.0.1)
|
|
22
|
-
--agent <pi|omp> Agent backend profile (default: pi)
|
|
23
22
|
--help Show help
|
|
24
23
|
```
|
|
25
24
|
|
|
26
|
-
To run against Oh My Pi, start with:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
npx -y pi-web@latest --agent omp
|
|
30
|
-
```
|
|
31
|
-
|
|
32
25
|
## Recommended: secure access with Tailscale (HTTPS, no app password)
|
|
33
26
|
|
|
34
27
|
`pi-web` does not include built-in authentication. **Recommended setup:** keep it bound to `127.0.0.1` (IPv4 loopback) and expose it only through your private Tailnet.
|
|
@@ -93,8 +86,7 @@ Notes:
|
|
|
93
86
|
git clone https://github.com/ravshansbox/pi-web
|
|
94
87
|
cd pi-web
|
|
95
88
|
npm install
|
|
96
|
-
npm run dev
|
|
97
|
-
npm run dev:omp # Oh My Pi backend
|
|
89
|
+
npm run dev
|
|
98
90
|
```
|
|
99
91
|
|
|
100
92
|
Requires Node.js 22+.
|
package/build/server/server.js
CHANGED
|
@@ -7,7 +7,7 @@ import { homedir } from 'node:os';
|
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
9
9
|
import { RpcSession } from './rpc.js';
|
|
10
|
-
import { listSessions, readSessionMessages, getSessionFilePath
|
|
10
|
+
import { listSessions, readSessionMessages, getSessionFilePath } from './sessions.js';
|
|
11
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
13
13
|
console.log(`
|
|
@@ -18,7 +18,6 @@ Usage: pi-web [options]
|
|
|
18
18
|
Options:
|
|
19
19
|
--port <number> Port to listen on (default: 8192)
|
|
20
20
|
--host <string> Host to bind to (default: 127.0.0.1)
|
|
21
|
-
--agent <pi|omp> Agent backend profile (default: pi)
|
|
22
21
|
-h, --help Show this help message
|
|
23
22
|
`.trim());
|
|
24
23
|
process.exit(0);
|
|
@@ -33,22 +32,9 @@ function getArg(name) {
|
|
|
33
32
|
return process.argv[idx + 1];
|
|
34
33
|
return undefined;
|
|
35
34
|
}
|
|
36
|
-
|
|
37
|
-
const agent = (value || 'pi').toLowerCase();
|
|
38
|
-
if (agent === 'pi' || agent === 'omp')
|
|
39
|
-
return agent;
|
|
40
|
-
console.error(`invalid --agent value "${value}". expected "pi" or "omp"`);
|
|
41
|
-
process.exit(1);
|
|
42
|
-
}
|
|
43
|
-
function getAgentCommand(agent) {
|
|
44
|
-
return agent === 'omp'
|
|
45
|
-
? { command: 'npx', args: ['-y', '@earendil-works/pi-coding-agent@latest'] }
|
|
46
|
-
: { command: 'npx', args: ['-y', '@earendil-works/pi-coding-agent@latest'] };
|
|
47
|
-
}
|
|
48
|
-
const AGENT = parseAgent(getArg('agent'));
|
|
35
|
+
const AGENT_CMD = { command: 'npx', args: ['-y', '@earendil-works/pi-coding-agent@latest'] };
|
|
49
36
|
const PORT = parseInt(getArg('port') || '8192', 10);
|
|
50
37
|
const HOST = getArg('host') || '127.0.0.1';
|
|
51
|
-
const AGENT_CMD = getAgentCommand(AGENT);
|
|
52
38
|
const IDLE_SESSION_TTL_MS = 60_000;
|
|
53
39
|
const isWatchMode = process.argv.includes('--watch') ||
|
|
54
40
|
process.execArgv.some((arg) => arg === '--watch' || arg.startsWith('--watch-'));
|
|
@@ -59,7 +45,7 @@ const distDir = distDirCandidates.find((candidate) => existsSync(join(candidate,
|
|
|
59
45
|
const htmlPath = join(distDir, 'index.html');
|
|
60
46
|
const htmlCache = isDev || !existsSync(htmlPath) ? null : readFileSync(htmlPath, 'utf-8');
|
|
61
47
|
const HOME_DIR = resolve(homedir() || '/');
|
|
62
|
-
const SESSION_ROOT = resolve(join(HOME_DIR,
|
|
48
|
+
const SESSION_ROOT = resolve(join(HOME_DIR, '.pi', 'agent', 'sessions'));
|
|
63
49
|
function isWithinRoot(path, root) {
|
|
64
50
|
const rel = relative(root, path);
|
|
65
51
|
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
|
|
@@ -116,7 +102,7 @@ function isValidSessionFilename(filename) {
|
|
|
116
102
|
function resolveSessionPath(cwd, filename) {
|
|
117
103
|
if (!isValidSessionFilename(filename))
|
|
118
104
|
return null;
|
|
119
|
-
const resolved = resolve(getSessionFilePath(cwd, filename
|
|
105
|
+
const resolved = resolve(getSessionFilePath(cwd, filename));
|
|
120
106
|
return isWithinRoot(resolved, SESSION_ROOT) ? resolved : null;
|
|
121
107
|
}
|
|
122
108
|
const server = createServer((req, res) => {
|
|
@@ -133,7 +119,7 @@ const server = createServer((req, res) => {
|
|
|
133
119
|
if (req.url?.startsWith('/api/sessions')) {
|
|
134
120
|
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
135
121
|
const cwd = url.searchParams.get('cwd') || undefined;
|
|
136
|
-
listSessions({ cwd, limit: 50
|
|
122
|
+
listSessions({ cwd, limit: 50 })
|
|
137
123
|
.then((data) => {
|
|
138
124
|
const sessionsWithRuntime = data.map((session) => ({
|
|
139
125
|
...session,
|
|
@@ -369,7 +355,7 @@ function updateSessionModelCapability(managed, event) {
|
|
|
369
355
|
}
|
|
370
356
|
}
|
|
371
357
|
function createManagedSession(cwd, sessionFile, initialKey) {
|
|
372
|
-
const sessionPath = sessionFile ? getSessionFilePath(cwd, sessionFile
|
|
358
|
+
const sessionPath = sessionFile ? getSessionFilePath(cwd, sessionFile) : undefined;
|
|
373
359
|
let managed = null;
|
|
374
360
|
const rpc = new RpcSession({
|
|
375
361
|
piCmd: AGENT_CMD,
|
package/build/server/sessions.js
CHANGED
|
@@ -4,34 +4,25 @@ import { homedir } from 'node:os';
|
|
|
4
4
|
import { createReadStream } from 'node:fs';
|
|
5
5
|
import { createInterface } from 'node:readline';
|
|
6
6
|
const HOME_DIR = resolve(homedir());
|
|
7
|
-
function getSessionDir(
|
|
8
|
-
|
|
9
|
-
return join(HOME_DIR, configDir, 'agent', 'sessions');
|
|
7
|
+
function getSessionDir() {
|
|
8
|
+
return join(HOME_DIR, '.pi', 'agent', 'sessions');
|
|
10
9
|
}
|
|
11
|
-
function cwdToSessionDir(cwd
|
|
10
|
+
function cwdToSessionDir(cwd) {
|
|
12
11
|
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
12
|
const encoded = normalisedCwd.replace(/^[/\\]/, '').replace(/[/\\:]/g, '-');
|
|
22
13
|
return `--${encoded}--`;
|
|
23
14
|
}
|
|
24
|
-
export function getSessionFilePath(cwd, filename
|
|
25
|
-
return join(getSessionDir(
|
|
15
|
+
export function getSessionFilePath(cwd, filename) {
|
|
16
|
+
return join(getSessionDir(), cwdToSessionDir(cwd), filename);
|
|
26
17
|
}
|
|
27
18
|
export async function listSessions(opts) {
|
|
28
|
-
const { cwd, limit = 30
|
|
19
|
+
const { cwd, limit = 30 } = opts;
|
|
29
20
|
const results = [];
|
|
30
|
-
const sessionDir = getSessionDir(
|
|
21
|
+
const sessionDir = getSessionDir();
|
|
31
22
|
try {
|
|
32
23
|
const cwdDirs = await readdir(sessionDir, { withFileTypes: true });
|
|
33
24
|
const targetDirs = cwd
|
|
34
|
-
? cwdDirs.filter((d) => d.isDirectory() && d.name === cwdToSessionDir(cwd
|
|
25
|
+
? cwdDirs.filter((d) => d.isDirectory() && d.name === cwdToSessionDir(cwd))
|
|
35
26
|
: cwdDirs.filter((d) => d.isDirectory());
|
|
36
27
|
for (const dir of targetDirs) {
|
|
37
28
|
const dirPath = join(sessionDir, dir.name);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-web",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Web UI for the pi coding agent",
|
|
6
6
|
"repository": {
|
|
@@ -24,9 +24,7 @@
|
|
|
24
24
|
"LICENSE"
|
|
25
25
|
],
|
|
26
26
|
"scripts": {
|
|
27
|
-
"dev": "
|
|
28
|
-
"dev:pi": "concurrently \"vite build --watch\" \"tsc -w -p tsconfig.server.json\" \"node --watch build/server/server.js --agent pi\"",
|
|
29
|
-
"dev:omp": "concurrently \"vite build --watch\" \"tsc -w -p tsconfig.server.json\" \"node --watch build/server/server.js --agent omp\"",
|
|
27
|
+
"dev": "concurrently \"vite build --watch\" \"tsc -w -p tsconfig.server.json\" \"node --watch build/server/server.js\"",
|
|
30
28
|
"build": "vite build && tsc -p tsconfig.server.json",
|
|
31
29
|
"build:bin": "node scripts/generate-bin.mjs",
|
|
32
30
|
"test:smoke": "node scripts/smoke-checks.mjs",
|