adhdev 0.7.28 → 0.7.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +303 -258
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +304 -259
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/vendor/session-host-daemon/index.js +39 -29
- package/vendor/session-host-daemon/index.js.map +1 -1
- package/vendor/session-host-daemon/index.mjs +39 -29
- package/vendor/session-host-daemon/index.mjs.map +1 -1
package/package.json
CHANGED
|
@@ -173,7 +173,6 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
173
173
|
this.storage = new SessionHostStorage({ appName: options.appName || "adhdev" });
|
|
174
174
|
}
|
|
175
175
|
async start() {
|
|
176
|
-
this.restorePersistedRuntimes();
|
|
177
176
|
if (this.endpoint.kind === "unix") {
|
|
178
177
|
try {
|
|
179
178
|
fs2.unlinkSync(this.endpoint.path);
|
|
@@ -196,6 +195,13 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
196
195
|
this.ipcServer?.listen(this.endpoint.path);
|
|
197
196
|
});
|
|
198
197
|
this.emit("log", `session host endpoint ready: ${this.endpoint.path}`);
|
|
198
|
+
setTimeout(() => {
|
|
199
|
+
try {
|
|
200
|
+
this.restorePersistedRuntimes();
|
|
201
|
+
} catch (error) {
|
|
202
|
+
this.emit("log", `session host restore failed: ${error?.message || String(error)}`);
|
|
203
|
+
}
|
|
204
|
+
}, 0);
|
|
199
205
|
}
|
|
200
206
|
async stop() {
|
|
201
207
|
this.flushAllPersistence();
|
|
@@ -386,6 +392,7 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
386
392
|
}
|
|
387
393
|
restorePersistedRuntimes() {
|
|
388
394
|
const states = this.storage.loadAll();
|
|
395
|
+
const runtimesToResume = [];
|
|
389
396
|
for (const persisted of states) {
|
|
390
397
|
const wasLiveRuntime = !["stopped", "failed"].includes(persisted.record.lifecycle);
|
|
391
398
|
const recoveredRecord = {
|
|
@@ -403,35 +410,38 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
403
410
|
this.registry.restoreSession(recoveredRecord, persisted.snapshot);
|
|
404
411
|
this.storage.save(recoveredRecord, persisted.snapshot);
|
|
405
412
|
if (wasLiveRuntime) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
413
|
+
runtimesToResume.push({ persisted, recoveredRecord });
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
for (const { persisted, recoveredRecord } of runtimesToResume) {
|
|
417
|
+
try {
|
|
418
|
+
const resumed = this.startRuntime(
|
|
419
|
+
recoveredRecord,
|
|
420
|
+
this.buildPayloadFromRecord(recoveredRecord),
|
|
421
|
+
"session_resumed"
|
|
422
|
+
);
|
|
423
|
+
const resumedMeta = {
|
|
424
|
+
...resumed.meta || {},
|
|
425
|
+
restoredFromStorage: true,
|
|
426
|
+
runtimeRecoveryState: "auto_resumed"
|
|
427
|
+
};
|
|
428
|
+
this.registry.restoreSession(
|
|
429
|
+
{ ...resumed, meta: resumedMeta },
|
|
430
|
+
this.registry.getSnapshot(resumed.sessionId)
|
|
431
|
+
);
|
|
432
|
+
this.persistNow(resumed.sessionId);
|
|
433
|
+
} catch (error) {
|
|
434
|
+
const interrupted = this.registry.setLifecycle(recoveredRecord.sessionId, "interrupted");
|
|
435
|
+
this.registry.restoreSession({
|
|
436
|
+
...interrupted,
|
|
437
|
+
meta: {
|
|
438
|
+
...interrupted.meta || {},
|
|
414
439
|
restoredFromStorage: true,
|
|
415
|
-
runtimeRecoveryState: "
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
);
|
|
421
|
-
this.persistNow(resumed.sessionId);
|
|
422
|
-
} catch (error) {
|
|
423
|
-
const interrupted = this.registry.setLifecycle(recoveredRecord.sessionId, "interrupted");
|
|
424
|
-
this.registry.restoreSession({
|
|
425
|
-
...interrupted,
|
|
426
|
-
meta: {
|
|
427
|
-
...interrupted.meta || {},
|
|
428
|
-
restoredFromStorage: true,
|
|
429
|
-
runtimeRecoveryState: "resume_failed",
|
|
430
|
-
runtimeRecoveryError: error?.message || String(error)
|
|
431
|
-
}
|
|
432
|
-
}, persisted.snapshot);
|
|
433
|
-
this.persistNow(recoveredRecord.sessionId);
|
|
434
|
-
}
|
|
440
|
+
runtimeRecoveryState: "resume_failed",
|
|
441
|
+
runtimeRecoveryError: error?.message || String(error)
|
|
442
|
+
}
|
|
443
|
+
}, persisted.snapshot);
|
|
444
|
+
this.persistNow(recoveredRecord.sessionId);
|
|
435
445
|
}
|
|
436
446
|
}
|
|
437
447
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/server.ts","../src/runtime.ts","../src/storage.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { randomUUID } from 'crypto';\nimport {\n SessionHostClient,\n formatRuntimeOwner,\n getDefaultSessionHostEndpoint,\n resolveRuntimeRecord,\n type SessionHostEvent,\n type SessionHostRecord,\n} from '@adhdev/session-host-core';\nimport { SessionHostServer } from './server.js';\n\nexport { SessionHostServer } from './server.js';\nexport type { SessionHostServerOptions } from './server.js';\n\nconst SESSION_HOST_APP_NAME = process.env.ADHDEV_SESSION_HOST_NAME || 'adhdev';\n\nfunction parseArgs(argv: string[]) {\n const [command, ...rest] = argv;\n const readOnly = rest.includes('--read-only');\n const takeover = rest.includes('--takeover');\n const showAll = rest.includes('--all');\n const positional = rest.filter((arg) => arg !== '--read-only' && arg !== '--takeover' && arg !== '--all');\n return {\n command: command || 'serve',\n positional,\n readOnly,\n takeover,\n showAll,\n };\n}\n\nasync function runServer(): Promise<void> {\n const server = new SessionHostServer({ appName: SESSION_HOST_APP_NAME });\n await server.start();\n\n process.on('SIGINT', async () => {\n await server.stop();\n process.exit(0);\n });\n\n process.on('SIGTERM', async () => {\n await server.stop();\n process.exit(0);\n });\n\n // Keep the host alive; IPC transport wiring comes next.\n await new Promise<void>(() => {});\n}\n\nasync function listRuntimes(showAll = false): Promise<void> {\n const client = new SessionHostClient({ endpoint: getDefaultSessionHostEndpoint(SESSION_HOST_APP_NAME) });\n try {\n const response = await client.request<SessionHostRecord[]>({\n type: 'list_sessions',\n payload: {},\n });\n if (!response.success) {\n throw new Error(response.error || 'Failed to list runtimes');\n }\n const runtimes = (response.result || []).filter((runtime: SessionHostRecord) => showAll || runtime.lifecycle !== 'stopped');\n if (runtimes.length === 0) {\n console.log('No runtimes.');\n return;\n }\n console.log('runtimeKey\\tlifecycle\\towner\\tworkspace\\tid\\tdisplayName');\n for (const runtime of runtimes) {\n console.log([\n runtime.runtimeKey,\n runtime.lifecycle,\n formatRuntimeOwner(runtime),\n runtime.workspaceLabel,\n runtime.sessionId,\n runtime.displayName,\n ].join('\\t'));\n }\n } finally {\n await client.close().catch(() => {});\n }\n}\n\nasync function attachRuntime(target: string, readOnly = false, takeover = false): Promise<void> {\n const client = new SessionHostClient({ endpoint: getDefaultSessionHostEndpoint(SESSION_HOST_APP_NAME) });\n const clientId = `local-terminal-${process.pid}-${randomUUID().slice(0, 8)}`;\n let lastSeq = 0;\n let restoredRawMode = false;\n let runtimeId = '';\n let localReadOnly = readOnly;\n\n const cleanup = async () => {\n process.stdout.off('resize', handleResize);\n process.stdin.off('data', handleInput);\n process.stdin.pause();\n if (process.stdin.isTTY && restoredRawMode) {\n process.stdin.setRawMode(false);\n }\n await client.request({\n type: 'release_write',\n payload: {\n sessionId: runtimeId,\n clientId,\n },\n }).catch(() => ({ success: false }));\n await client.request({\n type: 'detach_session',\n payload: {\n sessionId: runtimeId,\n clientId,\n },\n }).catch(() => ({ success: false }));\n await client.close().catch(() => {});\n };\n\n const handleResize = () => {\n void client.request({\n type: 'resize_session',\n payload: {\n sessionId: runtimeId,\n cols: process.stdout.columns || 120,\n rows: process.stdout.rows || 40,\n },\n }).catch(() => ({ success: false }));\n };\n\n const sendInputWithTakeover = async (data: string) => {\n let response = await client.request({\n type: 'send_input',\n payload: {\n sessionId: runtimeId,\n clientId,\n data,\n },\n });\n if (!response.success && response.error?.startsWith('Write owned by ')) {\n const ownerResponse = await client.request<SessionHostRecord>({\n type: 'acquire_write',\n payload: {\n sessionId: runtimeId,\n clientId,\n ownerType: 'user',\n force: true,\n },\n });\n if (ownerResponse.success && ownerResponse.result) {\n response = await client.request({\n type: 'send_input',\n payload: {\n sessionId: runtimeId,\n clientId,\n data,\n },\n });\n if (response.success) {\n process.stderr.write(`Took control of ${ownerResponse.result.runtimeKey}.\\n`);\n }\n }\n }\n return response;\n };\n\n const handleInput = (chunk: Buffer) => {\n if (!localReadOnly && chunk.length === 1 && chunk[0] === 0x1d) {\n void cleanup().finally(() => process.exit(0));\n return;\n }\n if (localReadOnly) return;\n void sendInputWithTakeover(chunk.toString('utf8')).catch(() => ({ success: false }));\n };\n\n try {\n if (readOnly && takeover) {\n throw new Error('Use either --read-only or --takeover, not both');\n }\n\n const listResponse = await client.request<SessionHostRecord[]>({\n type: 'list_sessions',\n payload: {},\n });\n if (!listResponse.success || !listResponse.result) {\n throw new Error(listResponse.error || 'Failed to list runtimes');\n }\n let runtimeRecord = resolveRuntimeRecord(listResponse.result, target);\n runtimeId = runtimeRecord.sessionId;\n\n if (runtimeRecord.lifecycle === 'interrupted' && !readOnly) {\n const resumeResponse = await client.request<SessionHostRecord>({\n type: 'resume_session',\n payload: {\n sessionId: runtimeId,\n },\n });\n if (resumeResponse.success && resumeResponse.result) {\n runtimeRecord = resumeResponse.result;\n } else {\n process.stderr.write(\n `Runtime ${runtimeRecord.runtimeKey} could not be resumed automatically: ${resumeResponse.error || 'unknown error'}\\n`,\n );\n }\n }\n\n let effectiveReadOnly = readOnly;\n if (!effectiveReadOnly && runtimeRecord.writeOwner && runtimeRecord.writeOwner.clientId !== clientId && !takeover) {\n process.stderr.write(\n `Runtime ${runtimeRecord.runtimeKey} is currently owned by ${runtimeRecord.writeOwner.clientId}; first input will take control here.\\n`,\n );\n }\n localReadOnly = effectiveReadOnly;\n\n const attachResponse = await client.request<SessionHostRecord>({\n type: 'attach_session',\n payload: {\n sessionId: runtimeId,\n clientId,\n clientType: 'local-terminal',\n readOnly: effectiveReadOnly,\n },\n });\n if (!attachResponse.success) {\n throw new Error(attachResponse.error || `Failed to attach runtime ${runtimeId}`);\n }\n const attachedRecord = attachResponse.result || null;\n\n if (!effectiveReadOnly && takeover) {\n const ownerResponse = await client.request<SessionHostRecord>({\n type: 'acquire_write',\n payload: {\n sessionId: runtimeId,\n clientId,\n ownerType: 'user',\n force: takeover,\n },\n });\n if (!ownerResponse.success) {\n throw new Error(ownerResponse.error || `Failed to acquire write owner for runtime ${runtimeId}`);\n }\n }\n\n const snapshotResponse = await client.request<{ seq: number; text: string; truncated: boolean }>({\n type: 'get_snapshot',\n payload: { sessionId: runtimeId },\n });\n if (!snapshotResponse.success) {\n throw new Error(snapshotResponse.error || `Failed to read runtime snapshot ${runtimeId}`);\n }\n lastSeq = snapshotResponse.result?.seq || 0;\n if (snapshotResponse.result?.text) {\n process.stdout.write(snapshotResponse.result.text);\n }\n if (attachedRecord?.lifecycle === 'stopped' || attachedRecord?.lifecycle === 'failed' || attachedRecord?.lifecycle === 'interrupted') {\n process.stderr.write(`Runtime ${attachedRecord.runtimeKey} is already ${attachedRecord.lifecycle}. Detached after snapshot.\\n`);\n await cleanup();\n return;\n }\n\n const stopSignals = ['SIGINT', 'SIGTERM', 'SIGHUP'] as const;\n const signalHandlers = stopSignals.map((signal) => {\n const handler = () => {\n void cleanup().finally(() => process.exit(0));\n };\n process.on(signal, handler);\n return { signal, handler };\n });\n\n const unsubscribe = client.onEvent((event: SessionHostEvent) => {\n if (event.sessionId !== runtimeId) return;\n if (event.type === 'session_output') {\n if (event.seq <= lastSeq) return;\n lastSeq = event.seq;\n process.stdout.write(event.data);\n return;\n }\n if (event.type === 'session_exit') {\n void cleanup().finally(() => {\n for (const { signal, handler } of signalHandlers) {\n process.off(signal, handler);\n }\n unsubscribe();\n process.exit(event.exitCode ?? 0);\n });\n }\n });\n\n process.stdout.on('resize', handleResize);\n process.stdin.on('data', handleInput);\n process.stdin.resume();\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n restoredRawMode = true;\n }\n handleResize();\n if (!effectiveReadOnly) {\n process.stderr.write(`Attached to runtime ${attachedRecord?.runtimeKey || runtimeId}. Press Ctrl+] to detach.\\n`);\n } else {\n process.stderr.write(`Attached to runtime ${attachedRecord?.runtimeKey || runtimeId} (read-only).\\n`);\n }\n await new Promise<void>(() => {});\n } catch (error) {\n await cleanup().catch(() => {});\n throw error;\n }\n}\n\nasync function main(): Promise<void> {\n const { command, positional, readOnly, takeover, showAll } = parseArgs(process.argv.slice(2));\n if (command === 'serve') {\n await runServer();\n return;\n }\n if (command === 'list') {\n await listRuntimes(showAll);\n return;\n }\n if (command === 'attach') {\n const target = positional[0];\n if (!target) {\n throw new Error('runtime target is required: adhdev-sessiond attach <runtimeId|runtimeKey>');\n }\n await attachRuntime(target, readOnly, takeover);\n return;\n }\n if (command === 'resume') {\n const target = positional[0];\n if (!target) {\n throw new Error('runtime target is required: adhdev-sessiond resume <runtimeId|runtimeKey>');\n }\n const client = new SessionHostClient({ endpoint: getDefaultSessionHostEndpoint(SESSION_HOST_APP_NAME) });\n try {\n const listResponse = await client.request<SessionHostRecord[]>({ type: 'list_sessions', payload: {} });\n if (!listResponse.success || !listResponse.result) {\n throw new Error(listResponse.error || 'Failed to list runtimes');\n }\n const runtimeRecord = resolveRuntimeRecord(listResponse.result, target);\n const resumeResponse = await client.request<SessionHostRecord>({\n type: 'resume_session',\n payload: {\n sessionId: runtimeRecord.sessionId,\n },\n });\n if (!resumeResponse.success || !resumeResponse.result) {\n throw new Error(resumeResponse.error || `Failed to resume runtime ${runtimeRecord.runtimeKey}`);\n }\n console.log(`Resumed ${resumeResponse.result.runtimeKey} (${resumeResponse.result.sessionId})`);\n } finally {\n await client.close().catch(() => {});\n }\n return;\n }\n throw new Error(`Unknown command: ${command}`);\n}\n\nif (require.main === module) {\n void main().catch((error) => {\n console.error(error instanceof Error ? error.message : error);\n process.exit(1);\n });\n}\n","import { EventEmitter } from 'events';\nimport * as fs from 'fs';\nimport * as net from 'net';\nimport {\n SessionHostRegistry,\n createLineParser,\n createResponseEnvelope,\n getDefaultSessionHostEndpoint,\n writeEnvelope,\n} from '@adhdev/session-host-core';\nimport type {\n CreateSessionPayload,\n SessionAttachedClient,\n SessionHostEndpoint,\n SessionHostEvent,\n SessionHostRecord,\n SessionHostRequestEnvelope,\n SessionHostRequest,\n SessionHostResponse,\n} from '@adhdev/session-host-core';\nimport { PtySessionRuntime } from './runtime.js';\nimport { SessionHostStorage } from './storage.js';\n\nexport interface SessionHostServerOptions {\n endpoint?: SessionHostEndpoint;\n appName?: string;\n}\n\nexport class SessionHostServer extends EventEmitter {\n readonly endpoint: SessionHostEndpoint;\n readonly registry = new SessionHostRegistry();\n private runtimes = new Map<string, PtySessionRuntime>();\n private readonly storage: SessionHostStorage;\n private ipcServer: net.Server | null = null;\n private sockets = new Set<net.Socket>();\n private persistTimers = new Map<string, NodeJS.Timeout>();\n\n constructor(options: SessionHostServerOptions = {}) {\n super();\n this.endpoint = options.endpoint || getDefaultSessionHostEndpoint(options.appName || 'adhdev');\n this.storage = new SessionHostStorage({ appName: options.appName || 'adhdev' });\n }\n\n async start(): Promise<void> {\n this.restorePersistedRuntimes();\n if (this.endpoint.kind === 'unix') {\n try {\n fs.unlinkSync(this.endpoint.path);\n } catch {\n // noop\n }\n }\n\n this.ipcServer = net.createServer((socket) => {\n this.sockets.add(socket);\n socket.on('close', () => {\n this.sockets.delete(socket);\n });\n socket.on('data', createLineParser((envelope) => {\n if (envelope.kind !== 'request') return;\n void this.handleIncomingRequest(socket, envelope);\n }));\n });\n\n await new Promise<void>((resolve, reject) => {\n this.ipcServer?.once('listening', () => resolve());\n this.ipcServer?.once('error', reject);\n this.ipcServer?.listen(this.endpoint.path);\n });\n\n this.emit('log', `session host endpoint ready: ${this.endpoint.path}`);\n }\n\n async stop(): Promise<void> {\n this.flushAllPersistence();\n for (const runtime of this.runtimes.values()) {\n try {\n runtime.stop();\n } catch {\n // noop\n }\n }\n this.runtimes.clear();\n for (const timer of this.persistTimers.values()) {\n clearTimeout(timer);\n }\n this.persistTimers.clear();\n for (const socket of this.sockets) {\n socket.destroy();\n }\n this.sockets.clear();\n if (this.ipcServer) {\n const server = this.ipcServer;\n this.ipcServer = null;\n await new Promise<void>((resolve) => server.close(() => resolve()));\n }\n if (this.endpoint.kind === 'unix') {\n try {\n fs.unlinkSync(this.endpoint.path);\n } catch {\n // noop\n }\n }\n this.removeAllListeners();\n }\n\n async handleRequest(request: SessionHostRequest): Promise<SessionHostResponse> {\n try {\n switch (request.type) {\n case 'create_session': {\n const record = this.registry.createSession(request.payload);\n this.schedulePersist(record.sessionId);\n this.emitEvent({ type: 'session_created', sessionId: record.sessionId, record });\n try {\n const startedRecord = this.startRuntime(record, request.payload, 'session_started');\n return { success: true, result: startedRecord };\n } catch (error: any) {\n this.registry.markStopped(record.sessionId, 'failed');\n this.persistNow(record.sessionId);\n return { success: false, error: error?.message || String(error) };\n }\n }\n case 'list_sessions':\n return { success: true, result: this.registry.listSessions() };\n case 'attach_session': {\n const record = this.registry.attachClient(request.payload);\n this.schedulePersist(record.sessionId);\n const client = record.attachedClients.find(item => item.clientId === request.payload.clientId);\n if (client) {\n this.emitEvent({ type: 'client_attached', sessionId: record.sessionId, client });\n }\n return { success: true, result: record };\n }\n case 'detach_session': {\n const record = this.registry.detachClient(request.payload);\n this.schedulePersist(record.sessionId);\n this.emitEvent({ type: 'client_detached', sessionId: record.sessionId, clientId: request.payload.clientId });\n return { success: true, result: record };\n }\n case 'acquire_write': {\n const record = this.registry.acquireWrite(request.payload);\n this.persistNow(record.sessionId);\n this.emitEvent({ type: 'write_owner_changed', sessionId: record.sessionId, owner: record.writeOwner });\n return { success: true, result: record };\n }\n case 'release_write': {\n const record = this.registry.releaseWrite(request.payload);\n this.persistNow(record.sessionId);\n this.emitEvent({ type: 'write_owner_changed', sessionId: record.sessionId, owner: record.writeOwner });\n return { success: true, result: record };\n }\n case 'get_snapshot':\n return { success: true, result: this.registry.getSnapshot(request.payload.sessionId, request.payload.sinceSeq) };\n case 'clear_session_buffer': {\n const record = this.registry.clearBuffer(request.payload.sessionId);\n this.persistNow(record.sessionId);\n this.emitEvent({ type: 'session_cleared', sessionId: record.sessionId });\n return { success: true, result: record };\n }\n case 'send_input': {\n const client = this.getAttachedClient(request.payload.sessionId, request.payload.clientId);\n if (client?.readOnly) {\n return { success: false, error: `Client ${request.payload.clientId} is read-only` };\n }\n const session = this.registry.getSession(request.payload.sessionId);\n if (session?.writeOwner && session.writeOwner.clientId !== request.payload.clientId) {\n return { success: false, error: `Write owned by ${session.writeOwner.clientId}` };\n }\n this.requireRuntime(request.payload.sessionId).write(request.payload.data);\n return { success: true, result: this.registry.getSession(request.payload.sessionId) };\n }\n case 'resize_session': {\n this.requireRuntime(request.payload.sessionId).resize(request.payload.cols, request.payload.rows);\n const record = this.registry.getSession(request.payload.sessionId);\n if (record) {\n this.registry.restoreSession(\n {\n ...record,\n meta: {\n ...(record.meta || {}),\n sessionHostCols: request.payload.cols,\n sessionHostRows: request.payload.rows,\n },\n },\n this.registry.getSnapshot(request.payload.sessionId),\n );\n }\n this.schedulePersist(request.payload.sessionId);\n this.emitEvent({\n type: 'session_resized',\n sessionId: request.payload.sessionId,\n cols: request.payload.cols,\n rows: request.payload.rows,\n });\n return { success: true, result: this.registry.getSession(request.payload.sessionId) };\n }\n case 'stop_session': {\n this.registry.setLifecycle(request.payload.sessionId, 'stopping');\n this.persistNow(request.payload.sessionId);\n this.requireRuntime(request.payload.sessionId).stop();\n this.emitEvent({ type: 'session_stopped', sessionId: request.payload.sessionId });\n return { success: true, result: this.registry.getSession(request.payload.sessionId) };\n }\n case 'resume_session': {\n const existing = this.registry.getSession(request.payload.sessionId);\n if (!existing) {\n return { success: false, error: `Unknown session: ${request.payload.sessionId}` };\n }\n if (this.runtimes.has(request.payload.sessionId)) {\n return { success: true, result: existing };\n }\n const resumed = this.startRuntime(existing, this.buildPayloadFromRecord(existing), 'session_resumed');\n return { success: true, result: resumed };\n }\n }\n } catch (error: any) {\n return { success: false, error: error?.message || String(error) };\n }\n }\n\n private requireRuntime(sessionId: string): PtySessionRuntime {\n const runtime = this.runtimes.get(sessionId);\n if (!runtime) throw new Error(`Runtime not found for session: ${sessionId}`);\n return runtime;\n }\n\n private getAttachedClient(sessionId: string, clientId: string): SessionAttachedClient | null {\n const session = this.registry.getSession(sessionId);\n return session?.attachedClients.find((client) => client.clientId === clientId) || null;\n }\n\n private emitEvent(event: SessionHostEvent): void {\n for (const socket of this.sockets) {\n writeEnvelope(socket, {\n kind: 'event',\n event,\n });\n }\n this.emit('event', event);\n }\n\n private async handleIncomingRequest(socket: net.Socket, envelope: SessionHostRequestEnvelope): Promise<void> {\n const response = await this.handleRequest(envelope.request);\n writeEnvelope(socket, createResponseEnvelope(envelope.requestId, response));\n }\n\n private schedulePersist(sessionId: string): void {\n const existing = this.persistTimers.get(sessionId);\n if (existing) clearTimeout(existing);\n this.persistTimers.set(sessionId, setTimeout(() => {\n this.persistTimers.delete(sessionId);\n this.persistNow(sessionId);\n }, 200));\n }\n\n private persistNow(sessionId: string): void {\n const record = this.registry.getSession(sessionId);\n if (!record) return;\n const snapshot = this.registry.getSnapshot(sessionId);\n this.storage.save(record, snapshot);\n }\n\n private flushAllPersistence(): void {\n for (const sessionId of this.runtimes.keys()) {\n this.persistNow(sessionId);\n }\n for (const record of this.registry.listSessions()) {\n this.persistNow(record.sessionId);\n }\n }\n\n private restorePersistedRuntimes(): void {\n const states = this.storage.loadAll();\n for (const persisted of states) {\n const wasLiveRuntime = !['stopped', 'failed'].includes(persisted.record.lifecycle);\n const recoveredRecord: SessionHostRecord = {\n ...persisted.record,\n attachedClients: [],\n writeOwner: null,\n lifecycle: wasLiveRuntime ? 'interrupted' : persisted.record.lifecycle,\n lastActivityAt: Date.now(),\n meta: {\n ...(persisted.record.meta || {}),\n restoredFromStorage: true,\n runtimeRecoveryState: wasLiveRuntime ? 'host_restart_interrupted' : 'snapshot',\n },\n };\n this.registry.restoreSession(recoveredRecord, persisted.snapshot);\n this.storage.save(recoveredRecord, persisted.snapshot);\n if (wasLiveRuntime) {\n try {\n const resumed = this.startRuntime(\n recoveredRecord,\n this.buildPayloadFromRecord(recoveredRecord),\n 'session_resumed',\n );\n const resumedMeta = {\n ...(resumed.meta || {}),\n restoredFromStorage: true,\n runtimeRecoveryState: 'auto_resumed',\n };\n this.registry.restoreSession(\n { ...resumed, meta: resumedMeta },\n this.registry.getSnapshot(resumed.sessionId),\n );\n this.persistNow(resumed.sessionId);\n } catch (error: any) {\n const interrupted = this.registry.setLifecycle(recoveredRecord.sessionId, 'interrupted');\n this.registry.restoreSession({\n ...interrupted,\n meta: {\n ...(interrupted.meta || {}),\n restoredFromStorage: true,\n runtimeRecoveryState: 'resume_failed',\n runtimeRecoveryError: error?.message || String(error),\n },\n }, persisted.snapshot);\n this.persistNow(recoveredRecord.sessionId);\n }\n }\n }\n }\n\n private buildPayloadFromRecord(record: SessionHostRecord): CreateSessionPayload {\n return {\n sessionId: record.sessionId,\n runtimeKey: record.runtimeKey,\n displayName: record.displayName,\n providerType: record.providerType,\n category: record.category,\n workspace: record.workspace,\n launchCommand: record.launchCommand,\n cols: typeof record.meta?.sessionHostCols === 'number' ? (record.meta.sessionHostCols as number) : 120,\n rows: typeof record.meta?.sessionHostRows === 'number' ? (record.meta.sessionHostRows as number) : 40,\n meta: record.meta,\n };\n }\n\n private startRuntime(\n record: SessionHostRecord,\n payload: CreateSessionPayload,\n startEventType: 'session_started' | 'session_resumed',\n ): SessionHostRecord {\n const runtime = new PtySessionRuntime({\n sessionId: record.sessionId,\n payload,\n onData: (data) => {\n const { seq } = this.registry.appendOutput(record.sessionId, data);\n this.schedulePersist(record.sessionId);\n this.emitEvent({ type: 'session_output', sessionId: record.sessionId, seq, data });\n },\n onExit: (exitCode) => {\n this.registry.markStopped(record.sessionId, exitCode === 0 ? 'stopped' : 'failed');\n this.runtimes.delete(record.sessionId);\n this.persistNow(record.sessionId);\n this.emitEvent({ type: 'session_exit', sessionId: record.sessionId, exitCode });\n },\n });\n\n this.registry.setLifecycle(record.sessionId, 'starting');\n const pid = runtime.start();\n this.runtimes.set(record.sessionId, runtime);\n const startedRecord = this.registry.markStarted(record.sessionId, pid);\n this.persistNow(record.sessionId);\n this.emitEvent({ type: startEventType, sessionId: record.sessionId, pid });\n return startedRecord;\n }\n}\n","import * as os from 'os';\nimport * as path from 'path';\nimport * as pty from 'node-pty';\nimport type { IPty } from 'node-pty';\nimport type { CreateSessionPayload } from '@adhdev/session-host-core';\n\nif (os.platform() !== 'win32') {\n try {\n const fs = require('fs');\n const ptyDir = path.resolve(path.dirname(require.resolve('node-pty')), '..');\n const platformArch = `${os.platform()}-${os.arch()}`;\n const helper = path.join(ptyDir, 'prebuilds', platformArch, 'spawn-helper');\n if (fs.existsSync(helper)) {\n const stat = fs.statSync(helper);\n if (!(stat.mode & 0o111)) {\n fs.chmodSync(helper, stat.mode | 0o755);\n }\n }\n } catch {\n // best-effort: node-pty still works on most installs without this\n }\n}\n\nexport interface PtyRuntimeOptions {\n sessionId: string;\n payload: CreateSessionPayload;\n onData: (data: string) => void;\n onExit: (exitCode: number | null) => void;\n}\n\nexport class PtySessionRuntime {\n readonly sessionId: string;\n readonly payload: CreateSessionPayload;\n readonly cols: number;\n readonly rows: number;\n\n private ptyProcess: IPty | null = null;\n private onDataCallback: (data: string) => void;\n private onExitCallback: (exitCode: number | null) => void;\n\n constructor(options: PtyRuntimeOptions) {\n this.sessionId = options.sessionId;\n this.payload = options.payload;\n this.cols = options.payload.cols || 120;\n this.rows = options.payload.rows || 40;\n this.onDataCallback = options.onData;\n this.onExitCallback = options.onExit;\n }\n\n start(): number {\n if (this.ptyProcess) return this.ptyProcess.pid;\n\n const command = this.payload.launchCommand.command;\n const args = this.payload.launchCommand.args || [];\n const cwd = this.payload.workspace || process.cwd();\n const env = {\n ...process.env,\n ...(this.payload.launchCommand.env || {}),\n } as Record<string, string>;\n\n this.ptyProcess = pty.spawn(command, args, {\n name: os.platform() === 'win32' ? 'xterm-color' : 'xterm-256color',\n cols: this.cols,\n rows: this.rows,\n cwd,\n env,\n });\n\n this.ptyProcess.onData((data: string) => {\n this.onDataCallback(data);\n });\n\n this.ptyProcess.onExit(({ exitCode }) => {\n this.ptyProcess = null;\n this.onExitCallback(exitCode ?? null);\n });\n\n return this.ptyProcess.pid;\n }\n\n write(data: string): void {\n if (!this.ptyProcess) throw new Error(`Session not running: ${this.sessionId}`);\n this.ptyProcess.write(data);\n }\n\n resize(cols: number, rows: number): void {\n if (!this.ptyProcess) throw new Error(`Session not running: ${this.sessionId}`);\n this.ptyProcess.resize(cols, rows);\n }\n\n stop(): void {\n if (!this.ptyProcess) return;\n this.ptyProcess.kill();\n }\n}\n","import * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\nimport type { SessionBufferSnapshot, SessionHostRecord } from '@adhdev/session-host-core';\n\nexport interface PersistedRuntimeState {\n record: SessionHostRecord;\n snapshot: SessionBufferSnapshot;\n updatedAt: number;\n}\n\ninterface SessionHostStorageOptions {\n appName?: string;\n}\n\nexport class SessionHostStorage {\n private readonly rootDir: string;\n private readonly runtimesDir: string;\n\n constructor(options: SessionHostStorageOptions = {}) {\n const appName = options.appName || 'adhdev';\n this.rootDir = path.join(os.homedir(), '.adhdev', 'session-host', appName);\n this.runtimesDir = path.join(this.rootDir, 'runtimes');\n }\n\n loadAll(): PersistedRuntimeState[] {\n if (!fs.existsSync(this.runtimesDir)) return [];\n const entries = fs.readdirSync(this.runtimesDir, { withFileTypes: true });\n const states: PersistedRuntimeState[] = [];\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.json')) continue;\n const fullPath = path.join(this.runtimesDir, entry.name);\n try {\n const parsed = JSON.parse(fs.readFileSync(fullPath, 'utf8')) as PersistedRuntimeState;\n if (parsed?.record?.sessionId) {\n states.push(parsed);\n }\n } catch {\n // Ignore malformed snapshots; host should still boot.\n }\n }\n return states.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));\n }\n\n save(record: SessionHostRecord, snapshot: SessionBufferSnapshot): void {\n fs.mkdirSync(this.runtimesDir, { recursive: true });\n const filePath = path.join(this.runtimesDir, `${record.sessionId}.json`);\n const payload: PersistedRuntimeState = {\n record,\n snapshot,\n updatedAt: Date.now(),\n };\n fs.writeFileSync(filePath, JSON.stringify(payload, null, 2), 'utf8');\n }\n}\n"],"mappings":"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","names":["import_session_host_core","fs","fs","os","path","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/server.ts","../src/runtime.ts","../src/storage.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { randomUUID } from 'crypto';\nimport {\n SessionHostClient,\n formatRuntimeOwner,\n getDefaultSessionHostEndpoint,\n resolveRuntimeRecord,\n type SessionHostEvent,\n type SessionHostRecord,\n} from '@adhdev/session-host-core';\nimport { SessionHostServer } from './server.js';\n\nexport { SessionHostServer } from './server.js';\nexport type { SessionHostServerOptions } from './server.js';\n\nconst SESSION_HOST_APP_NAME = process.env.ADHDEV_SESSION_HOST_NAME || 'adhdev';\n\nfunction parseArgs(argv: string[]) {\n const [command, ...rest] = argv;\n const readOnly = rest.includes('--read-only');\n const takeover = rest.includes('--takeover');\n const showAll = rest.includes('--all');\n const positional = rest.filter((arg) => arg !== '--read-only' && arg !== '--takeover' && arg !== '--all');\n return {\n command: command || 'serve',\n positional,\n readOnly,\n takeover,\n showAll,\n };\n}\n\nasync function runServer(): Promise<void> {\n const server = new SessionHostServer({ appName: SESSION_HOST_APP_NAME });\n await server.start();\n\n process.on('SIGINT', async () => {\n await server.stop();\n process.exit(0);\n });\n\n process.on('SIGTERM', async () => {\n await server.stop();\n process.exit(0);\n });\n\n // Keep the host alive; IPC transport wiring comes next.\n await new Promise<void>(() => {});\n}\n\nasync function listRuntimes(showAll = false): Promise<void> {\n const client = new SessionHostClient({ endpoint: getDefaultSessionHostEndpoint(SESSION_HOST_APP_NAME) });\n try {\n const response = await client.request<SessionHostRecord[]>({\n type: 'list_sessions',\n payload: {},\n });\n if (!response.success) {\n throw new Error(response.error || 'Failed to list runtimes');\n }\n const runtimes = (response.result || []).filter((runtime: SessionHostRecord) => showAll || runtime.lifecycle !== 'stopped');\n if (runtimes.length === 0) {\n console.log('No runtimes.');\n return;\n }\n console.log('runtimeKey\\tlifecycle\\towner\\tworkspace\\tid\\tdisplayName');\n for (const runtime of runtimes) {\n console.log([\n runtime.runtimeKey,\n runtime.lifecycle,\n formatRuntimeOwner(runtime),\n runtime.workspaceLabel,\n runtime.sessionId,\n runtime.displayName,\n ].join('\\t'));\n }\n } finally {\n await client.close().catch(() => {});\n }\n}\n\nasync function attachRuntime(target: string, readOnly = false, takeover = false): Promise<void> {\n const client = new SessionHostClient({ endpoint: getDefaultSessionHostEndpoint(SESSION_HOST_APP_NAME) });\n const clientId = `local-terminal-${process.pid}-${randomUUID().slice(0, 8)}`;\n let lastSeq = 0;\n let restoredRawMode = false;\n let runtimeId = '';\n let localReadOnly = readOnly;\n\n const cleanup = async () => {\n process.stdout.off('resize', handleResize);\n process.stdin.off('data', handleInput);\n process.stdin.pause();\n if (process.stdin.isTTY && restoredRawMode) {\n process.stdin.setRawMode(false);\n }\n await client.request({\n type: 'release_write',\n payload: {\n sessionId: runtimeId,\n clientId,\n },\n }).catch(() => ({ success: false }));\n await client.request({\n type: 'detach_session',\n payload: {\n sessionId: runtimeId,\n clientId,\n },\n }).catch(() => ({ success: false }));\n await client.close().catch(() => {});\n };\n\n const handleResize = () => {\n void client.request({\n type: 'resize_session',\n payload: {\n sessionId: runtimeId,\n cols: process.stdout.columns || 120,\n rows: process.stdout.rows || 40,\n },\n }).catch(() => ({ success: false }));\n };\n\n const sendInputWithTakeover = async (data: string) => {\n let response = await client.request({\n type: 'send_input',\n payload: {\n sessionId: runtimeId,\n clientId,\n data,\n },\n });\n if (!response.success && response.error?.startsWith('Write owned by ')) {\n const ownerResponse = await client.request<SessionHostRecord>({\n type: 'acquire_write',\n payload: {\n sessionId: runtimeId,\n clientId,\n ownerType: 'user',\n force: true,\n },\n });\n if (ownerResponse.success && ownerResponse.result) {\n response = await client.request({\n type: 'send_input',\n payload: {\n sessionId: runtimeId,\n clientId,\n data,\n },\n });\n if (response.success) {\n process.stderr.write(`Took control of ${ownerResponse.result.runtimeKey}.\\n`);\n }\n }\n }\n return response;\n };\n\n const handleInput = (chunk: Buffer) => {\n if (!localReadOnly && chunk.length === 1 && chunk[0] === 0x1d) {\n void cleanup().finally(() => process.exit(0));\n return;\n }\n if (localReadOnly) return;\n void sendInputWithTakeover(chunk.toString('utf8')).catch(() => ({ success: false }));\n };\n\n try {\n if (readOnly && takeover) {\n throw new Error('Use either --read-only or --takeover, not both');\n }\n\n const listResponse = await client.request<SessionHostRecord[]>({\n type: 'list_sessions',\n payload: {},\n });\n if (!listResponse.success || !listResponse.result) {\n throw new Error(listResponse.error || 'Failed to list runtimes');\n }\n let runtimeRecord = resolveRuntimeRecord(listResponse.result, target);\n runtimeId = runtimeRecord.sessionId;\n\n if (runtimeRecord.lifecycle === 'interrupted' && !readOnly) {\n const resumeResponse = await client.request<SessionHostRecord>({\n type: 'resume_session',\n payload: {\n sessionId: runtimeId,\n },\n });\n if (resumeResponse.success && resumeResponse.result) {\n runtimeRecord = resumeResponse.result;\n } else {\n process.stderr.write(\n `Runtime ${runtimeRecord.runtimeKey} could not be resumed automatically: ${resumeResponse.error || 'unknown error'}\\n`,\n );\n }\n }\n\n let effectiveReadOnly = readOnly;\n if (!effectiveReadOnly && runtimeRecord.writeOwner && runtimeRecord.writeOwner.clientId !== clientId && !takeover) {\n process.stderr.write(\n `Runtime ${runtimeRecord.runtimeKey} is currently owned by ${runtimeRecord.writeOwner.clientId}; first input will take control here.\\n`,\n );\n }\n localReadOnly = effectiveReadOnly;\n\n const attachResponse = await client.request<SessionHostRecord>({\n type: 'attach_session',\n payload: {\n sessionId: runtimeId,\n clientId,\n clientType: 'local-terminal',\n readOnly: effectiveReadOnly,\n },\n });\n if (!attachResponse.success) {\n throw new Error(attachResponse.error || `Failed to attach runtime ${runtimeId}`);\n }\n const attachedRecord = attachResponse.result || null;\n\n if (!effectiveReadOnly && takeover) {\n const ownerResponse = await client.request<SessionHostRecord>({\n type: 'acquire_write',\n payload: {\n sessionId: runtimeId,\n clientId,\n ownerType: 'user',\n force: takeover,\n },\n });\n if (!ownerResponse.success) {\n throw new Error(ownerResponse.error || `Failed to acquire write owner for runtime ${runtimeId}`);\n }\n }\n\n const snapshotResponse = await client.request<{ seq: number; text: string; truncated: boolean }>({\n type: 'get_snapshot',\n payload: { sessionId: runtimeId },\n });\n if (!snapshotResponse.success) {\n throw new Error(snapshotResponse.error || `Failed to read runtime snapshot ${runtimeId}`);\n }\n lastSeq = snapshotResponse.result?.seq || 0;\n if (snapshotResponse.result?.text) {\n process.stdout.write(snapshotResponse.result.text);\n }\n if (attachedRecord?.lifecycle === 'stopped' || attachedRecord?.lifecycle === 'failed' || attachedRecord?.lifecycle === 'interrupted') {\n process.stderr.write(`Runtime ${attachedRecord.runtimeKey} is already ${attachedRecord.lifecycle}. Detached after snapshot.\\n`);\n await cleanup();\n return;\n }\n\n const stopSignals = ['SIGINT', 'SIGTERM', 'SIGHUP'] as const;\n const signalHandlers = stopSignals.map((signal) => {\n const handler = () => {\n void cleanup().finally(() => process.exit(0));\n };\n process.on(signal, handler);\n return { signal, handler };\n });\n\n const unsubscribe = client.onEvent((event: SessionHostEvent) => {\n if (event.sessionId !== runtimeId) return;\n if (event.type === 'session_output') {\n if (event.seq <= lastSeq) return;\n lastSeq = event.seq;\n process.stdout.write(event.data);\n return;\n }\n if (event.type === 'session_exit') {\n void cleanup().finally(() => {\n for (const { signal, handler } of signalHandlers) {\n process.off(signal, handler);\n }\n unsubscribe();\n process.exit(event.exitCode ?? 0);\n });\n }\n });\n\n process.stdout.on('resize', handleResize);\n process.stdin.on('data', handleInput);\n process.stdin.resume();\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n restoredRawMode = true;\n }\n handleResize();\n if (!effectiveReadOnly) {\n process.stderr.write(`Attached to runtime ${attachedRecord?.runtimeKey || runtimeId}. Press Ctrl+] to detach.\\n`);\n } else {\n process.stderr.write(`Attached to runtime ${attachedRecord?.runtimeKey || runtimeId} (read-only).\\n`);\n }\n await new Promise<void>(() => {});\n } catch (error) {\n await cleanup().catch(() => {});\n throw error;\n }\n}\n\nasync function main(): Promise<void> {\n const { command, positional, readOnly, takeover, showAll } = parseArgs(process.argv.slice(2));\n if (command === 'serve') {\n await runServer();\n return;\n }\n if (command === 'list') {\n await listRuntimes(showAll);\n return;\n }\n if (command === 'attach') {\n const target = positional[0];\n if (!target) {\n throw new Error('runtime target is required: adhdev-sessiond attach <runtimeId|runtimeKey>');\n }\n await attachRuntime(target, readOnly, takeover);\n return;\n }\n if (command === 'resume') {\n const target = positional[0];\n if (!target) {\n throw new Error('runtime target is required: adhdev-sessiond resume <runtimeId|runtimeKey>');\n }\n const client = new SessionHostClient({ endpoint: getDefaultSessionHostEndpoint(SESSION_HOST_APP_NAME) });\n try {\n const listResponse = await client.request<SessionHostRecord[]>({ type: 'list_sessions', payload: {} });\n if (!listResponse.success || !listResponse.result) {\n throw new Error(listResponse.error || 'Failed to list runtimes');\n }\n const runtimeRecord = resolveRuntimeRecord(listResponse.result, target);\n const resumeResponse = await client.request<SessionHostRecord>({\n type: 'resume_session',\n payload: {\n sessionId: runtimeRecord.sessionId,\n },\n });\n if (!resumeResponse.success || !resumeResponse.result) {\n throw new Error(resumeResponse.error || `Failed to resume runtime ${runtimeRecord.runtimeKey}`);\n }\n console.log(`Resumed ${resumeResponse.result.runtimeKey} (${resumeResponse.result.sessionId})`);\n } finally {\n await client.close().catch(() => {});\n }\n return;\n }\n throw new Error(`Unknown command: ${command}`);\n}\n\nif (require.main === module) {\n void main().catch((error) => {\n console.error(error instanceof Error ? error.message : error);\n process.exit(1);\n });\n}\n","import { EventEmitter } from 'events';\nimport * as fs from 'fs';\nimport * as net from 'net';\nimport {\n SessionHostRegistry,\n createLineParser,\n createResponseEnvelope,\n getDefaultSessionHostEndpoint,\n writeEnvelope,\n} from '@adhdev/session-host-core';\nimport type {\n CreateSessionPayload,\n SessionAttachedClient,\n SessionHostEndpoint,\n SessionHostEvent,\n SessionHostRecord,\n SessionHostRequestEnvelope,\n SessionHostRequest,\n SessionHostResponse,\n} from '@adhdev/session-host-core';\nimport { PtySessionRuntime } from './runtime.js';\nimport { SessionHostStorage, type PersistedRuntimeState } from './storage.js';\n\nexport interface SessionHostServerOptions {\n endpoint?: SessionHostEndpoint;\n appName?: string;\n}\n\nexport class SessionHostServer extends EventEmitter {\n readonly endpoint: SessionHostEndpoint;\n readonly registry = new SessionHostRegistry();\n private runtimes = new Map<string, PtySessionRuntime>();\n private readonly storage: SessionHostStorage;\n private ipcServer: net.Server | null = null;\n private sockets = new Set<net.Socket>();\n private persistTimers = new Map<string, NodeJS.Timeout>();\n\n constructor(options: SessionHostServerOptions = {}) {\n super();\n this.endpoint = options.endpoint || getDefaultSessionHostEndpoint(options.appName || 'adhdev');\n this.storage = new SessionHostStorage({ appName: options.appName || 'adhdev' });\n }\n\n async start(): Promise<void> {\n if (this.endpoint.kind === 'unix') {\n try {\n fs.unlinkSync(this.endpoint.path);\n } catch {\n // noop\n }\n }\n\n this.ipcServer = net.createServer((socket) => {\n this.sockets.add(socket);\n socket.on('close', () => {\n this.sockets.delete(socket);\n });\n socket.on('data', createLineParser((envelope) => {\n if (envelope.kind !== 'request') return;\n void this.handleIncomingRequest(socket, envelope);\n }));\n });\n\n await new Promise<void>((resolve, reject) => {\n this.ipcServer?.once('listening', () => resolve());\n this.ipcServer?.once('error', reject);\n this.ipcServer?.listen(this.endpoint.path);\n });\n\n this.emit('log', `session host endpoint ready: ${this.endpoint.path}`);\n // Do not block readiness on restoring/resuming persisted runtimes.\n // Startup callers only need the IPC endpoint to accept connections.\n setTimeout(() => {\n try {\n this.restorePersistedRuntimes();\n } catch (error: any) {\n this.emit('log', `session host restore failed: ${error?.message || String(error)}`);\n }\n }, 0);\n }\n\n async stop(): Promise<void> {\n this.flushAllPersistence();\n for (const runtime of this.runtimes.values()) {\n try {\n runtime.stop();\n } catch {\n // noop\n }\n }\n this.runtimes.clear();\n for (const timer of this.persistTimers.values()) {\n clearTimeout(timer);\n }\n this.persistTimers.clear();\n for (const socket of this.sockets) {\n socket.destroy();\n }\n this.sockets.clear();\n if (this.ipcServer) {\n const server = this.ipcServer;\n this.ipcServer = null;\n await new Promise<void>((resolve) => server.close(() => resolve()));\n }\n if (this.endpoint.kind === 'unix') {\n try {\n fs.unlinkSync(this.endpoint.path);\n } catch {\n // noop\n }\n }\n this.removeAllListeners();\n }\n\n async handleRequest(request: SessionHostRequest): Promise<SessionHostResponse> {\n try {\n switch (request.type) {\n case 'create_session': {\n const record = this.registry.createSession(request.payload);\n this.schedulePersist(record.sessionId);\n this.emitEvent({ type: 'session_created', sessionId: record.sessionId, record });\n try {\n const startedRecord = this.startRuntime(record, request.payload, 'session_started');\n return { success: true, result: startedRecord };\n } catch (error: any) {\n this.registry.markStopped(record.sessionId, 'failed');\n this.persistNow(record.sessionId);\n return { success: false, error: error?.message || String(error) };\n }\n }\n case 'list_sessions':\n return { success: true, result: this.registry.listSessions() };\n case 'attach_session': {\n const record = this.registry.attachClient(request.payload);\n this.schedulePersist(record.sessionId);\n const client = record.attachedClients.find(item => item.clientId === request.payload.clientId);\n if (client) {\n this.emitEvent({ type: 'client_attached', sessionId: record.sessionId, client });\n }\n return { success: true, result: record };\n }\n case 'detach_session': {\n const record = this.registry.detachClient(request.payload);\n this.schedulePersist(record.sessionId);\n this.emitEvent({ type: 'client_detached', sessionId: record.sessionId, clientId: request.payload.clientId });\n return { success: true, result: record };\n }\n case 'acquire_write': {\n const record = this.registry.acquireWrite(request.payload);\n this.persistNow(record.sessionId);\n this.emitEvent({ type: 'write_owner_changed', sessionId: record.sessionId, owner: record.writeOwner });\n return { success: true, result: record };\n }\n case 'release_write': {\n const record = this.registry.releaseWrite(request.payload);\n this.persistNow(record.sessionId);\n this.emitEvent({ type: 'write_owner_changed', sessionId: record.sessionId, owner: record.writeOwner });\n return { success: true, result: record };\n }\n case 'get_snapshot':\n return { success: true, result: this.registry.getSnapshot(request.payload.sessionId, request.payload.sinceSeq) };\n case 'clear_session_buffer': {\n const record = this.registry.clearBuffer(request.payload.sessionId);\n this.persistNow(record.sessionId);\n this.emitEvent({ type: 'session_cleared', sessionId: record.sessionId });\n return { success: true, result: record };\n }\n case 'send_input': {\n const client = this.getAttachedClient(request.payload.sessionId, request.payload.clientId);\n if (client?.readOnly) {\n return { success: false, error: `Client ${request.payload.clientId} is read-only` };\n }\n const session = this.registry.getSession(request.payload.sessionId);\n if (session?.writeOwner && session.writeOwner.clientId !== request.payload.clientId) {\n return { success: false, error: `Write owned by ${session.writeOwner.clientId}` };\n }\n this.requireRuntime(request.payload.sessionId).write(request.payload.data);\n return { success: true, result: this.registry.getSession(request.payload.sessionId) };\n }\n case 'resize_session': {\n this.requireRuntime(request.payload.sessionId).resize(request.payload.cols, request.payload.rows);\n const record = this.registry.getSession(request.payload.sessionId);\n if (record) {\n this.registry.restoreSession(\n {\n ...record,\n meta: {\n ...(record.meta || {}),\n sessionHostCols: request.payload.cols,\n sessionHostRows: request.payload.rows,\n },\n },\n this.registry.getSnapshot(request.payload.sessionId),\n );\n }\n this.schedulePersist(request.payload.sessionId);\n this.emitEvent({\n type: 'session_resized',\n sessionId: request.payload.sessionId,\n cols: request.payload.cols,\n rows: request.payload.rows,\n });\n return { success: true, result: this.registry.getSession(request.payload.sessionId) };\n }\n case 'stop_session': {\n this.registry.setLifecycle(request.payload.sessionId, 'stopping');\n this.persistNow(request.payload.sessionId);\n this.requireRuntime(request.payload.sessionId).stop();\n this.emitEvent({ type: 'session_stopped', sessionId: request.payload.sessionId });\n return { success: true, result: this.registry.getSession(request.payload.sessionId) };\n }\n case 'resume_session': {\n const existing = this.registry.getSession(request.payload.sessionId);\n if (!existing) {\n return { success: false, error: `Unknown session: ${request.payload.sessionId}` };\n }\n if (this.runtimes.has(request.payload.sessionId)) {\n return { success: true, result: existing };\n }\n const resumed = this.startRuntime(existing, this.buildPayloadFromRecord(existing), 'session_resumed');\n return { success: true, result: resumed };\n }\n }\n } catch (error: any) {\n return { success: false, error: error?.message || String(error) };\n }\n }\n\n private requireRuntime(sessionId: string): PtySessionRuntime {\n const runtime = this.runtimes.get(sessionId);\n if (!runtime) throw new Error(`Runtime not found for session: ${sessionId}`);\n return runtime;\n }\n\n private getAttachedClient(sessionId: string, clientId: string): SessionAttachedClient | null {\n const session = this.registry.getSession(sessionId);\n return session?.attachedClients.find((client) => client.clientId === clientId) || null;\n }\n\n private emitEvent(event: SessionHostEvent): void {\n for (const socket of this.sockets) {\n writeEnvelope(socket, {\n kind: 'event',\n event,\n });\n }\n this.emit('event', event);\n }\n\n private async handleIncomingRequest(socket: net.Socket, envelope: SessionHostRequestEnvelope): Promise<void> {\n const response = await this.handleRequest(envelope.request);\n writeEnvelope(socket, createResponseEnvelope(envelope.requestId, response));\n }\n\n private schedulePersist(sessionId: string): void {\n const existing = this.persistTimers.get(sessionId);\n if (existing) clearTimeout(existing);\n this.persistTimers.set(sessionId, setTimeout(() => {\n this.persistTimers.delete(sessionId);\n this.persistNow(sessionId);\n }, 200));\n }\n\n private persistNow(sessionId: string): void {\n const record = this.registry.getSession(sessionId);\n if (!record) return;\n const snapshot = this.registry.getSnapshot(sessionId);\n this.storage.save(record, snapshot);\n }\n\n private flushAllPersistence(): void {\n for (const sessionId of this.runtimes.keys()) {\n this.persistNow(sessionId);\n }\n for (const record of this.registry.listSessions()) {\n this.persistNow(record.sessionId);\n }\n }\n\n private restorePersistedRuntimes(): void {\n const states = this.storage.loadAll();\n const runtimesToResume: Array<{\n persisted: PersistedRuntimeState;\n recoveredRecord: SessionHostRecord;\n }> = [];\n for (const persisted of states) {\n const wasLiveRuntime = !['stopped', 'failed'].includes(persisted.record.lifecycle);\n const recoveredRecord: SessionHostRecord = {\n ...persisted.record,\n attachedClients: [],\n writeOwner: null,\n lifecycle: wasLiveRuntime ? 'interrupted' : persisted.record.lifecycle,\n lastActivityAt: Date.now(),\n meta: {\n ...(persisted.record.meta || {}),\n restoredFromStorage: true,\n runtimeRecoveryState: wasLiveRuntime ? 'host_restart_interrupted' : 'snapshot',\n },\n };\n this.registry.restoreSession(recoveredRecord, persisted.snapshot);\n this.storage.save(recoveredRecord, persisted.snapshot);\n if (wasLiveRuntime) {\n runtimesToResume.push({ persisted, recoveredRecord });\n }\n }\n\n for (const { persisted, recoveredRecord } of runtimesToResume) {\n try {\n const resumed = this.startRuntime(\n recoveredRecord,\n this.buildPayloadFromRecord(recoveredRecord),\n 'session_resumed',\n );\n const resumedMeta = {\n ...(resumed.meta || {}),\n restoredFromStorage: true,\n runtimeRecoveryState: 'auto_resumed',\n };\n this.registry.restoreSession(\n { ...resumed, meta: resumedMeta },\n this.registry.getSnapshot(resumed.sessionId),\n );\n this.persistNow(resumed.sessionId);\n } catch (error: any) {\n const interrupted = this.registry.setLifecycle(recoveredRecord.sessionId, 'interrupted');\n this.registry.restoreSession({\n ...interrupted,\n meta: {\n ...(interrupted.meta || {}),\n restoredFromStorage: true,\n runtimeRecoveryState: 'resume_failed',\n runtimeRecoveryError: error?.message || String(error),\n },\n }, persisted.snapshot);\n this.persistNow(recoveredRecord.sessionId);\n }\n }\n }\n\n private buildPayloadFromRecord(record: SessionHostRecord): CreateSessionPayload {\n return {\n sessionId: record.sessionId,\n runtimeKey: record.runtimeKey,\n displayName: record.displayName,\n providerType: record.providerType,\n category: record.category,\n workspace: record.workspace,\n launchCommand: record.launchCommand,\n cols: typeof record.meta?.sessionHostCols === 'number' ? (record.meta.sessionHostCols as number) : 120,\n rows: typeof record.meta?.sessionHostRows === 'number' ? (record.meta.sessionHostRows as number) : 40,\n meta: record.meta,\n };\n }\n\n private startRuntime(\n record: SessionHostRecord,\n payload: CreateSessionPayload,\n startEventType: 'session_started' | 'session_resumed',\n ): SessionHostRecord {\n const runtime = new PtySessionRuntime({\n sessionId: record.sessionId,\n payload,\n onData: (data) => {\n const { seq } = this.registry.appendOutput(record.sessionId, data);\n this.schedulePersist(record.sessionId);\n this.emitEvent({ type: 'session_output', sessionId: record.sessionId, seq, data });\n },\n onExit: (exitCode) => {\n this.registry.markStopped(record.sessionId, exitCode === 0 ? 'stopped' : 'failed');\n this.runtimes.delete(record.sessionId);\n this.persistNow(record.sessionId);\n this.emitEvent({ type: 'session_exit', sessionId: record.sessionId, exitCode });\n },\n });\n\n this.registry.setLifecycle(record.sessionId, 'starting');\n const pid = runtime.start();\n this.runtimes.set(record.sessionId, runtime);\n const startedRecord = this.registry.markStarted(record.sessionId, pid);\n this.persistNow(record.sessionId);\n this.emitEvent({ type: startEventType, sessionId: record.sessionId, pid });\n return startedRecord;\n }\n}\n","import * as os from 'os';\nimport * as path from 'path';\nimport * as pty from 'node-pty';\nimport type { IPty } from 'node-pty';\nimport type { CreateSessionPayload } from '@adhdev/session-host-core';\n\nif (os.platform() !== 'win32') {\n try {\n const fs = require('fs');\n const ptyDir = path.resolve(path.dirname(require.resolve('node-pty')), '..');\n const platformArch = `${os.platform()}-${os.arch()}`;\n const helper = path.join(ptyDir, 'prebuilds', platformArch, 'spawn-helper');\n if (fs.existsSync(helper)) {\n const stat = fs.statSync(helper);\n if (!(stat.mode & 0o111)) {\n fs.chmodSync(helper, stat.mode | 0o755);\n }\n }\n } catch {\n // best-effort: node-pty still works on most installs without this\n }\n}\n\nexport interface PtyRuntimeOptions {\n sessionId: string;\n payload: CreateSessionPayload;\n onData: (data: string) => void;\n onExit: (exitCode: number | null) => void;\n}\n\nexport class PtySessionRuntime {\n readonly sessionId: string;\n readonly payload: CreateSessionPayload;\n readonly cols: number;\n readonly rows: number;\n\n private ptyProcess: IPty | null = null;\n private onDataCallback: (data: string) => void;\n private onExitCallback: (exitCode: number | null) => void;\n\n constructor(options: PtyRuntimeOptions) {\n this.sessionId = options.sessionId;\n this.payload = options.payload;\n this.cols = options.payload.cols || 120;\n this.rows = options.payload.rows || 40;\n this.onDataCallback = options.onData;\n this.onExitCallback = options.onExit;\n }\n\n start(): number {\n if (this.ptyProcess) return this.ptyProcess.pid;\n\n const command = this.payload.launchCommand.command;\n const args = this.payload.launchCommand.args || [];\n const cwd = this.payload.workspace || process.cwd();\n const env = {\n ...process.env,\n ...(this.payload.launchCommand.env || {}),\n } as Record<string, string>;\n\n this.ptyProcess = pty.spawn(command, args, {\n name: os.platform() === 'win32' ? 'xterm-color' : 'xterm-256color',\n cols: this.cols,\n rows: this.rows,\n cwd,\n env,\n });\n\n this.ptyProcess.onData((data: string) => {\n this.onDataCallback(data);\n });\n\n this.ptyProcess.onExit(({ exitCode }) => {\n this.ptyProcess = null;\n this.onExitCallback(exitCode ?? null);\n });\n\n return this.ptyProcess.pid;\n }\n\n write(data: string): void {\n if (!this.ptyProcess) throw new Error(`Session not running: ${this.sessionId}`);\n this.ptyProcess.write(data);\n }\n\n resize(cols: number, rows: number): void {\n if (!this.ptyProcess) throw new Error(`Session not running: ${this.sessionId}`);\n this.ptyProcess.resize(cols, rows);\n }\n\n stop(): void {\n if (!this.ptyProcess) return;\n this.ptyProcess.kill();\n }\n}\n","import * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\nimport type { SessionBufferSnapshot, SessionHostRecord } from '@adhdev/session-host-core';\n\nexport interface PersistedRuntimeState {\n record: SessionHostRecord;\n snapshot: SessionBufferSnapshot;\n updatedAt: number;\n}\n\ninterface SessionHostStorageOptions {\n appName?: string;\n}\n\nexport class SessionHostStorage {\n private readonly rootDir: string;\n private readonly runtimesDir: string;\n\n constructor(options: SessionHostStorageOptions = {}) {\n const appName = options.appName || 'adhdev';\n this.rootDir = path.join(os.homedir(), '.adhdev', 'session-host', appName);\n this.runtimesDir = path.join(this.rootDir, 'runtimes');\n }\n\n loadAll(): PersistedRuntimeState[] {\n if (!fs.existsSync(this.runtimesDir)) return [];\n const entries = fs.readdirSync(this.runtimesDir, { withFileTypes: true });\n const states: PersistedRuntimeState[] = [];\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.json')) continue;\n const fullPath = path.join(this.runtimesDir, entry.name);\n try {\n const parsed = JSON.parse(fs.readFileSync(fullPath, 'utf8')) as PersistedRuntimeState;\n if (parsed?.record?.sessionId) {\n states.push(parsed);\n }\n } catch {\n // Ignore malformed snapshots; host should still boot.\n }\n }\n return states.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));\n }\n\n save(record: SessionHostRecord, snapshot: SessionBufferSnapshot): void {\n fs.mkdirSync(this.runtimesDir, { recursive: true });\n const filePath = path.join(this.runtimesDir, `${record.sessionId}.json`);\n const payload: PersistedRuntimeState = {\n record,\n snapshot,\n updatedAt: Date.now(),\n };\n fs.writeFileSync(filePath, JSON.stringify(payload, null, 2), 'utf8');\n }\n}\n"],"mappings":"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","names":["import_session_host_core","fs","fs","os","path","resolve"]}
|
|
@@ -157,7 +157,6 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
157
157
|
this.storage = new SessionHostStorage({ appName: options.appName || "adhdev" });
|
|
158
158
|
}
|
|
159
159
|
async start() {
|
|
160
|
-
this.restorePersistedRuntimes();
|
|
161
160
|
if (this.endpoint.kind === "unix") {
|
|
162
161
|
try {
|
|
163
162
|
fs2.unlinkSync(this.endpoint.path);
|
|
@@ -180,6 +179,13 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
180
179
|
this.ipcServer?.listen(this.endpoint.path);
|
|
181
180
|
});
|
|
182
181
|
this.emit("log", `session host endpoint ready: ${this.endpoint.path}`);
|
|
182
|
+
setTimeout(() => {
|
|
183
|
+
try {
|
|
184
|
+
this.restorePersistedRuntimes();
|
|
185
|
+
} catch (error) {
|
|
186
|
+
this.emit("log", `session host restore failed: ${error?.message || String(error)}`);
|
|
187
|
+
}
|
|
188
|
+
}, 0);
|
|
183
189
|
}
|
|
184
190
|
async stop() {
|
|
185
191
|
this.flushAllPersistence();
|
|
@@ -370,6 +376,7 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
370
376
|
}
|
|
371
377
|
restorePersistedRuntimes() {
|
|
372
378
|
const states = this.storage.loadAll();
|
|
379
|
+
const runtimesToResume = [];
|
|
373
380
|
for (const persisted of states) {
|
|
374
381
|
const wasLiveRuntime = !["stopped", "failed"].includes(persisted.record.lifecycle);
|
|
375
382
|
const recoveredRecord = {
|
|
@@ -387,35 +394,38 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
387
394
|
this.registry.restoreSession(recoveredRecord, persisted.snapshot);
|
|
388
395
|
this.storage.save(recoveredRecord, persisted.snapshot);
|
|
389
396
|
if (wasLiveRuntime) {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
397
|
+
runtimesToResume.push({ persisted, recoveredRecord });
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
for (const { persisted, recoveredRecord } of runtimesToResume) {
|
|
401
|
+
try {
|
|
402
|
+
const resumed = this.startRuntime(
|
|
403
|
+
recoveredRecord,
|
|
404
|
+
this.buildPayloadFromRecord(recoveredRecord),
|
|
405
|
+
"session_resumed"
|
|
406
|
+
);
|
|
407
|
+
const resumedMeta = {
|
|
408
|
+
...resumed.meta || {},
|
|
409
|
+
restoredFromStorage: true,
|
|
410
|
+
runtimeRecoveryState: "auto_resumed"
|
|
411
|
+
};
|
|
412
|
+
this.registry.restoreSession(
|
|
413
|
+
{ ...resumed, meta: resumedMeta },
|
|
414
|
+
this.registry.getSnapshot(resumed.sessionId)
|
|
415
|
+
);
|
|
416
|
+
this.persistNow(resumed.sessionId);
|
|
417
|
+
} catch (error) {
|
|
418
|
+
const interrupted = this.registry.setLifecycle(recoveredRecord.sessionId, "interrupted");
|
|
419
|
+
this.registry.restoreSession({
|
|
420
|
+
...interrupted,
|
|
421
|
+
meta: {
|
|
422
|
+
...interrupted.meta || {},
|
|
398
423
|
restoredFromStorage: true,
|
|
399
|
-
runtimeRecoveryState: "
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
);
|
|
405
|
-
this.persistNow(resumed.sessionId);
|
|
406
|
-
} catch (error) {
|
|
407
|
-
const interrupted = this.registry.setLifecycle(recoveredRecord.sessionId, "interrupted");
|
|
408
|
-
this.registry.restoreSession({
|
|
409
|
-
...interrupted,
|
|
410
|
-
meta: {
|
|
411
|
-
...interrupted.meta || {},
|
|
412
|
-
restoredFromStorage: true,
|
|
413
|
-
runtimeRecoveryState: "resume_failed",
|
|
414
|
-
runtimeRecoveryError: error?.message || String(error)
|
|
415
|
-
}
|
|
416
|
-
}, persisted.snapshot);
|
|
417
|
-
this.persistNow(recoveredRecord.sessionId);
|
|
418
|
-
}
|
|
424
|
+
runtimeRecoveryState: "resume_failed",
|
|
425
|
+
runtimeRecoveryError: error?.message || String(error)
|
|
426
|
+
}
|
|
427
|
+
}, persisted.snapshot);
|
|
428
|
+
this.persistNow(recoveredRecord.sessionId);
|
|
419
429
|
}
|
|
420
430
|
}
|
|
421
431
|
}
|