fix(app): terminal issues

This commit is contained in:
Adam
2026-02-26 15:50:15 -06:00
parent a0b3bbffd5
commit 8c484a05b8

View File

@@ -23,60 +23,6 @@ export namespace Pty {
close: (code?: number, reason?: string) => void
}
type Subscriber = {
id: number
token: unknown
}
const sockets = new WeakMap<object, number>()
const owners = new WeakMap<object, string>()
let socketCounter = 0
const tagSocket = (ws: Socket) => {
if (!ws || typeof ws !== "object") return
const next = (socketCounter = (socketCounter + 1) % Number.MAX_SAFE_INTEGER)
sockets.set(ws, next)
return next
}
const token = (ws: Socket) => {
const data = ws.data
if (data === undefined) return
if (data === null) return
if (typeof data !== "object") return data
const id = (data as { connId?: unknown }).connId
if (typeof id === "number" || typeof id === "string") return id
const href = (data as { href?: unknown }).href
if (typeof href === "string") return href
const url = (data as { url?: unknown }).url
if (typeof url === "string") return url
if (url && typeof url === "object") {
const href = (url as { href?: unknown }).href
if (typeof href === "string") return href
return url
}
const events = (data as { events?: unknown }).events
if (typeof events === "number" || typeof events === "string") return events
if (events && typeof events === "object") {
const id = (events as { connId?: unknown }).connId
if (typeof id === "number" || typeof id === "string") return id
const id2 = (events as { connection?: unknown }).connection
if (typeof id2 === "number" || typeof id2 === "string") return id2
const id3 = (events as { id?: unknown }).id
if (typeof id3 === "number" || typeof id3 === "string") return id3
return events
}
return data
}
// WebSocket control frame: 0x00 + UTF-8 JSON.
const meta = (cursor: number) => {
const json = JSON.stringify({ cursor })
@@ -141,7 +87,7 @@ export namespace Pty {
buffer: string
bufferCursor: number
cursor: number
subscribers: Map<Socket, Subscriber>
subscribers: Map<unknown, Socket>
}
const state = Instance.state(
@@ -151,9 +97,9 @@ export namespace Pty {
try {
session.process.kill()
} catch {}
for (const ws of session.subscribers.keys()) {
for (const [key, ws] of session.subscribers.entries()) {
try {
ws.close()
if (ws.data === key) ws.close()
} catch {
// ignore
}
@@ -224,26 +170,21 @@ export namespace Pty {
ptyProcess.onData((chunk) => {
session.cursor += chunk.length
for (const [ws, sub] of session.subscribers) {
for (const [key, ws] of session.subscribers.entries()) {
if (ws.readyState !== 1) {
session.subscribers.delete(ws)
session.subscribers.delete(key)
continue
}
if (typeof ws === "object" && sockets.get(ws) !== sub.id) {
session.subscribers.delete(ws)
continue
}
if (token(ws) !== sub.token) {
session.subscribers.delete(ws)
if (ws.data !== key) {
session.subscribers.delete(key)
continue
}
try {
ws.send(chunk)
} catch {
session.subscribers.delete(ws)
session.subscribers.delete(key)
}
}
@@ -256,9 +197,9 @@ export namespace Pty {
ptyProcess.onExit(({ exitCode }) => {
log.info("session exited", { id, exitCode })
session.info.status = "exited"
for (const ws of session.subscribers.keys()) {
for (const [key, ws] of session.subscribers.entries()) {
try {
ws.close()
if (ws.data === key) ws.close()
} catch {
// ignore
}
@@ -291,9 +232,9 @@ export namespace Pty {
try {
session.process.kill()
} catch {}
for (const ws of session.subscribers.keys()) {
for (const [key, ws] of session.subscribers.entries()) {
try {
ws.close()
if (ws.data === key) ws.close()
} catch {
// ignore
}
@@ -325,23 +266,16 @@ export namespace Pty {
}
log.info("client connected to session", { id })
const socketId = tagSocket(ws)
if (socketId === undefined) {
ws.close()
return
}
// Use ws.data as the unique key for this connection lifecycle.
// If ws.data is undefined, fallback to ws object.
const connectionKey = ws.data && typeof ws.data === "object" ? ws.data : ws
const previous = owners.get(ws)
if (previous && previous !== id) {
state().get(previous)?.subscribers.delete(ws)
}
owners.set(ws, id)
session.subscribers.set(ws, { id: socketId, token: token(ws) })
// Optionally cleanup if the key somehow exists
session.subscribers.delete(connectionKey)
session.subscribers.set(connectionKey, ws)
const cleanup = () => {
session.subscribers.delete(ws)
if (owners.get(ws) === id) owners.delete(ws)
session.subscribers.delete(connectionKey)
}
const start = session.bufferCursor