Scaffolda tunn vertikal slice: pnpm-monorepo (shared/daemon/server/web)

- shared: typat WebSocket-protokoll (daemon/server/web som enda sanning)
- daemon: node-pty kör agenten (default gemini), strömmar pty över WS,
  auto-reconnect; fast pty-storlek
- server: WS-mux + token-auth + session-registry + headless-xterm scrollback
  (SerializeAddon) + routing av input till rätt session
- web: Vue 3 + Vite + xterm.js, session-lista, mobil-layout och
  fallback-tangentbord (piltangenter/Enter/Esc/Tab/Ctrl-C) som skickar
  escape-sekvenser rakt till pty:n -> styr sessionen även utan hooks
- workspace: pnpm-workspace.yaml (onlyBuiltDependencies: node-pty, esbuild),
  tsconfig.base, .npmrc (retry-inställningar), .env.example

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 14:28:04 +02:00
parent 0c75d94812
commit c58f73626e
24 changed files with 1173 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
{
"name": "@agent-helm/shared",
"version": "0.0.0",
"private": true,
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": "./src/index.ts"
},
"scripts": {
"build": "tsc --noEmit -p tsconfig.json",
"typecheck": "tsc --noEmit -p tsconfig.json"
},
"devDependencies": {
"typescript": "^5.7.0"
}
}

View File

@@ -0,0 +1,125 @@
/**
* Delat WebSocket-protokoll mellan daemon, server och frontend.
* Allt skickas som JSON-strängar. Typerna nedan är enda källan till sanning
* för meddelandeformaten — importeras av alla tre paketen.
*/
export type SessionStatus = "running" | "exited";
export interface SessionMeta {
sessionId: string;
title: string;
status: SessionStatus;
cols: number;
rows: number;
exitCode?: number;
}
/* ----------------------------- Daemon -> Server ---------------------------- */
export interface DaemonHello {
type: "daemon:hello";
token: string;
sessionId: string;
title: string;
cols: number;
rows: number;
}
export interface DaemonOutput {
type: "daemon:output";
/** Rå pty-output (utf8). */
data: string;
}
export interface DaemonExit {
type: "daemon:exit";
code: number;
}
export type DaemonMessage = DaemonHello | DaemonOutput | DaemonExit;
/* ----------------------------- Server -> Daemon ---------------------------- */
export interface ServerInput {
type: "server:input";
/** Text eller escape-sekvenser som ska skrivas till pty:ns stdin. */
data: string;
}
export type ServerToDaemon = ServerInput;
/* ---------------------------- Frontend -> Server --------------------------- */
export interface WebHello {
type: "web:hello";
token: string;
}
export interface WebSubscribe {
type: "web:subscribe";
sessionId: string;
}
export interface WebInput {
type: "web:input";
sessionId: string;
/** Text eller escape-sekvens (piltangenter, Enter, Ctrl-C ...). */
data: string;
}
export type WebMessage = WebHello | WebSubscribe | WebInput;
/* ---------------------------- Server -> Frontend --------------------------- */
export interface ServerWelcome {
type: "server:welcome";
}
export interface ServerSessions {
type: "server:sessions";
sessions: SessionMeta[];
}
export interface ServerSnapshot {
type: "server:snapshot";
sessionId: string;
/** Serialiserad terminal-state (scrollback) så en ny klient ser var den är. */
data: string;
meta: SessionMeta;
}
export interface ServerOutput {
type: "server:output";
sessionId: string;
data: string;
}
export interface ServerSessionExit {
type: "server:exit";
sessionId: string;
code: number;
}
export interface ServerError {
type: "server:error";
message: string;
}
export type ServerToWeb =
| ServerWelcome
| ServerSessions
| ServerSnapshot
| ServerOutput
| ServerSessionExit
| ServerError;
/* --------------------------------- Helpers --------------------------------- */
export function parseMessage<T>(raw: string): T | null {
try {
return JSON.parse(raw) as T;
} catch {
return null;
}
}

View File

@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"lib": ["ES2022"],
"types": []
},
"include": ["src"]
}