let the pty sidecar decide what shell it runs
officer built the whole PtyInitConfig: it read the owner's SHELL (defaulting to /bin/zsh), added `-i`, read their HOME, expanded `~` against it, and hardcoded `host: true`. none of that is a proxy's business — the sidecar is the process that calls pty.spawn, so it is the one that should know what to spawn and where. the config now carries only what the bridge actually knows: sessionId, the folder the panel was opened on, and the client's cols/rows. shell, args, home and cwd resolution moved into the sidecar. home comes from HOME_DIR ?? HOME, mirroring data-path.ts:getOwnerHomeDir — terminal was the one host-executing surface reading process.env.HOME directly, which is identical here and divergent anywhere HOME_DIR is set to something else. deleted the bwrap sandbox branch rather than moving it. it was selected by `config.host`, which officer hardcoded to true, so it never ran — and it expected `shell` to contain a fully-built bwrap command that nothing on either side ever built. it could not have worked. a terminal here is the owner's own shell on the owner's own machine by design (platform/CLAUDE.md), so there is no jail to preserve. its ensureUserFiles half duplicated api/users/provision.ts:seedShellConfigs, which is the live seeder of those same templates and stays. also deleted the 'cwd' handler that turned a message into `cd <path>\r` typed at the shell. no frontend has ever sent that message — the browser composes its own cd — so it was unreachable, and synthesizing keystrokes is not something a relay should do. the integration test pins SHELL and HOME_DIR now that the sidecar reads them, and asserts the shell starts in the resolved `~` rather than officer having resolved it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import type { ServerWebSocket } from 'bun';
|
||||
import { join } from 'node:path';
|
||||
import { sendPtyCommand, sendPtyCommandAsync, on, isTerminalConnected } from '@@/sidecar-registry';
|
||||
import type { PtyInitConfig } from '../../sidecar/protocol';
|
||||
|
||||
@@ -34,13 +33,6 @@ const sendOutput = (ws: ServerWebSocket<WSData>, data: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const resolveCwd = (home: string, cwd?: string) => {
|
||||
if (!cwd || cwd === '~') return home;
|
||||
if (cwd.startsWith('~/')) return join(home, cwd.slice(2));
|
||||
if (cwd.startsWith('/')) return cwd;
|
||||
return home;
|
||||
};
|
||||
|
||||
export const terminalWebsocket = {
|
||||
async open(ws: ServerWebSocket<WSData>) {
|
||||
const { email, username } = ws.data;
|
||||
@@ -54,17 +46,10 @@ export const terminalWebsocket = {
|
||||
|
||||
const sessionId = ws.data.sessionId ?? `host-${ws.data.userId}`;
|
||||
|
||||
// The server owner is the only account, so the terminal is always a plain host shell.
|
||||
const config: PtyInitConfig = {
|
||||
sessionId,
|
||||
host: true,
|
||||
shell: { command: process.env.SHELL ?? '/bin/zsh', args: ['-i'] },
|
||||
cwd: resolveCwd(process.env.HOME!, ws.data.cwd),
|
||||
homeDir: process.env.HOME!,
|
||||
userLabel: email,
|
||||
cols: ws.data.cols,
|
||||
rows: ws.data.rows,
|
||||
};
|
||||
// Everything this bridge knows: which session, which folder the panel was opened on, and how big the
|
||||
// client's viewport is. The shell, its arguments and the home directory are the sidecar's — it is the
|
||||
// process that spawns them, and officer has no business reading the owner's SHELL and HOME to guess.
|
||||
const config: PtyInitConfig = { sessionId, cwd: ws.data.cwd, cols: ws.data.cols, rows: ws.data.rows };
|
||||
|
||||
// The sidecar emits one global stream, so each frame is filtered down to this session and relabelled.
|
||||
const relay = (event: 'pty:output' | 'pty:replay' | 'pty:exit', clientType: string) =>
|
||||
@@ -118,16 +103,9 @@ export const terminalWebsocket = {
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'cwd':
|
||||
if (msg.path) {
|
||||
sendPtyCommand({
|
||||
type: 'pty:input',
|
||||
id: nextId(),
|
||||
sessionId: session.sessionId,
|
||||
data: `cd ${JSON.stringify(msg.path)}\r`,
|
||||
});
|
||||
}
|
||||
break;
|
||||
// There was a 'cwd' case here that typed `cd <path>\r` into the user's shell. No frontend sends
|
||||
// that message — the browser composes its own `cd` (Terminal.tsx / CommandTerminalWrapper.tsx) —
|
||||
// so it was unreachable, and synthesizing keystrokes is not a thing a proxy should do.
|
||||
}
|
||||
} catch {
|
||||
// ignore malformed messages
|
||||
|
||||
Reference in New Issue
Block a user