adhdev 0.8.50 → 0.8.52
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 +30 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +30 -5
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/vendor/terminal-mux-cli/index.d.mts +1 -0
- package/vendor/terminal-mux-cli/index.d.ts +1 -0
- package/vendor/terminal-mux-cli/index.js +2056 -0
- package/vendor/terminal-mux-cli/index.mjs +2048 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/session-host-core/index.d.mts +427 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/session-host-core/index.d.ts +427 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/session-host-core/index.js +617 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/session-host-core/index.js.map +1 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/session-host-core/index.mjs +573 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/session-host-core/index.mjs.map +1 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/session-host-core/package.json +7 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/api.d.mts +16 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/api.d.ts +16 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/api.js +206 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/api.mjs +17 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/chunk-7RNMRPVZ.mjs +183 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/chunk-R4EFW6W3.mjs +46 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/chunk-XZWWVN5W.mjs +164 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/control-socket.d.mts +35 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/control-socket.d.ts +35 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/control-socket.js +219 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/control-socket.mjs +13 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/index.d.mts +5 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/index.d.ts +5 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/index.js +427 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/index.mjs +34 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/package.json +33 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/storage.d.mts +49 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/storage.d.ts +49 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/storage.js +222 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/storage.mjs +16 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-core/index.d.mts +162 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-core/index.d.ts +162 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-core/index.js +985 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-core/index.mjs +948 -0
- package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-core/package.json +7 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/buffer.ts","../src/registry.ts","../src/runtime-labels.ts","../src/ipc.ts","../src/spawn-env.ts"],"sourcesContent":["import type { SessionBufferSnapshot } from './types.js';\n\nexport interface SessionRingBufferOptions {\n maxBytes?: number;\n}\n\nexport class SessionRingBuffer {\n private maxBytes: number;\n private chunks: { seq: number; data: string; bytes: number }[] = [];\n private nextSeq = 1;\n private totalBytes = 0;\n\n constructor(options: SessionRingBufferOptions = {}) {\n this.maxBytes = options.maxBytes ?? 512 * 1024;\n }\n\n append(data: string): number {\n const normalized = typeof data === 'string' ? data : String(data ?? '');\n const bytes = Buffer.byteLength(normalized, 'utf8');\n const seq = this.nextSeq++;\n\n this.chunks.push({ seq, data: normalized, bytes });\n this.totalBytes += bytes;\n this.trim();\n return seq;\n }\n\n snapshot(sinceSeq?: number): SessionBufferSnapshot {\n const relevant = typeof sinceSeq === 'number'\n ? this.chunks.filter(chunk => chunk.seq > sinceSeq)\n : this.chunks;\n\n const text = relevant.map(chunk => chunk.data).join('');\n const truncated = !!this.chunks[0] && typeof sinceSeq === 'number' && sinceSeq < this.chunks[0].seq - 1;\n\n return {\n seq: this.nextSeq - 1,\n text,\n truncated,\n };\n }\n\n getState(): { scrollbackBytes: number; snapshotSeq: number } {\n return {\n scrollbackBytes: this.totalBytes,\n snapshotSeq: this.nextSeq - 1,\n };\n }\n\n clear(): void {\n this.chunks = [];\n this.totalBytes = 0;\n this.nextSeq = 1;\n }\n\n restore(snapshot: { seq: number; text: string }): void {\n this.clear();\n const text = String(snapshot.text || '');\n if (!text) {\n this.nextSeq = Math.max(1, Number(snapshot.seq || 0) + 1);\n return;\n }\n const bytes = Buffer.byteLength(text, 'utf8');\n const seq = Math.max(1, Number(snapshot.seq || 1));\n this.chunks = [{ seq, data: text, bytes }];\n this.totalBytes = bytes;\n this.nextSeq = seq + 1;\n this.trim();\n }\n\n private trim(): void {\n while (this.totalBytes > this.maxBytes && this.chunks.length > 1) {\n const removed = this.chunks.shift();\n if (!removed) break;\n this.totalBytes -= removed.bytes;\n }\n }\n}\n","import { randomUUID } from 'crypto';\nimport type {\n AcquireWritePayload,\n AttachSessionPayload,\n CreateSessionPayload,\n DetachSessionPayload,\n ReleaseWritePayload,\n SessionAttachedClient,\n SessionHostRecord,\n} from './types.js';\nimport { SessionRingBuffer } from './buffer.js';\nimport { buildRuntimeDisplayName, buildRuntimeKey, getWorkspaceLabel } from './runtime-labels.js';\n\ninterface SessionRuntimeState {\n record: SessionHostRecord;\n buffer: SessionRingBuffer;\n}\n\nexport class SessionHostRegistry {\n private sessions = new Map<string, SessionRuntimeState>();\n\n createSession(payload: CreateSessionPayload): SessionHostRecord {\n const sessionId = payload.sessionId || randomUUID();\n if (this.sessions.has(sessionId)) {\n throw new Error(`Session already exists: ${sessionId}`);\n }\n const now = Date.now();\n const initialClient = payload.clientId\n ? [{\n clientId: payload.clientId,\n type: payload.clientType || 'daemon',\n readOnly: false,\n attachedAt: now,\n lastSeenAt: now,\n } satisfies SessionAttachedClient]\n : [];\n\n const record: SessionHostRecord = {\n sessionId,\n runtimeKey: buildRuntimeKey(\n payload,\n Array.from(this.sessions.values(), (state) => state.record.runtimeKey),\n ),\n displayName: buildRuntimeDisplayName(payload),\n workspaceLabel: getWorkspaceLabel(payload.workspace),\n transport: 'pty',\n providerType: payload.providerType,\n category: payload.category,\n workspace: payload.workspace,\n launchCommand: payload.launchCommand,\n createdAt: now,\n lastActivityAt: now,\n lifecycle: 'starting',\n writeOwner: null,\n attachedClients: initialClient,\n buffer: {\n scrollbackBytes: 0,\n snapshotSeq: 0,\n },\n meta: payload.meta || {},\n };\n\n record.meta = {\n sessionHostCols: payload.cols || 80,\n sessionHostRows: payload.rows || 24,\n ...record.meta,\n };\n\n this.sessions.set(sessionId, {\n record,\n buffer: new SessionRingBuffer(),\n });\n\n return this.cloneRecord(record);\n }\n\n restoreSession(record: SessionHostRecord, snapshot?: { seq: number; text: string } | null): SessionHostRecord {\n const cloned = this.cloneRecord(record);\n this.sessions.set(cloned.sessionId, {\n record: cloned,\n buffer: (() => {\n const buffer = new SessionRingBuffer();\n if (snapshot) buffer.restore(snapshot);\n return buffer;\n })(),\n });\n return this.cloneRecord(cloned);\n }\n\n listSessions(): SessionHostRecord[] {\n return Array.from(this.sessions.values())\n .map(state => this.cloneRecord(state.record))\n .sort((a, b) => b.lastActivityAt - a.lastActivityAt);\n }\n\n getSession(sessionId: string): SessionHostRecord | null {\n const state = this.sessions.get(sessionId);\n return state ? this.cloneRecord(state.record) : null;\n }\n\n attachClient(payload: AttachSessionPayload): SessionHostRecord {\n const state = this.requireSession(payload.sessionId);\n const now = Date.now();\n let removedDaemonOwner = false;\n\n if (payload.clientType === 'daemon') {\n const staleDaemonClientIds = state.record.attachedClients\n .filter(client => client.type === 'daemon' && client.clientId !== payload.clientId)\n .map(client => client.clientId);\n if (staleDaemonClientIds.length > 0) {\n state.record.attachedClients = state.record.attachedClients.filter(\n client => !(client.type === 'daemon' && client.clientId !== payload.clientId),\n );\n if (state.record.writeOwner && staleDaemonClientIds.includes(state.record.writeOwner.clientId)) {\n removedDaemonOwner = true;\n }\n }\n }\n\n const existing = state.record.attachedClients.find(client => client.clientId === payload.clientId);\n\n if (existing) {\n existing.type = payload.clientType;\n existing.readOnly = !!payload.readOnly;\n existing.lastSeenAt = now;\n } else {\n state.record.attachedClients.push({\n clientId: payload.clientId,\n type: payload.clientType,\n readOnly: !!payload.readOnly,\n attachedAt: now,\n lastSeenAt: now,\n });\n }\n\n if (removedDaemonOwner) {\n state.record.writeOwner = null;\n }\n\n state.record.lastActivityAt = now;\n return this.cloneRecord(state.record);\n }\n\n detachClient(payload: DetachSessionPayload): SessionHostRecord {\n const state = this.requireSession(payload.sessionId);\n state.record.attachedClients = state.record.attachedClients.filter(client => client.clientId !== payload.clientId);\n if (state.record.writeOwner?.clientId === payload.clientId) {\n state.record.writeOwner = null;\n }\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n acquireWrite(payload: AcquireWritePayload): SessionHostRecord {\n const state = this.requireSession(payload.sessionId);\n if (state.record.writeOwner && state.record.writeOwner.clientId !== payload.clientId && !payload.force) {\n throw new Error(`Write owned by ${state.record.writeOwner.clientId}`);\n }\n const attachedClient = state.record.attachedClients.find(client => client.clientId === payload.clientId);\n if (attachedClient) {\n attachedClient.readOnly = false;\n attachedClient.lastSeenAt = Date.now();\n }\n state.record.writeOwner = {\n clientId: payload.clientId,\n ownerType: payload.ownerType,\n acquiredAt: Date.now(),\n };\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n releaseWrite(payload: ReleaseWritePayload): SessionHostRecord {\n const state = this.requireSession(payload.sessionId);\n const attachedClient = state.record.attachedClients.find(client => client.clientId === payload.clientId);\n if (attachedClient) {\n attachedClient.readOnly = false;\n attachedClient.lastSeenAt = Date.now();\n }\n if (state.record.writeOwner?.clientId === payload.clientId) {\n state.record.writeOwner = null;\n }\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n appendOutput(sessionId: string, data: string): { record: SessionHostRecord; seq: number } {\n const state = this.requireSession(sessionId);\n const seq = state.buffer.append(data);\n state.record.buffer = state.buffer.getState();\n state.record.lastActivityAt = Date.now();\n return { record: this.cloneRecord(state.record), seq };\n }\n\n getSnapshot(sessionId: string, sinceSeq?: number) {\n const state = this.requireSession(sessionId);\n state.record.buffer = state.buffer.getState();\n return state.buffer.snapshot(sinceSeq);\n }\n\n clearBuffer(sessionId: string): SessionHostRecord {\n const state = this.requireSession(sessionId);\n state.buffer.clear();\n state.record.buffer = state.buffer.getState();\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n updateSessionMeta(sessionId: string, meta: Record<string, unknown>, replace = false): SessionHostRecord {\n const state = this.requireSession(sessionId);\n state.record.meta = replace\n ? { ...meta }\n : {\n ...(state.record.meta || {}),\n ...meta,\n };\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n markStarted(sessionId: string, pid?: number): SessionHostRecord {\n const state = this.requireSession(sessionId);\n state.record.lifecycle = 'running';\n state.record.startedAt = state.record.startedAt || Date.now();\n if (typeof pid === 'number') state.record.osPid = pid;\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n markStopped(sessionId: string, lifecycle: 'stopped' | 'failed' = 'stopped'): SessionHostRecord {\n const state = this.requireSession(sessionId);\n state.record.lifecycle = lifecycle;\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n setLifecycle(sessionId: string, lifecycle: 'starting' | 'running' | 'stopping' | 'stopped' | 'failed' | 'interrupted'): SessionHostRecord {\n const state = this.requireSession(sessionId);\n state.record.lifecycle = lifecycle;\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n deleteSession(sessionId: string): boolean {\n return this.sessions.delete(sessionId);\n }\n\n private requireSession(sessionId: string): SessionRuntimeState {\n const state = this.sessions.get(sessionId);\n if (!state) throw new Error(`Unknown session: ${sessionId}`);\n return state;\n }\n\n private cloneRecord(record: SessionHostRecord): SessionHostRecord {\n return {\n ...record,\n launchCommand: {\n ...record.launchCommand,\n args: [...record.launchCommand.args],\n env: record.launchCommand.env ? { ...record.launchCommand.env } : undefined,\n },\n writeOwner: record.writeOwner ? { ...record.writeOwner } : null,\n attachedClients: record.attachedClients.map(client => ({ ...client })),\n buffer: { ...record.buffer },\n meta: { ...record.meta },\n };\n }\n}\n","import * as path from 'path';\nimport type { CreateSessionPayload, SessionHostRecord } from './types.js';\n\nfunction normalizeSlug(input: string): string {\n return input\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 48);\n}\n\nfunction normalizeValue(input: string): string {\n return input.trim().toLowerCase();\n}\n\nexport function getWorkspaceLabel(workspace: string): string {\n const trimmed = workspace.trim();\n if (!trimmed) return 'workspace';\n const normalized = trimmed.replace(/[\\\\/]+$/, '');\n const base = path.basename(normalized);\n return base || normalized;\n}\n\nexport function buildRuntimeDisplayName(payload: Pick<CreateSessionPayload, 'displayName' | 'providerType' | 'workspace'>): string {\n const explicit = payload.displayName?.trim();\n if (explicit) return explicit;\n const workspaceLabel = getWorkspaceLabel(payload.workspace);\n const providerLabel = payload.providerType.trim() || 'runtime';\n return `${providerLabel} @ ${workspaceLabel}`;\n}\n\nexport function buildRuntimeKey(\n payload: Pick<CreateSessionPayload, 'runtimeKey' | 'displayName' | 'providerType' | 'workspace'>,\n existingKeys: Iterable<string>,\n): string {\n const requested = payload.runtimeKey?.trim();\n const existing = new Set(Array.from(existingKeys, (key) => key.toLowerCase()));\n const displayName = buildRuntimeDisplayName(payload);\n const baseKey = normalizeSlug(requested || displayName || getWorkspaceLabel(payload.workspace) || payload.providerType || 'runtime') || 'runtime';\n if (!existing.has(baseKey)) return baseKey;\n\n let suffix = 2;\n let candidate = `${baseKey}-${suffix}`;\n while (existing.has(candidate)) {\n suffix += 1;\n candidate = `${baseKey}-${suffix}`;\n }\n return candidate;\n}\n\nfunction uniqueMatch(records: SessionHostRecord[], predicate: (record: SessionHostRecord) => boolean): SessionHostRecord | null {\n const matches = records.filter(predicate);\n if (matches.length === 1) return matches[0] || null;\n if (matches.length === 0) return null;\n const labels = matches.map((record) => `${record.runtimeKey} (${record.sessionId})`).join(', ');\n throw new Error(`Ambiguous runtime target. Matches: ${labels}`);\n}\n\nexport function resolveRuntimeRecord(records: SessionHostRecord[], identifier: string): SessionHostRecord {\n const target = identifier.trim();\n if (!target) {\n throw new Error('Runtime target is required');\n }\n\n const exact = uniqueMatch(records, (record) =>\n record.sessionId === target ||\n normalizeValue(record.runtimeKey) === normalizeValue(target) ||\n normalizeValue(record.displayName) === normalizeValue(target),\n );\n if (exact) return exact;\n\n const prefix = uniqueMatch(records, (record) =>\n record.sessionId.startsWith(target) ||\n normalizeValue(record.runtimeKey).startsWith(normalizeValue(target)),\n );\n if (prefix) return prefix;\n\n throw new Error(`Unknown runtime target: ${target}`);\n}\n\nexport function formatRuntimeOwner(record: Pick<SessionHostRecord, 'writeOwner'>): string {\n if (!record.writeOwner) return 'none';\n return `${record.writeOwner.ownerType}:${record.writeOwner.clientId}`;\n}\n","import * as os from 'os';\nimport * as path from 'path';\nimport * as net from 'net';\nimport { randomUUID } from 'crypto';\nimport type {\n SessionHostEvent,\n SessionHostRequest,\n SessionHostRequestEnvelope,\n SessionHostResponse,\n SessionHostResponseEnvelope,\n SessionHostWireEnvelope,\n} from './types.js';\n\nexport interface SessionHostEndpoint {\n kind: 'unix' | 'pipe';\n path: string;\n}\n\nexport function getDefaultSessionHostEndpoint(appName = 'adhdev'): SessionHostEndpoint {\n if (process.platform === 'win32') {\n return {\n kind: 'pipe',\n path: `\\\\\\\\.\\\\pipe\\\\${appName}-session-host`,\n };\n }\n\n return {\n kind: 'unix',\n path: path.join(os.tmpdir(), `${appName}-session-host.sock`),\n };\n}\n\nfunction serializeEnvelope(envelope: SessionHostWireEnvelope): string {\n return `${JSON.stringify(envelope)}\\n`;\n}\n\nfunction createLineParser(onEnvelope: (envelope: SessionHostWireEnvelope) => void) {\n let buffer = '';\n return (chunk: Buffer | string) => {\n buffer += chunk.toString();\n let newlineIndex = buffer.indexOf('\\n');\n while (newlineIndex >= 0) {\n const rawLine = buffer.slice(0, newlineIndex).trim();\n buffer = buffer.slice(newlineIndex + 1);\n if (rawLine) {\n onEnvelope(JSON.parse(rawLine) as SessionHostWireEnvelope);\n }\n newlineIndex = buffer.indexOf('\\n');\n }\n };\n}\n\nexport interface SessionHostClientOptions {\n endpoint?: SessionHostEndpoint;\n appName?: string;\n}\n\nexport class SessionHostClient {\n readonly endpoint: SessionHostEndpoint;\n\n private socket: net.Socket | null = null;\n private requestWaiters = new Map<string, { resolve: (value: SessionHostResponse) => void; reject: (error: Error) => void }>();\n private eventListeners = new Set<(event: SessionHostEvent) => void>();\n\n constructor(options: SessionHostClientOptions = {}) {\n this.endpoint = options.endpoint || getDefaultSessionHostEndpoint(options.appName || 'adhdev');\n }\n\n async connect(): Promise<void> {\n if (this.socket && !this.socket.destroyed) return;\n // Cleanup stale socket reference left after error/disconnect\n if (this.socket) {\n try { this.socket.destroy(); } catch { /* noop */ }\n this.socket = null;\n }\n\n const socket = net.createConnection(this.endpoint.path);\n this.socket = socket;\n\n socket.on('data', createLineParser((envelope) => {\n if (envelope.kind === 'response') {\n const waiter = this.requestWaiters.get(envelope.requestId);\n if (waiter) {\n this.requestWaiters.delete(envelope.requestId);\n waiter.resolve(envelope.response);\n }\n return;\n }\n\n if (envelope.kind === 'event') {\n for (const listener of this.eventListeners) listener(envelope.event);\n }\n }));\n\n socket.on('error', (error) => {\n for (const waiter of this.requestWaiters.values()) {\n waiter.reject(error);\n }\n this.requestWaiters.clear();\n // Clear the dead socket reference so the next connect() creates a fresh connection\n // instead of skipping reconnection on the `!this.socket.destroyed` guard.\n if (this.socket === socket) {\n this.socket = null;\n }\n try { socket.destroy(); } catch { /* noop */ }\n });\n\n await new Promise<void>((resolve, reject) => {\n socket.once('connect', () => resolve());\n socket.once('error', reject);\n });\n }\n\n onEvent(listener: (event: SessionHostEvent) => void): () => void {\n this.eventListeners.add(listener);\n return () => {\n this.eventListeners.delete(listener);\n };\n }\n\n async request<T = unknown>(request: SessionHostRequest): Promise<SessionHostResponse<T>> {\n await this.connect();\n if (!this.socket) throw new Error('Session host socket unavailable');\n\n const requestId = randomUUID();\n const envelope: SessionHostRequestEnvelope = {\n kind: 'request',\n requestId,\n request,\n };\n\n const response = await new Promise<SessionHostResponse>((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.requestWaiters.delete(requestId);\n reject(new Error(`Session host request timed out after 30s (${request.type})`));\n }, 30_000);\n this.requestWaiters.set(requestId, {\n resolve: (value) => { clearTimeout(timeout); resolve(value); },\n reject: (error) => { clearTimeout(timeout); reject(error); },\n });\n this.socket?.write(serializeEnvelope(envelope));\n });\n\n return response as SessionHostResponse<T>;\n }\n\n async close(): Promise<void> {\n if (!this.socket) return;\n const socket = this.socket;\n this.socket = null;\n for (const waiter of this.requestWaiters.values()) {\n waiter.reject(new Error('Session host client closed'));\n }\n this.requestWaiters.clear();\n await new Promise<void>((resolve) => {\n let settled = false;\n const done = () => {\n if (settled) return;\n settled = true;\n resolve();\n };\n socket.once('close', done);\n socket.end();\n socket.destroy();\n setTimeout(done, 50);\n });\n }\n}\n\nexport function createResponseEnvelope(requestId: string, response: SessionHostResponse): SessionHostResponseEnvelope {\n return {\n kind: 'response',\n requestId,\n response,\n };\n}\n\nexport function writeEnvelope(socket: Pick<net.Socket, 'write'>, envelope: SessionHostWireEnvelope): void {\n socket.write(serializeEnvelope(envelope));\n}\n\nexport { createLineParser };\n","/**\n * Shared PTY spawn environment utilities.\n *\n * Centralises npm/pnpm/yarn env variable stripping, terminal colour env\n * injection, and node-pty spawn-helper permission fixing.\n *\n * Used by daemon-core (provider-cli-adapter), session-host-daemon (runtime),\n * and daemon-cloud (session-host).\n */\n\nimport * as os from 'os';\nimport * as path from 'path';\n\n/**\n * Strip package-manager injected environment variables that can interfere\n * with child CLI processes and apply terminal colour defaults.\n */\nexport function sanitizeSpawnEnv(\n baseEnv: NodeJS.ProcessEnv,\n overrides?: Record<string, string>,\n): Record<string, string> {\n const env: Record<string, string> = {};\n const source = { ...baseEnv, ...(overrides || {}) } as NodeJS.ProcessEnv;\n\n for (const [key, value] of Object.entries(source)) {\n if (typeof value !== 'string') continue;\n env[key] = value;\n }\n\n for (const key of Object.keys(env)) {\n if (\n key === 'INIT_CWD'\n || key === 'npm_command'\n || key === 'npm_execpath'\n || key === 'npm_node_execpath'\n || key.startsWith('npm_')\n || key.startsWith('npm_config_')\n || key.startsWith('npm_package_')\n || key.startsWith('npm_lifecycle_')\n || key.startsWith('PNPM_')\n || key.startsWith('YARN_')\n || key.startsWith('BUN_')\n || key.startsWith('VSCODE_')\n || key.startsWith('ELECTRON_')\n ) {\n delete env[key];\n }\n }\n\n // Do not leak the parent Codex app/extension thread identity into child CLIs.\n // Those variables cause codex-cli to attach to the current IDE thread instead\n // of creating or resuming its own standalone CLI thread, which breaks\n // providerSessionId discovery and reconnect semantics.\n delete env.CODEX_THREAD_ID;\n delete env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE;\n\n applyTerminalColorEnv(env);\n return env;\n}\n\n/**\n * Apply preferred terminal colour environment variables.\n * Ensures TERM is set to xterm-256color and enables colour on Windows.\n */\nexport function applyTerminalColorEnv(env: Record<string, string>): void {\n if (env.NO_COLOR) return;\n\n if (!env.TERM || env.TERM === 'xterm-color') {\n env.TERM = 'xterm-256color';\n }\n if (!env.COLORTERM) env.COLORTERM = 'truecolor';\n\n if (process.platform === 'win32') {\n if (!env.FORCE_COLOR) env.FORCE_COLOR = '1';\n if (!env.CLICOLOR) env.CLICOLOR = '1';\n }\n}\n\n/**\n * Ensure node-pty's spawn-helper binary has execute permissions.\n *\n * npm's default umask can strip +x from the prebuilt spawn-helper on macOS/Linux,\n * causing EACCES when node-pty tries to fork. Best-effort fix.\n *\n * @param logFn Optional log callback for reporting the fix.\n */\nexport function ensureNodePtySpawnHelperPermissions(\n logFn?: (msg: string) => void,\n): void {\n if (os.platform() === 'win32') return;\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 logFn?.(`Fixed spawn-helper permissions: ${helper}`);\n }\n }\n } catch {\n // best-effort: node-pty still works on most installs without this\n }\n}\n"],"mappings":"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","names":["path","randomUUID","resolve","os","path"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SessionHostResponse } from '@adhdev/session-host-core';
|
|
2
|
+
import { AdhMuxControlRequest } from './control-socket.mjs';
|
|
3
|
+
export { AdhMuxControlClient, AdhMuxControlEvent, withAdhMuxControlClient } from './control-socket.mjs';
|
|
4
|
+
import { WorkspaceControlEndpoint } from './storage.mjs';
|
|
5
|
+
import '@adhdev/terminal-mux-core';
|
|
6
|
+
|
|
7
|
+
interface AdhMuxSocketInfo {
|
|
8
|
+
workspaceName: string;
|
|
9
|
+
live: boolean;
|
|
10
|
+
endpoint: WorkspaceControlEndpoint;
|
|
11
|
+
}
|
|
12
|
+
declare function requestWorkspaceControl<T = unknown>(workspaceName: string, request: AdhMuxControlRequest): Promise<SessionHostResponse<T> | null>;
|
|
13
|
+
declare function getWorkspaceState<T = unknown>(workspaceName: string): Promise<SessionHostResponse<T> | null>;
|
|
14
|
+
declare function getWorkspaceSocketInfo(workspaceName: string): Promise<AdhMuxSocketInfo>;
|
|
15
|
+
|
|
16
|
+
export { AdhMuxControlRequest, type AdhMuxSocketInfo, getWorkspaceSocketInfo, getWorkspaceState, requestWorkspaceControl };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SessionHostResponse } from '@adhdev/session-host-core';
|
|
2
|
+
import { AdhMuxControlRequest } from './control-socket.js';
|
|
3
|
+
export { AdhMuxControlClient, AdhMuxControlEvent, withAdhMuxControlClient } from './control-socket.js';
|
|
4
|
+
import { WorkspaceControlEndpoint } from './storage.js';
|
|
5
|
+
import '@adhdev/terminal-mux-core';
|
|
6
|
+
|
|
7
|
+
interface AdhMuxSocketInfo {
|
|
8
|
+
workspaceName: string;
|
|
9
|
+
live: boolean;
|
|
10
|
+
endpoint: WorkspaceControlEndpoint;
|
|
11
|
+
}
|
|
12
|
+
declare function requestWorkspaceControl<T = unknown>(workspaceName: string, request: AdhMuxControlRequest): Promise<SessionHostResponse<T> | null>;
|
|
13
|
+
declare function getWorkspaceState<T = unknown>(workspaceName: string): Promise<SessionHostResponse<T> | null>;
|
|
14
|
+
declare function getWorkspaceSocketInfo(workspaceName: string): Promise<AdhMuxSocketInfo>;
|
|
15
|
+
|
|
16
|
+
export { AdhMuxControlRequest, type AdhMuxSocketInfo, getWorkspaceSocketInfo, getWorkspaceState, requestWorkspaceControl };
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/api.ts
|
|
31
|
+
var api_exports = {};
|
|
32
|
+
__export(api_exports, {
|
|
33
|
+
AdhMuxControlClient: () => AdhMuxControlClient,
|
|
34
|
+
getWorkspaceSocketInfo: () => getWorkspaceSocketInfo,
|
|
35
|
+
getWorkspaceState: () => getWorkspaceState,
|
|
36
|
+
requestWorkspaceControl: () => requestWorkspaceControl,
|
|
37
|
+
withAdhMuxControlClient: () => withAdhMuxControlClient
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(api_exports);
|
|
40
|
+
|
|
41
|
+
// src/control-socket.ts
|
|
42
|
+
var import_net = __toESM(require("net"));
|
|
43
|
+
|
|
44
|
+
// src/storage.ts
|
|
45
|
+
var import_os = __toESM(require("os"));
|
|
46
|
+
var import_path = __toESM(require("path"));
|
|
47
|
+
function sanitizeWorkspaceName(name) {
|
|
48
|
+
return name.trim().replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
49
|
+
}
|
|
50
|
+
function getWorkspaceControlEndpoint(name) {
|
|
51
|
+
const sanitized = sanitizeWorkspaceName(name);
|
|
52
|
+
if (process.platform === "win32") {
|
|
53
|
+
return {
|
|
54
|
+
kind: "pipe",
|
|
55
|
+
path: `\\\\.\\pipe\\adhmux-${sanitized}`
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
kind: "unix",
|
|
60
|
+
path: import_path.default.join(import_os.default.tmpdir(), `adhmux-${sanitized}.sock`)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/control-socket.ts
|
|
65
|
+
function serializeEnvelope(envelope) {
|
|
66
|
+
return `${JSON.stringify(envelope)}
|
|
67
|
+
`;
|
|
68
|
+
}
|
|
69
|
+
function createControlLineParser(onEnvelope) {
|
|
70
|
+
let buffer = "";
|
|
71
|
+
return (chunk) => {
|
|
72
|
+
buffer += chunk.toString();
|
|
73
|
+
let newlineIndex = buffer.indexOf("\n");
|
|
74
|
+
while (newlineIndex >= 0) {
|
|
75
|
+
const rawLine = buffer.slice(0, newlineIndex).trim();
|
|
76
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
77
|
+
if (rawLine) {
|
|
78
|
+
onEnvelope(JSON.parse(rawLine));
|
|
79
|
+
}
|
|
80
|
+
newlineIndex = buffer.indexOf("\n");
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
var AdhMuxControlClient = class {
|
|
85
|
+
endpoint;
|
|
86
|
+
socket = null;
|
|
87
|
+
waiters = /* @__PURE__ */ new Map();
|
|
88
|
+
eventListeners = /* @__PURE__ */ new Set();
|
|
89
|
+
constructor(workspaceName) {
|
|
90
|
+
this.endpoint = getWorkspaceControlEndpoint(workspaceName);
|
|
91
|
+
}
|
|
92
|
+
async connect() {
|
|
93
|
+
if (this.socket && !this.socket.destroyed) return;
|
|
94
|
+
const socket = import_net.default.createConnection(this.endpoint.path);
|
|
95
|
+
this.socket = socket;
|
|
96
|
+
socket.on("data", createControlLineParser((envelope) => {
|
|
97
|
+
if (envelope.kind === "response") {
|
|
98
|
+
const waiter = this.waiters.get(envelope.requestId);
|
|
99
|
+
if (!waiter) return;
|
|
100
|
+
this.waiters.delete(envelope.requestId);
|
|
101
|
+
waiter.resolve(envelope.response);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (envelope.kind === "event") {
|
|
105
|
+
for (const listener of this.eventListeners) listener(envelope.event);
|
|
106
|
+
}
|
|
107
|
+
}));
|
|
108
|
+
socket.on("error", (error) => {
|
|
109
|
+
for (const waiter of this.waiters.values()) {
|
|
110
|
+
waiter.reject(error);
|
|
111
|
+
}
|
|
112
|
+
this.waiters.clear();
|
|
113
|
+
});
|
|
114
|
+
await new Promise((resolve, reject) => {
|
|
115
|
+
socket.once("connect", () => resolve());
|
|
116
|
+
socket.once("error", reject);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
async request(request) {
|
|
120
|
+
await this.connect();
|
|
121
|
+
if (!this.socket) throw new Error("adhmux control socket unavailable");
|
|
122
|
+
const requestId = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
123
|
+
const envelope = { kind: "request", requestId, request };
|
|
124
|
+
const response = await new Promise((resolve, reject) => {
|
|
125
|
+
this.waiters.set(requestId, { resolve, reject });
|
|
126
|
+
this.socket?.write(serializeEnvelope(envelope));
|
|
127
|
+
});
|
|
128
|
+
return response;
|
|
129
|
+
}
|
|
130
|
+
onEvent(listener) {
|
|
131
|
+
this.eventListeners.add(listener);
|
|
132
|
+
return () => {
|
|
133
|
+
this.eventListeners.delete(listener);
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
async close() {
|
|
137
|
+
if (!this.socket) return;
|
|
138
|
+
const socket = this.socket;
|
|
139
|
+
this.socket = null;
|
|
140
|
+
for (const waiter of this.waiters.values()) {
|
|
141
|
+
waiter.reject(new Error("adhmux control client closed"));
|
|
142
|
+
}
|
|
143
|
+
this.waiters.clear();
|
|
144
|
+
await new Promise((resolve) => {
|
|
145
|
+
let settled = false;
|
|
146
|
+
const done = () => {
|
|
147
|
+
if (settled) return;
|
|
148
|
+
settled = true;
|
|
149
|
+
resolve();
|
|
150
|
+
};
|
|
151
|
+
socket.once("close", done);
|
|
152
|
+
socket.end();
|
|
153
|
+
socket.destroy();
|
|
154
|
+
setTimeout(done, 50);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
async function withAdhMuxControlClient(workspaceName, fn) {
|
|
159
|
+
const client = new AdhMuxControlClient(workspaceName);
|
|
160
|
+
try {
|
|
161
|
+
return await fn(client);
|
|
162
|
+
} finally {
|
|
163
|
+
await client.close();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/api.ts
|
|
168
|
+
function isControlSocketUnavailable(error) {
|
|
169
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
170
|
+
return /ENOENT|ECONNREFUSED|EPIPE|socket/i.test(message);
|
|
171
|
+
}
|
|
172
|
+
async function requestWorkspaceControl(workspaceName, request) {
|
|
173
|
+
try {
|
|
174
|
+
return await withAdhMuxControlClient(workspaceName, (client) => client.request(request));
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (isControlSocketUnavailable(error)) return null;
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async function getWorkspaceState(workspaceName) {
|
|
181
|
+
return requestWorkspaceControl(workspaceName, { type: "workspace_state" });
|
|
182
|
+
}
|
|
183
|
+
async function getWorkspaceSocketInfo(workspaceName) {
|
|
184
|
+
const endpoint = getWorkspaceControlEndpoint(workspaceName);
|
|
185
|
+
const client = new AdhMuxControlClient(workspaceName);
|
|
186
|
+
try {
|
|
187
|
+
await client.connect();
|
|
188
|
+
return { workspaceName, live: true, endpoint };
|
|
189
|
+
} catch (error) {
|
|
190
|
+
if (isControlSocketUnavailable(error)) {
|
|
191
|
+
return { workspaceName, live: false, endpoint };
|
|
192
|
+
}
|
|
193
|
+
throw error;
|
|
194
|
+
} finally {
|
|
195
|
+
await client.close().catch(() => {
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
200
|
+
0 && (module.exports = {
|
|
201
|
+
AdhMuxControlClient,
|
|
202
|
+
getWorkspaceSocketInfo,
|
|
203
|
+
getWorkspaceState,
|
|
204
|
+
requestWorkspaceControl,
|
|
205
|
+
withAdhMuxControlClient
|
|
206
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getWorkspaceSocketInfo,
|
|
3
|
+
getWorkspaceState,
|
|
4
|
+
requestWorkspaceControl
|
|
5
|
+
} from "./chunk-R4EFW6W3.mjs";
|
|
6
|
+
import {
|
|
7
|
+
AdhMuxControlClient,
|
|
8
|
+
withAdhMuxControlClient
|
|
9
|
+
} from "./chunk-XZWWVN5W.mjs";
|
|
10
|
+
import "./chunk-7RNMRPVZ.mjs";
|
|
11
|
+
export {
|
|
12
|
+
AdhMuxControlClient,
|
|
13
|
+
getWorkspaceSocketInfo,
|
|
14
|
+
getWorkspaceState,
|
|
15
|
+
requestWorkspaceControl,
|
|
16
|
+
withAdhMuxControlClient
|
|
17
|
+
};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// src/storage.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import path from "path";
|
|
5
|
+
function getRootDir() {
|
|
6
|
+
return path.join(os.homedir(), ".adhdev", "terminal-mux");
|
|
7
|
+
}
|
|
8
|
+
var WINDOW_MARKER = "--w--";
|
|
9
|
+
function getWorkspacesDir() {
|
|
10
|
+
return path.join(getRootDir(), "workspaces");
|
|
11
|
+
}
|
|
12
|
+
function getStatePath() {
|
|
13
|
+
return path.join(getRootDir(), "state.json");
|
|
14
|
+
}
|
|
15
|
+
function sanitizeWorkspaceName(name) {
|
|
16
|
+
return name.trim().replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
17
|
+
}
|
|
18
|
+
function toWorkspaceRef(name) {
|
|
19
|
+
const workspaceName = sanitizeWorkspaceName(name);
|
|
20
|
+
const markerIndex = workspaceName.indexOf(WINDOW_MARKER);
|
|
21
|
+
if (markerIndex < 0) {
|
|
22
|
+
return {
|
|
23
|
+
sessionName: workspaceName,
|
|
24
|
+
windowName: workspaceName,
|
|
25
|
+
workspaceName
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
sessionName: workspaceName.slice(0, markerIndex),
|
|
30
|
+
windowName: workspaceName.slice(markerIndex + WINDOW_MARKER.length),
|
|
31
|
+
workspaceName
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function buildWorkspaceName(sessionName, windowName) {
|
|
35
|
+
const session = sanitizeWorkspaceName(sessionName);
|
|
36
|
+
const window = sanitizeWorkspaceName(windowName);
|
|
37
|
+
if (!session || session === window) return window;
|
|
38
|
+
return `${session}${WINDOW_MARKER}${window}`;
|
|
39
|
+
}
|
|
40
|
+
function getWorkspaceControlEndpoint(name) {
|
|
41
|
+
const sanitized = sanitizeWorkspaceName(name);
|
|
42
|
+
if (process.platform === "win32") {
|
|
43
|
+
return {
|
|
44
|
+
kind: "pipe",
|
|
45
|
+
path: `\\\\.\\pipe\\adhmux-${sanitized}`
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
kind: "unix",
|
|
50
|
+
path: path.join(os.tmpdir(), `adhmux-${sanitized}.sock`)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
var TerminalMuxStorage = class {
|
|
54
|
+
workspacesDir = getWorkspacesDir();
|
|
55
|
+
statePath = getStatePath();
|
|
56
|
+
listWorkspaceNames() {
|
|
57
|
+
return this.listWorkspaces().map((workspace) => workspace.name);
|
|
58
|
+
}
|
|
59
|
+
listWorkspaces() {
|
|
60
|
+
if (!fs.existsSync(this.workspacesDir)) return [];
|
|
61
|
+
return fs.readdirSync(this.workspacesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).flatMap((entry) => {
|
|
62
|
+
const name = entry.name.replace(/\.json$/, "");
|
|
63
|
+
const filePath = this.getWorkspacePath(name);
|
|
64
|
+
let title = name;
|
|
65
|
+
let paneCount = 0;
|
|
66
|
+
let updatedAt = 0;
|
|
67
|
+
try {
|
|
68
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
69
|
+
title = parsed.title || name;
|
|
70
|
+
paneCount = Object.keys(parsed.panes || {}).length;
|
|
71
|
+
updatedAt = fs.statSync(filePath).mtimeMs;
|
|
72
|
+
} catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
return [{
|
|
76
|
+
name,
|
|
77
|
+
title,
|
|
78
|
+
paneCount,
|
|
79
|
+
updatedAt
|
|
80
|
+
}];
|
|
81
|
+
}).sort((a, b) => b.updatedAt - a.updatedAt || a.name.localeCompare(b.name));
|
|
82
|
+
}
|
|
83
|
+
listSessions() {
|
|
84
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
85
|
+
for (const workspace of this.listWorkspaces()) {
|
|
86
|
+
const ref = toWorkspaceRef(workspace.name);
|
|
87
|
+
const items = grouped.get(ref.sessionName) || [];
|
|
88
|
+
items.push(workspace);
|
|
89
|
+
grouped.set(ref.sessionName, items);
|
|
90
|
+
}
|
|
91
|
+
return Array.from(grouped.entries()).map(([sessionName, workspaces]) => {
|
|
92
|
+
const sorted = [...workspaces].sort((a, b) => b.updatedAt - a.updatedAt || a.name.localeCompare(b.name));
|
|
93
|
+
const active = sorted[0];
|
|
94
|
+
return {
|
|
95
|
+
name: sessionName,
|
|
96
|
+
title: toWorkspaceRef(active.name).sessionName,
|
|
97
|
+
windowCount: workspaces.length,
|
|
98
|
+
updatedAt: active.updatedAt,
|
|
99
|
+
activeWindowName: toWorkspaceRef(active.name).windowName
|
|
100
|
+
};
|
|
101
|
+
}).sort((a, b) => b.updatedAt - a.updatedAt || a.name.localeCompare(b.name));
|
|
102
|
+
}
|
|
103
|
+
listSessionWindows(sessionName) {
|
|
104
|
+
const normalizedSession = sanitizeWorkspaceName(sessionName);
|
|
105
|
+
return this.listWorkspaces().filter((workspace) => toWorkspaceRef(workspace.name).sessionName === normalizedSession);
|
|
106
|
+
}
|
|
107
|
+
resolveSessionWindowWorkspace(sessionName, windowName) {
|
|
108
|
+
const windows = this.listSessionWindows(sessionName);
|
|
109
|
+
if (windows.length === 0) return null;
|
|
110
|
+
if (!windowName) return windows[0].name;
|
|
111
|
+
const normalizedWindow = sanitizeWorkspaceName(windowName);
|
|
112
|
+
const exact = windows.find((workspace) => toWorkspaceRef(workspace.name).windowName === normalizedWindow);
|
|
113
|
+
return exact?.name || null;
|
|
114
|
+
}
|
|
115
|
+
loadWorkspace(name) {
|
|
116
|
+
const filePath = this.getWorkspacePath(name);
|
|
117
|
+
if (!fs.existsSync(filePath)) return null;
|
|
118
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
119
|
+
return parsed?.workspaceId ? parsed : null;
|
|
120
|
+
}
|
|
121
|
+
saveWorkspace(name, workspace) {
|
|
122
|
+
fs.mkdirSync(this.workspacesDir, { recursive: true });
|
|
123
|
+
fs.writeFileSync(this.getWorkspacePath(name), JSON.stringify(workspace, null, 2), "utf8");
|
|
124
|
+
}
|
|
125
|
+
renameWorkspace(fromName, toName) {
|
|
126
|
+
const fromPath = this.getWorkspacePath(fromName);
|
|
127
|
+
const toPath = this.getWorkspacePath(toName);
|
|
128
|
+
if (!fs.existsSync(fromPath)) {
|
|
129
|
+
throw new Error(`Workspace not found: ${fromName}`);
|
|
130
|
+
}
|
|
131
|
+
fs.mkdirSync(this.workspacesDir, { recursive: true });
|
|
132
|
+
fs.renameSync(fromPath, toPath);
|
|
133
|
+
}
|
|
134
|
+
deleteWorkspace(name) {
|
|
135
|
+
const filePath = this.getWorkspacePath(name);
|
|
136
|
+
if (!fs.existsSync(filePath)) {
|
|
137
|
+
throw new Error(`Workspace not found: ${name}`);
|
|
138
|
+
}
|
|
139
|
+
fs.unlinkSync(filePath);
|
|
140
|
+
const state = this.loadClientState();
|
|
141
|
+
if (state.lastWorkspace === sanitizeWorkspaceName(name)) {
|
|
142
|
+
this.saveClientState({ ...state, lastWorkspace: void 0, updatedAt: Date.now() });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
getLastWorkspace() {
|
|
146
|
+
const state = this.loadClientState();
|
|
147
|
+
return state.lastWorkspace || null;
|
|
148
|
+
}
|
|
149
|
+
setLastWorkspace(name) {
|
|
150
|
+
this.saveClientState({
|
|
151
|
+
...this.loadClientState(),
|
|
152
|
+
lastWorkspace: sanitizeWorkspaceName(name),
|
|
153
|
+
updatedAt: Date.now()
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
getWorkspacePath(name) {
|
|
157
|
+
return path.join(this.workspacesDir, `${sanitizeWorkspaceName(name)}.json`);
|
|
158
|
+
}
|
|
159
|
+
loadClientState() {
|
|
160
|
+
if (!fs.existsSync(this.statePath)) {
|
|
161
|
+
return { updatedAt: 0 };
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const parsed = JSON.parse(fs.readFileSync(this.statePath, "utf8"));
|
|
165
|
+
return typeof parsed === "object" && parsed ? parsed : { updatedAt: 0 };
|
|
166
|
+
} catch {
|
|
167
|
+
return { updatedAt: 0 };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
saveClientState(state) {
|
|
171
|
+
fs.mkdirSync(getRootDir(), { recursive: true });
|
|
172
|
+
fs.writeFileSync(this.statePath, JSON.stringify(state, null, 2), "utf8");
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export {
|
|
177
|
+
getRootDir,
|
|
178
|
+
sanitizeWorkspaceName,
|
|
179
|
+
toWorkspaceRef,
|
|
180
|
+
buildWorkspaceName,
|
|
181
|
+
getWorkspaceControlEndpoint,
|
|
182
|
+
TerminalMuxStorage
|
|
183
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AdhMuxControlClient,
|
|
3
|
+
withAdhMuxControlClient
|
|
4
|
+
} from "./chunk-XZWWVN5W.mjs";
|
|
5
|
+
import {
|
|
6
|
+
getWorkspaceControlEndpoint
|
|
7
|
+
} from "./chunk-7RNMRPVZ.mjs";
|
|
8
|
+
|
|
9
|
+
// src/api.ts
|
|
10
|
+
function isControlSocketUnavailable(error) {
|
|
11
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12
|
+
return /ENOENT|ECONNREFUSED|EPIPE|socket/i.test(message);
|
|
13
|
+
}
|
|
14
|
+
async function requestWorkspaceControl(workspaceName, request) {
|
|
15
|
+
try {
|
|
16
|
+
return await withAdhMuxControlClient(workspaceName, (client) => client.request(request));
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (isControlSocketUnavailable(error)) return null;
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function getWorkspaceState(workspaceName) {
|
|
23
|
+
return requestWorkspaceControl(workspaceName, { type: "workspace_state" });
|
|
24
|
+
}
|
|
25
|
+
async function getWorkspaceSocketInfo(workspaceName) {
|
|
26
|
+
const endpoint = getWorkspaceControlEndpoint(workspaceName);
|
|
27
|
+
const client = new AdhMuxControlClient(workspaceName);
|
|
28
|
+
try {
|
|
29
|
+
await client.connect();
|
|
30
|
+
return { workspaceName, live: true, endpoint };
|
|
31
|
+
} catch (error) {
|
|
32
|
+
if (isControlSocketUnavailable(error)) {
|
|
33
|
+
return { workspaceName, live: false, endpoint };
|
|
34
|
+
}
|
|
35
|
+
throw error;
|
|
36
|
+
} finally {
|
|
37
|
+
await client.close().catch(() => {
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export {
|
|
43
|
+
requestWorkspaceControl,
|
|
44
|
+
getWorkspaceState,
|
|
45
|
+
getWorkspaceSocketInfo
|
|
46
|
+
};
|