pi-web 0.13.6 → 0.14.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/build/server/rpc.js +9 -3
- package/build/server/server.js +17 -35
- package/build/server/sessions.js +16 -10
- package/build/server/shared/pi-types.js +1 -0
- package/build/server/shared/rpc-event.js +1 -0
- package/dist/assets/index-o_ZaJEV-.js +25 -0
- package/dist/index.html +1 -1
- package/package.json +18 -6
- package/dist/assets/index-h7NcwCKn.js +0 -25
package/build/server/rpc.js
CHANGED
|
@@ -25,7 +25,9 @@ export class RpcSession {
|
|
|
25
25
|
try {
|
|
26
26
|
this.opts.onEvent(JSON.parse(line));
|
|
27
27
|
}
|
|
28
|
-
catch {
|
|
28
|
+
catch {
|
|
29
|
+
// malformed JSON line — skip
|
|
30
|
+
}
|
|
29
31
|
}
|
|
30
32
|
idx = this.buffer.indexOf('\n');
|
|
31
33
|
}
|
|
@@ -56,9 +58,13 @@ export class RpcSession {
|
|
|
56
58
|
try {
|
|
57
59
|
this.proc.kill('SIGKILL');
|
|
58
60
|
}
|
|
59
|
-
catch {
|
|
61
|
+
catch {
|
|
62
|
+
// process already gone
|
|
63
|
+
}
|
|
60
64
|
}, 2000);
|
|
61
65
|
}
|
|
62
|
-
catch {
|
|
66
|
+
catch {
|
|
67
|
+
// process already gone
|
|
68
|
+
}
|
|
63
69
|
}
|
|
64
70
|
}
|
package/build/server/server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createServer } from 'node:http';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { createReadStream, existsSync, readFileSync, statSync, unlinkSync
|
|
3
|
+
import { createReadStream, existsSync, readFileSync, statSync, unlinkSync } from 'node:fs';
|
|
4
4
|
import { readdir } from 'node:fs/promises';
|
|
5
5
|
import { basename, dirname, extname, isAbsolute, join, normalize, relative, resolve, } from 'node:path';
|
|
6
6
|
import { homedir } from 'node:os';
|
|
@@ -29,9 +29,7 @@ function getArg(name) {
|
|
|
29
29
|
if (pair)
|
|
30
30
|
return pair.slice(flag.length);
|
|
31
31
|
const idx = process.argv.indexOf(`--${name}`);
|
|
32
|
-
if (idx !== -1 &&
|
|
33
|
-
process.argv[idx + 1] &&
|
|
34
|
-
!process.argv[idx + 1].startsWith('--'))
|
|
32
|
+
if (idx !== -1 && process.argv[idx + 1] && !process.argv[idx + 1].startsWith('--'))
|
|
35
33
|
return process.argv[idx + 1];
|
|
36
34
|
return undefined;
|
|
37
35
|
}
|
|
@@ -55,11 +53,9 @@ const IDLE_SESSION_TTL_MS = 60_000;
|
|
|
55
53
|
const isWatchMode = process.argv.includes('--watch') ||
|
|
56
54
|
process.execArgv.some((arg) => arg === '--watch' || arg.startsWith('--watch-'));
|
|
57
55
|
const isDev = isWatchMode;
|
|
58
|
-
const distDirCandidates = [
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
];
|
|
62
|
-
const distDir = distDirCandidates.find((candidate) => existsSync(join(candidate, 'index.html'))) ?? distDirCandidates[0];
|
|
56
|
+
const distDirCandidates = [join(__dirname, 'dist'), join(__dirname, '..', '..', 'dist')];
|
|
57
|
+
const distDir = distDirCandidates.find((candidate) => existsSync(join(candidate, 'index.html'))) ??
|
|
58
|
+
distDirCandidates[0];
|
|
63
59
|
const htmlPath = join(distDir, 'index.html');
|
|
64
60
|
const htmlCache = isDev || !existsSync(htmlPath) ? null : readFileSync(htmlPath, 'utf-8');
|
|
65
61
|
const HOME_DIR = resolve(homedir() || '/');
|
|
@@ -208,16 +204,12 @@ const server = createServer((req, res) => {
|
|
|
208
204
|
.replace(/^(\.\.[/\\])+/, '')
|
|
209
205
|
.replace(/^[/\\]+/, '');
|
|
210
206
|
const filePath = resolve(join(distDir, safePath));
|
|
211
|
-
if (isWithinRoot(filePath, distDir) &&
|
|
212
|
-
existsSync(filePath) &&
|
|
213
|
-
statSync(filePath).isFile()) {
|
|
207
|
+
if (isWithinRoot(filePath, distDir) && existsSync(filePath) && statSync(filePath).isFile()) {
|
|
214
208
|
serveFile(filePath, res);
|
|
215
209
|
return;
|
|
216
210
|
}
|
|
217
211
|
const acceptsHtml = (req.headers.accept ?? '').includes('text/html');
|
|
218
|
-
if (req.method === 'GET' &&
|
|
219
|
-
!url.pathname.startsWith('/api/') &&
|
|
220
|
-
acceptsHtml) {
|
|
212
|
+
if (req.method === 'GET' && !url.pathname.startsWith('/api/') && acceptsHtml) {
|
|
221
213
|
if (!existsSync(htmlPath)) {
|
|
222
214
|
res.writeHead(503, { 'Content-Type': 'text/plain' });
|
|
223
215
|
res.end('frontend not built. run: npm run build');
|
|
@@ -339,9 +331,9 @@ function detachSocket(ws) {
|
|
|
339
331
|
cleanupIfIdle(current);
|
|
340
332
|
}
|
|
341
333
|
function registerDiscoveredSessionKey(managed, event) {
|
|
342
|
-
if (event
|
|
334
|
+
if (event.type !== 'response' || event.command !== 'get_state' || !event.success)
|
|
343
335
|
return;
|
|
344
|
-
const sessionPath = event
|
|
336
|
+
const sessionPath = event.data?.sessionFile;
|
|
345
337
|
if (typeof sessionPath !== 'string' || sessionPath.length === 0)
|
|
346
338
|
return;
|
|
347
339
|
const key = buildSessionKey(managed.cwd, basename(sessionPath));
|
|
@@ -356,8 +348,6 @@ function deriveModelSupportsImages(model) {
|
|
|
356
348
|
return input.includes('image');
|
|
357
349
|
}
|
|
358
350
|
function updateSessionModelCapability(managed, event) {
|
|
359
|
-
if (!event || typeof event !== 'object')
|
|
360
|
-
return;
|
|
361
351
|
if (event.type === 'model_changed') {
|
|
362
352
|
const supports = deriveModelSupportsImages(event.model);
|
|
363
353
|
if (supports != null)
|
|
@@ -365,7 +355,7 @@ function updateSessionModelCapability(managed, event) {
|
|
|
365
355
|
return;
|
|
366
356
|
}
|
|
367
357
|
if (event.type === 'response') {
|
|
368
|
-
if (event.command === 'get_state') {
|
|
358
|
+
if (event.command === 'get_state' && event.success) {
|
|
369
359
|
const supports = deriveModelSupportsImages(event.data?.model);
|
|
370
360
|
if (supports != null)
|
|
371
361
|
managed.currentModelSupportsImages = supports;
|
|
@@ -379,9 +369,7 @@ function updateSessionModelCapability(managed, event) {
|
|
|
379
369
|
}
|
|
380
370
|
}
|
|
381
371
|
function createManagedSession(cwd, sessionFile, initialKey) {
|
|
382
|
-
const sessionPath = sessionFile
|
|
383
|
-
? getSessionFilePath(cwd, sessionFile, AGENT)
|
|
384
|
-
: undefined;
|
|
372
|
+
const sessionPath = sessionFile ? getSessionFilePath(cwd, sessionFile, AGENT) : undefined;
|
|
385
373
|
let managed = null;
|
|
386
374
|
const rpc = new RpcSession({
|
|
387
375
|
piCmd: AGENT_CMD,
|
|
@@ -390,11 +378,11 @@ function createManagedSession(cwd, sessionFile, initialKey) {
|
|
|
390
378
|
onEvent: (event) => {
|
|
391
379
|
if (!managed)
|
|
392
380
|
return;
|
|
393
|
-
if (event
|
|
381
|
+
if (event.type === 'agent_start') {
|
|
394
382
|
managed.isAgentRunning = true;
|
|
395
383
|
clearIdleCleanupTimer(managed);
|
|
396
384
|
}
|
|
397
|
-
if (event
|
|
385
|
+
if (event.type === 'agent_end') {
|
|
398
386
|
managed.isAgentRunning = false;
|
|
399
387
|
cleanupIfIdle(managed);
|
|
400
388
|
}
|
|
@@ -447,9 +435,7 @@ wss.on('connection', (ws) => {
|
|
|
447
435
|
return;
|
|
448
436
|
}
|
|
449
437
|
if (msg.type === 'start_session') {
|
|
450
|
-
const cwd = typeof msg.cwd === 'string' && msg.cwd.trim().length > 0
|
|
451
|
-
? resolve(msg.cwd)
|
|
452
|
-
: HOME_DIR;
|
|
438
|
+
const cwd = typeof msg.cwd === 'string' && msg.cwd.trim().length > 0 ? resolve(msg.cwd) : HOME_DIR;
|
|
453
439
|
const sessionFile = typeof msg.sessionFile === 'string' && msg.sessionFile.length > 0
|
|
454
440
|
? basename(msg.sessionFile)
|
|
455
441
|
: null;
|
|
@@ -479,13 +465,9 @@ wss.on('connection', (ws) => {
|
|
|
479
465
|
return;
|
|
480
466
|
}
|
|
481
467
|
const command = msg.command;
|
|
482
|
-
const isPromptLikeCommand = command
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
const hasImages = Array.isArray(command?.images) && command.images.length > 0;
|
|
486
|
-
if (isPromptLikeCommand &&
|
|
487
|
-
hasImages &&
|
|
488
|
-
managed.currentModelSupportsImages === false) {
|
|
468
|
+
const isPromptLikeCommand = command.type === 'prompt' || command.type === 'steer' || command.type === 'follow_up';
|
|
469
|
+
const hasImages = 'images' in command && Array.isArray(command.images) && command.images.length > 0;
|
|
470
|
+
if (isPromptLikeCommand && hasImages && managed.currentModelSupportsImages === false) {
|
|
489
471
|
sendToSocket(ws, {
|
|
490
472
|
type: 'error',
|
|
491
473
|
message: 'selected model does not support file attachments',
|
package/build/server/sessions.js
CHANGED
|
@@ -14,9 +14,7 @@ function cwdToSessionDir(cwd, agent) {
|
|
|
14
14
|
if (normalisedCwd === HOME_DIR ||
|
|
15
15
|
normalisedCwd.startsWith(`${HOME_DIR}/`) ||
|
|
16
16
|
normalisedCwd.startsWith(`${HOME_DIR}\\`)) {
|
|
17
|
-
const relative = normalisedCwd
|
|
18
|
-
.slice(HOME_DIR.length)
|
|
19
|
-
.replace(/^[/\\]/, '');
|
|
17
|
+
const relative = normalisedCwd.slice(HOME_DIR.length).replace(/^[/\\]/, '');
|
|
20
18
|
return `-${relative.replace(/[/\\:]/g, '-')}`;
|
|
21
19
|
}
|
|
22
20
|
}
|
|
@@ -54,13 +52,17 @@ export async function listSessions(opts) {
|
|
|
54
52
|
if (info)
|
|
55
53
|
results.push(info);
|
|
56
54
|
}
|
|
57
|
-
catch {
|
|
55
|
+
catch {
|
|
56
|
+
// unreadable session file — skip
|
|
57
|
+
}
|
|
58
58
|
}
|
|
59
59
|
if (results.length >= limit)
|
|
60
60
|
break;
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
catch {
|
|
63
|
+
catch {
|
|
64
|
+
// session directory does not exist or is unreadable
|
|
65
|
+
}
|
|
64
66
|
results.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
65
67
|
return results.slice(0, limit);
|
|
66
68
|
}
|
|
@@ -105,7 +107,9 @@ export async function readSessionMessages(filePath) {
|
|
|
105
107
|
usage: msg.usage,
|
|
106
108
|
});
|
|
107
109
|
}
|
|
108
|
-
catch {
|
|
110
|
+
catch {
|
|
111
|
+
// malformed JSON line — skip
|
|
112
|
+
}
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
finally {
|
|
@@ -163,7 +167,9 @@ async function readSessionHeader(filePath) {
|
|
|
163
167
|
}
|
|
164
168
|
}
|
|
165
169
|
}
|
|
166
|
-
catch {
|
|
170
|
+
catch {
|
|
171
|
+
// malformed JSON line — skip
|
|
172
|
+
}
|
|
167
173
|
}
|
|
168
174
|
}
|
|
169
175
|
finally {
|
|
@@ -173,10 +179,10 @@ async function readSessionHeader(filePath) {
|
|
|
173
179
|
if (!header)
|
|
174
180
|
return null;
|
|
175
181
|
return {
|
|
176
|
-
id: header.id,
|
|
182
|
+
id: typeof header.id === 'string' ? header.id : '',
|
|
177
183
|
file: basename(filePath),
|
|
178
|
-
cwd: header.cwd
|
|
179
|
-
timestamp: header.timestamp
|
|
184
|
+
cwd: typeof header.cwd === 'string' ? header.cwd : '',
|
|
185
|
+
timestamp: typeof header.timestamp === 'string' ? header.timestamp : '',
|
|
180
186
|
firstPrompt,
|
|
181
187
|
messageCount,
|
|
182
188
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|