fix(pi): add snap node compatibility diagnostics and documentation

- Added detailed error logging to detect snap node compatibility issues
- When Pi process exits with code 1, log helpful diagnostic info including node path
- Add hint to check for snap node and reinstall via apt/nvm
- Create SNAP_NODE_COMPATIBILITY.md with full troubleshooting guide
- Document root cause: snap node has file descriptor incompatibility with Bun.spawn stdin pipes
- Provide clear installation instructions for NodeSource and nvm alternatives
This commit is contained in:
2026-03-04 01:45:36 +00:00
parent 72d1341cbc
commit ef13f96d36
34 changed files with 2394 additions and 1466 deletions
+29 -6
View File
@@ -30,6 +30,7 @@ piRestRouter.get('/pi/models', async (ctx: Context) => {
providerNames[`officer-local-${lp.id}`] = lp.name;
}
logger.info('Models endpoint', { count: models.length, providers: [...new Set(models.map((m) => m.provider))] });
return ctx.json({ models, providerNames, hostHome: process.env.HOME ?? '' });
} catch (err) {
logger.error('Failed to list models', { error: String(err) });
@@ -51,7 +52,9 @@ piRestRouter.post('/pi/sessions', async (ctx: Context) => {
const body = await ctx.req.json().catch(() => ({}));
const userHome = getHomeDir(user.email);
const filterCwd = body.cwd ? resolveBaseCwd(user.email, body.cwdRoot, body.cwd) : null;
const contextFilter = body.context ? { context: body.context as string, contextId: body.contextId as string | undefined } : undefined;
const contextFilter = body.context
? { context: body.context as string, contextId: body.contextId as string | undefined }
: undefined;
try {
let sessions = await storage.listUserSessions(userHome, contextFilter);
@@ -119,6 +122,14 @@ piRestRouter.get('/pi/sessions/:sessionId', async (ctx: Context) => {
}
});
/**
* PUT /api/pi/sessions/:sessionId/messages
* Client-side message save — no-op, sessions are persisted server-side via WebSocket events
*/
piRestRouter.put('/pi/sessions/:sessionId/messages', async (ctx: Context) => {
return ctx.json({ success: true });
});
/**
* PATCH /api/pi/sessions/:sessionId
* Update session metadata (e.g., rename)
@@ -162,9 +173,14 @@ piRestRouter.patch('/pi/sessions/:sessionId', async (ctx: Context) => {
}
}
const updatedMeta = await storage.updateSessionMeta(userHome, sessionId, {
title: body.title,
}, groupSlug);
const updatedMeta = await storage.updateSessionMeta(
userHome,
sessionId,
{
title: body.title,
},
groupSlug,
);
return ctx.json({
success: true,
@@ -246,7 +262,9 @@ piRestRouter.delete('/pi/sessions', async (ctx: Context) => {
}
const body = await ctx.req.json().catch(() => ({}));
const contextFilter = body.context ? { context: body.context as string, contextId: body.contextId as string | undefined } : undefined;
const contextFilter = body.context
? { context: body.context as string, contextId: body.contextId as string | undefined }
: undefined;
const userHome = getHomeDir(user.email);
try {
@@ -260,7 +278,12 @@ piRestRouter.delete('/pi/sessions', async (ctx: Context) => {
// Skip sessions that fail to delete
}
}
logger.info('Bulk deleted sessions', { email: user.email, deleted, total: sessions.length, context: contextFilter?.context });
logger.info('Bulk deleted sessions', {
email: user.email,
deleted,
total: sessions.length,
context: contextFilter?.context,
});
return ctx.json({ success: true, deleted });
} catch (err) {
logger.error('Failed to bulk delete sessions', { email: user.email, error: String(err) });