feat: server-first terminal creation, broadcast-only WS clients
- Add POST /create-terminal endpoint with MAX_TERMINALS=5 limit - Server creates PTY, runs command, registers and broadcasts atomically - Frontend startTerminal() calls server first, connects in reconnect mode - Remove registerTerminalOnServer() — server handles registration - Separate broadcast-only WS clients from PTY clients (no phantom "main" PTY) - All broadcast functions use broadcastToAll() helper - Fix resume existing flow to create terminal with --resume flag
This commit is contained in:
@@ -40,6 +40,9 @@ const {
|
||||
switchAgent,
|
||||
selectSession,
|
||||
createNewSession,
|
||||
startTerminal,
|
||||
parkCurrentTerminal,
|
||||
fetchSessionContent,
|
||||
switchToTerminal,
|
||||
closeTerminal,
|
||||
disconnectRealtime,
|
||||
@@ -468,7 +471,11 @@ async function handleModalResume(sessionId: string, agent: AgentName) {
|
||||
if (agent !== selectedAgent.value) {
|
||||
await switchAgent(agent)
|
||||
}
|
||||
selectSession(sessionId)
|
||||
// Load transcript + create terminal with --resume
|
||||
parkCurrentTerminal()
|
||||
selectedSessionId.value = sessionId
|
||||
await fetchSessionContent(sessionId)
|
||||
await startTerminal(sessionId)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
@@ -92,30 +92,6 @@ export function useTranscriptDebug() {
|
||||
|
||||
// ── Server registry HTTP helpers ──
|
||||
|
||||
async function registerTerminalOnServer(
|
||||
ephemeralSessionId: string,
|
||||
transcriptSessionId: string,
|
||||
agent: AgentName,
|
||||
label: string,
|
||||
command: string
|
||||
) {
|
||||
try {
|
||||
await fetch(terminalApiUrl('/register-terminal'), {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
ephemeralSessionId,
|
||||
transcriptSessionId,
|
||||
agent,
|
||||
label,
|
||||
command,
|
||||
createdAt: new Date().toISOString()
|
||||
})
|
||||
})
|
||||
// Server broadcasts change → all clients pick it up via WS
|
||||
} catch { /* best effort */ }
|
||||
}
|
||||
|
||||
async function updateTerminalOnServer(
|
||||
ephemeralSessionId: string,
|
||||
updates: { transcriptSessionId?: string; label?: string }
|
||||
@@ -150,26 +126,44 @@ export function useTranscriptDebug() {
|
||||
return sessionId.slice(0, 12) + '...'
|
||||
}
|
||||
|
||||
function startTerminal(sessionId?: string) {
|
||||
async function startTerminal(sessionId?: string) {
|
||||
const key = sessionId || '__new__'
|
||||
const cmd = sessionId
|
||||
? `${AGENT_CMD[selectedAgent.value]} --resume "${sessionId}"`
|
||||
: AGENT_CMD[selectedAgent.value]
|
||||
|
||||
const term = useEphemeralTerminal(cmd)
|
||||
localTerminals.set(key, term)
|
||||
activeTerminalSessionId.value = key
|
||||
console.log(`[TranscriptDebug] startTerminal called — key=${key} cmd=${cmd} url=${terminalApiUrl('/create-terminal')}`)
|
||||
|
||||
term.start()
|
||||
try {
|
||||
// Server creates PTY, runs command, registers in registry, broadcasts
|
||||
const res = await fetch(terminalApiUrl('/create-terminal'), {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
agent: selectedAgent.value,
|
||||
transcriptSessionId: key,
|
||||
label: sessionId ? getSessionLabel(sessionId) : 'New session',
|
||||
command: cmd
|
||||
})
|
||||
})
|
||||
|
||||
// Register on server
|
||||
registerTerminalOnServer(
|
||||
term.ephemeralSessionId,
|
||||
key,
|
||||
selectedAgent.value,
|
||||
sessionId ? getSessionLabel(sessionId) : 'New session',
|
||||
cmd
|
||||
)
|
||||
if (!res.ok) {
|
||||
const err = await res.json().catch(() => ({ error: 'Unknown error' }))
|
||||
console.error(`[TranscriptDebug] Failed to create terminal: ${err.error}`)
|
||||
return
|
||||
}
|
||||
|
||||
const { ephemeralSessionId } = await res.json()
|
||||
|
||||
// Connect to the existing PTY in reconnect mode
|
||||
const term = useEphemeralTerminal(cmd, ephemeralSessionId)
|
||||
localTerminals.set(key, term)
|
||||
activeTerminalSessionId.value = key
|
||||
|
||||
term.start()
|
||||
} catch (e: any) {
|
||||
console.error('[TranscriptDebug] Terminal creation failed:', e.message)
|
||||
}
|
||||
}
|
||||
|
||||
function parkCurrentTerminal() {
|
||||
@@ -281,7 +275,7 @@ export function useTranscriptDebug() {
|
||||
pendingPrompt.value = initialPrompt?.trim() || null
|
||||
|
||||
awaitingNewSession.value = true
|
||||
startTerminal() // no sessionId → brand new session
|
||||
await startTerminal() // no sessionId → brand new session
|
||||
}
|
||||
|
||||
// ── WebSocket realtime ──
|
||||
@@ -949,6 +943,9 @@ export function useTranscriptDebug() {
|
||||
switchAgent,
|
||||
selectSession,
|
||||
createNewSession,
|
||||
startTerminal,
|
||||
parkCurrentTerminal,
|
||||
fetchSessionContent,
|
||||
switchToTerminal,
|
||||
closeTerminal,
|
||||
connectRealtime,
|
||||
|
||||
Reference in New Issue
Block a user