@adhdev/daemon-core 0.8.12 → 0.8.14
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-adapters/spawn-env.d.ts +8 -0
- package/dist/cli-adapters/terminal-screen.d.ts +5 -3
- package/dist/detection/cli-detector.d.ts +1 -1
- package/dist/index.js +347 -236
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +343 -229
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +5 -3
- package/dist/providers/contracts.d.ts +38 -0
- package/node_modules/@adhdev/session-host-core/dist/index.d.mts +30 -1
- package/node_modules/@adhdev/session-host-core/dist/index.d.ts +30 -1
- package/node_modules/@adhdev/session-host-core/dist/index.js +73 -6
- package/node_modules/@adhdev/session-host-core/dist/index.js.map +1 -1
- package/node_modules/@adhdev/session-host-core/dist/index.mjs +77 -6
- package/node_modules/@adhdev/session-host-core/dist/index.mjs.map +1 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +3 -3
- package/src/cli-adapters/provider-cli-adapter.ts +36 -62
- package/src/cli-adapters/pty-transport.ts +13 -1
- package/src/cli-adapters/spawn-env.ts +12 -0
- package/src/cli-adapters/terminal-screen.ts +12 -3
- package/src/commands/cli-manager.ts +13 -2
- package/src/commands/workspace-commands.ts +37 -9
- package/src/detection/cli-detector.ts +36 -2
- package/src/providers/cli-provider-instance.ts +62 -36
- package/src/providers/contracts.ts +39 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/buffer.ts","../src/registry.ts","../src/runtime-labels.ts","../src/ipc.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 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\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 });\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"],"mappings":"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","names":["path","randomUUID"]}
|
|
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 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 ) {\n delete env[key];\n }\n }\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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.14",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -38,13 +38,13 @@
|
|
|
38
38
|
"author": "vilmire",
|
|
39
39
|
"license": "AGPL-3.0-or-later",
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@agentclientprotocol/sdk": "^0.16.1",
|
|
42
41
|
"@adhdev/session-host-core": "*",
|
|
42
|
+
"@agentclientprotocol/sdk": "^0.16.1",
|
|
43
43
|
"@xterm/xterm": "^6.0.0",
|
|
44
44
|
"chalk": "^5.3.0",
|
|
45
45
|
"chokidar": "^5.0.0",
|
|
46
46
|
"conf": "^13.0.0",
|
|
47
|
-
"node-pty": "^1.
|
|
47
|
+
"node-pty": "^1.2.0-beta.12",
|
|
48
48
|
"ws": "^8.19.0"
|
|
49
49
|
},
|
|
50
50
|
"bundleDependencies": [
|
|
@@ -27,26 +27,12 @@ import {
|
|
|
27
27
|
type PtyRuntimeTransport,
|
|
28
28
|
type PtyTransportFactory,
|
|
29
29
|
} from './pty-transport.js';
|
|
30
|
+
import { sanitizeSpawnEnv, ensureNodePtySpawnHelperPermissions } from './spawn-env.js';
|
|
30
31
|
|
|
31
32
|
let pty: any;
|
|
32
33
|
try {
|
|
33
34
|
pty = require('node-pty');
|
|
34
|
-
|
|
35
|
-
if (os.platform() !== 'win32') {
|
|
36
|
-
try {
|
|
37
|
-
const fs = require('fs');
|
|
38
|
-
const ptyDir = path.resolve(path.dirname(require.resolve('node-pty')), '..');
|
|
39
|
-
const platformArch = `${os.platform()}-${os.arch()}`;
|
|
40
|
-
const helper = path.join(ptyDir, 'prebuilds', platformArch, 'spawn-helper');
|
|
41
|
-
if (fs.existsSync(helper)) {
|
|
42
|
-
const stat = fs.statSync(helper);
|
|
43
|
-
if (!(stat.mode & 0o111)) {
|
|
44
|
-
fs.chmodSync(helper, stat.mode | 0o755);
|
|
45
|
-
LOG.info('CLI', '[node-pty] Fixed spawn-helper permissions');
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
} catch { /* best-effort */ }
|
|
49
|
-
}
|
|
35
|
+
ensureNodePtySpawnHelperPermissions((msg: string) => LOG.info('CLI', msg));
|
|
50
36
|
} catch {
|
|
51
37
|
LOG.error('CLI', '[ProviderCliAdapter] node-pty not found. Terminal features disabled.');
|
|
52
38
|
}
|
|
@@ -195,50 +181,8 @@ function sanitizeTerminalText(str: string): string {
|
|
|
195
181
|
return stripTerminalNoise(stripAnsi(str));
|
|
196
182
|
}
|
|
197
183
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (!env.TERM || env.TERM === 'xterm-color') {
|
|
202
|
-
env.TERM = 'xterm-256color';
|
|
203
|
-
}
|
|
204
|
-
if (!env.COLORTERM) env.COLORTERM = 'truecolor';
|
|
205
|
-
|
|
206
|
-
if (process.platform === 'win32') {
|
|
207
|
-
if (!env.FORCE_COLOR) env.FORCE_COLOR = '1';
|
|
208
|
-
if (!env.CLICOLOR) env.CLICOLOR = '1';
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function buildCliSpawnEnv(baseEnv: NodeJS.ProcessEnv, overrides?: Record<string, string>): Record<string, string> {
|
|
213
|
-
const env: Record<string, string> = {};
|
|
214
|
-
const source = { ...baseEnv, ...(overrides || {}) } as NodeJS.ProcessEnv;
|
|
215
|
-
|
|
216
|
-
for (const [key, value] of Object.entries(source)) {
|
|
217
|
-
if (typeof value !== 'string') continue;
|
|
218
|
-
env[key] = value;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
for (const key of Object.keys(env)) {
|
|
222
|
-
if (
|
|
223
|
-
key === 'INIT_CWD'
|
|
224
|
-
|| key === 'npm_command'
|
|
225
|
-
|| key === 'npm_execpath'
|
|
226
|
-
|| key === 'npm_node_execpath'
|
|
227
|
-
|| key.startsWith('npm_')
|
|
228
|
-
|| key.startsWith('npm_config_')
|
|
229
|
-
|| key.startsWith('npm_package_')
|
|
230
|
-
|| key.startsWith('npm_lifecycle_')
|
|
231
|
-
|| key.startsWith('PNPM_')
|
|
232
|
-
|| key.startsWith('YARN_')
|
|
233
|
-
|| key.startsWith('BUN_')
|
|
234
|
-
) {
|
|
235
|
-
delete env[key];
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
applyPreferredTerminalColorEnv(env);
|
|
240
|
-
return env;
|
|
241
|
-
}
|
|
184
|
+
// Re-export sanitizeSpawnEnv under the local alias for backward compat within this file
|
|
185
|
+
const buildCliSpawnEnv = sanitizeSpawnEnv;
|
|
242
186
|
|
|
243
187
|
function computeTerminalQueryTail(buffer: string): string {
|
|
244
188
|
const prefixes = ['\x1b[6n', '\x1b[?6n'];
|
|
@@ -815,7 +759,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
815
759
|
);
|
|
816
760
|
// On Windows, .cmd/.bat shims cannot be spawned directly — must go through cmd.exe
|
|
817
761
|
const isCmdShim = isWin && /\.(cmd|bat)$/i.test(binaryPath);
|
|
818
|
-
const
|
|
762
|
+
const useShellWin = isCmdShim
|
|
763
|
+
|| !path.isAbsolute(binaryPath)
|
|
764
|
+
|| isScriptBinary(binaryPath);
|
|
765
|
+
const useShell = isWin ? useShellWin : useShellUnix;
|
|
819
766
|
|
|
820
767
|
if (useShell) {
|
|
821
768
|
if (!spawnConfig.shell && !isWin) {
|
|
@@ -823,6 +770,8 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
823
770
|
}
|
|
824
771
|
if (isCmdShim) {
|
|
825
772
|
LOG.info('CLI', `[${this.cliType}] Using cmd.exe shell for .cmd/.bat shim: ${binaryPath}`);
|
|
773
|
+
} else if (isWin) {
|
|
774
|
+
LOG.info('CLI', `[${this.cliType}] Using cmd.exe shell on Windows: ${binaryPath}`);
|
|
826
775
|
}
|
|
827
776
|
shellCmd = isWin ? 'cmd.exe' : (process.env.SHELL || '/bin/zsh');
|
|
828
777
|
if (isWin) {
|
|
@@ -836,6 +785,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
836
785
|
shellArgs = ['-l', '-c', fullCmd];
|
|
837
786
|
}
|
|
838
787
|
} else {
|
|
788
|
+
if (isWin && spawnConfig.shell) {
|
|
789
|
+
LOG.info('CLI', `[${this.cliType}] Spawning Windows binary directly without cmd.exe: ${binaryPath}`);
|
|
790
|
+
}
|
|
839
791
|
shellCmd = binaryPath;
|
|
840
792
|
shellArgs = allArgs;
|
|
841
793
|
}
|
|
@@ -866,6 +818,19 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
866
818
|
shellArgs = ['-l', '-c', fullCmd];
|
|
867
819
|
this.ptyProcess = this.transportFactory.spawn(shellCmd, shellArgs, ptyOpts);
|
|
868
820
|
} else {
|
|
821
|
+
// Windows native error codes are often cryptic — provide helpful hints
|
|
822
|
+
if (isWin) {
|
|
823
|
+
const hint = /error code 267|ERROR_DIRECTORY/i.test(msg)
|
|
824
|
+
? ' (working directory does not exist or is not a directory)'
|
|
825
|
+
: /error code 740|elevation/i.test(msg)
|
|
826
|
+
? ' (requires administrator privileges)'
|
|
827
|
+
: /error code 2|ENOENT|not found/i.test(msg)
|
|
828
|
+
? ` (executable not found: ${shellCmd})`
|
|
829
|
+
: '';
|
|
830
|
+
if (hint) {
|
|
831
|
+
throw new Error(`Failed to spawn CLI${hint}: ${msg}`);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
869
834
|
throw err;
|
|
870
835
|
}
|
|
871
836
|
}
|
|
@@ -1698,7 +1663,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1698
1663
|
: `${stopCommand}${this.sendKey}`;
|
|
1699
1664
|
this.ptyProcess.write(payload);
|
|
1700
1665
|
};
|
|
1701
|
-
|
|
1666
|
+
const interruptGraceMs = typeof resume.interruptGraceMs === 'number'
|
|
1667
|
+
? Math.max(100, resume.interruptGraceMs)
|
|
1668
|
+
: 500;
|
|
1669
|
+
if (wasProcessing) setTimeout(writeCommand, interruptGraceMs);
|
|
1702
1670
|
else writeCommand();
|
|
1703
1671
|
} else {
|
|
1704
1672
|
this.ptyProcess.write('\x03');
|
|
@@ -1737,6 +1705,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1737
1705
|
if (this.settleTimer) { clearTimeout(this.settleTimer); this.settleTimer = null; }
|
|
1738
1706
|
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
1739
1707
|
if (this.submitRetryTimer) { clearTimeout(this.submitRetryTimer); this.submitRetryTimer = null; }
|
|
1708
|
+
if (this.responseTimeout) { clearTimeout(this.responseTimeout); this.responseTimeout = null; }
|
|
1709
|
+
if (this.idleTimeout) { clearTimeout(this.idleTimeout); this.idleTimeout = null; }
|
|
1710
|
+
if (this.pendingScriptStatusTimer) { clearTimeout(this.pendingScriptStatusTimer); this.pendingScriptStatusTimer = null; }
|
|
1740
1711
|
if (this.pendingOutputParseTimer) { clearTimeout(this.pendingOutputParseTimer); this.pendingOutputParseTimer = null; }
|
|
1741
1712
|
this.pendingOutputParseBuffer = '';
|
|
1742
1713
|
this.pendingTerminalQueryTail = '';
|
|
@@ -1761,6 +1732,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1761
1732
|
if (this.settleTimer) { clearTimeout(this.settleTimer); this.settleTimer = null; }
|
|
1762
1733
|
if (this.approvalExitTimeout) { clearTimeout(this.approvalExitTimeout); this.approvalExitTimeout = null; }
|
|
1763
1734
|
if (this.submitRetryTimer) { clearTimeout(this.submitRetryTimer); this.submitRetryTimer = null; }
|
|
1735
|
+
if (this.responseTimeout) { clearTimeout(this.responseTimeout); this.responseTimeout = null; }
|
|
1736
|
+
if (this.idleTimeout) { clearTimeout(this.idleTimeout); this.idleTimeout = null; }
|
|
1737
|
+
if (this.pendingScriptStatusTimer) { clearTimeout(this.pendingScriptStatusTimer); this.pendingScriptStatusTimer = null; }
|
|
1764
1738
|
if (this.pendingOutputParseTimer) { clearTimeout(this.pendingOutputParseTimer); this.pendingOutputParseTimer = null; }
|
|
1765
1739
|
this.pendingOutputParseBuffer = '';
|
|
1766
1740
|
this.pendingTerminalQueryTail = '';
|
|
@@ -91,11 +91,23 @@ class NodePtyRuntimeTransport implements PtyRuntimeTransport {
|
|
|
91
91
|
export class NodePtyTransportFactory implements PtyTransportFactory {
|
|
92
92
|
spawn(command: string, args: string[], options: PtySpawnOptions): PtyRuntimeTransport {
|
|
93
93
|
if (!pty) throw new Error('node-pty is not installed');
|
|
94
|
+
// Validate cwd — an invalid directory causes a native crash on Windows
|
|
95
|
+
// (node-pty error code 267: ERROR_DIRECTORY) that bypasses JS try/catch
|
|
96
|
+
let cwd = options.cwd;
|
|
97
|
+
if (cwd) {
|
|
98
|
+
try {
|
|
99
|
+
const fs = require('fs');
|
|
100
|
+
const stat = fs.statSync(cwd);
|
|
101
|
+
if (!stat.isDirectory()) cwd = os.homedir();
|
|
102
|
+
} catch {
|
|
103
|
+
cwd = os.homedir();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
94
106
|
const handle = pty.spawn(command, args, {
|
|
95
107
|
name: 'xterm-256color',
|
|
96
108
|
cols: options.cols,
|
|
97
109
|
rows: options.rows,
|
|
98
|
-
cwd
|
|
110
|
+
cwd,
|
|
99
111
|
env: options.env,
|
|
100
112
|
});
|
|
101
113
|
return new NodePtyRuntimeTransport(handle);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-export spawn environment utilities from @adhdev/session-host-core.
|
|
3
|
+
*
|
|
4
|
+
* The canonical implementation lives in session-host-core so that both
|
|
5
|
+
* daemon-core and session-host-daemon can share it. This file exists
|
|
6
|
+
* to keep the import path short within daemon-core consumers.
|
|
7
|
+
*/
|
|
8
|
+
export {
|
|
9
|
+
sanitizeSpawnEnv,
|
|
10
|
+
applyTerminalColorEnv,
|
|
11
|
+
ensureNodePtySpawnHelperPermissions,
|
|
12
|
+
} from '@adhdev/session-host-core';
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PTY screen snapshot abstraction.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Terminal viewport backend selection.
|
|
5
|
+
*
|
|
6
|
+
* Runtime preference is backend-agnostic:
|
|
7
|
+
* - prefer ghostty-vt when available (or when explicitly requested)
|
|
8
|
+
* - fall back to xterm when ghostty-vt is unavailable
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
11
|
import { LOG } from '../logging/logger.js';
|
|
@@ -64,6 +66,13 @@ function logTerminalBackendSelection(
|
|
|
64
66
|
const key = `${preference}:${ghosttyAvailable}:${backendKind}`;
|
|
65
67
|
if (loggedTerminalBackends.has(key)) return;
|
|
66
68
|
loggedTerminalBackends.add(key);
|
|
69
|
+
if (backendKind === 'xterm' && preference !== 'xterm' && !ghosttyAvailable) {
|
|
70
|
+
LOG.warn(
|
|
71
|
+
'Terminal',
|
|
72
|
+
`[terminal-screen] ghostty-vt unavailable; using xterm fallback (preference=${preference})`,
|
|
73
|
+
);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
67
76
|
LOG.info(
|
|
68
77
|
'Terminal',
|
|
69
78
|
`[terminal-screen] backend=${backendKind} preference=${preference} ghosttyAvailable=${ghosttyAvailable}`,
|
|
@@ -518,7 +518,17 @@ export class DaemonCliManager {
|
|
|
518
518
|
|
|
519
519
|
// ─── CLI category handling (existing) ───
|
|
520
520
|
const cliInfo = await detectCLI(cliType, this.providerLoader);
|
|
521
|
-
if (!cliInfo)
|
|
521
|
+
if (!cliInfo) {
|
|
522
|
+
const installHint = provider?.install || '';
|
|
523
|
+
const displayName = provider?.displayName || provider?.name || cliType;
|
|
524
|
+
const spawnCmd = provider?.spawn?.command || cliType;
|
|
525
|
+
throw new Error(
|
|
526
|
+
`${displayName} is not installed.\n` +
|
|
527
|
+
`Command '${spawnCmd}' not found on PATH.\n` +
|
|
528
|
+
(installHint ? `\n${installHint}\n` : '') +
|
|
529
|
+
`\nRun 'adhdev doctor' for detailed diagnostics.`
|
|
530
|
+
);
|
|
531
|
+
}
|
|
522
532
|
|
|
523
533
|
console.log(colorize('yellow', ` ⚡ Starting CLI ${cliType} in ${resolvedDir}...`));
|
|
524
534
|
if (provider) {
|
|
@@ -868,8 +878,9 @@ export class DaemonCliManager {
|
|
|
868
878
|
const dir = rdir.path;
|
|
869
879
|
if (!cliType) throw new Error('cliType required');
|
|
870
880
|
const found = this.findAdapter(cliType, { instanceKey: args?.targetSessionId, dir });
|
|
881
|
+
const prevCliArgs = found ? (found.adapter as any).extraArgs : undefined;
|
|
871
882
|
if (found) await this.stopSession(found.key);
|
|
872
|
-
await this.startSession(cliType, dir);
|
|
883
|
+
await this.startSession(cliType, dir, args?.cliArgs || prevCliArgs, args?.initialModel);
|
|
873
884
|
return { success: true, restarted: true };
|
|
874
885
|
}
|
|
875
886
|
case 'agent_command': {
|
|
@@ -3,12 +3,31 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { loadConfig, saveConfig } from '../config/config.js';
|
|
6
|
+
import type { ADHDevConfig } from '../config/config.js';
|
|
6
7
|
import * as W from '../config/workspaces.js';
|
|
7
8
|
|
|
8
9
|
export type WorkspaceCommandResult = { success: boolean;[key: string]: unknown };
|
|
9
10
|
|
|
11
|
+
function loadWorkspaceConfig(): ADHDevConfig | { error: string } {
|
|
12
|
+
try {
|
|
13
|
+
return loadConfig();
|
|
14
|
+
} catch (e: any) {
|
|
15
|
+
return { error: `Could not load config: ${e?.message || 'unknown error'}` };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function persistWorkspaceConfig(config: ADHDevConfig): { ok: true } | { error: string } {
|
|
20
|
+
try {
|
|
21
|
+
saveConfig(config);
|
|
22
|
+
return { ok: true };
|
|
23
|
+
} catch (e: any) {
|
|
24
|
+
return { error: `Could not save config: ${e?.message || 'unknown error'}` };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
10
28
|
export function handleWorkspaceList(): WorkspaceCommandResult {
|
|
11
|
-
const config =
|
|
29
|
+
const config = loadWorkspaceConfig();
|
|
30
|
+
if ('error' in config) return { success: false, error: config.error };
|
|
12
31
|
const state = W.getWorkspaceState(config);
|
|
13
32
|
return {
|
|
14
33
|
success: true,
|
|
@@ -24,11 +43,13 @@ export function handleWorkspaceAdd(args: any): WorkspaceCommandResult {
|
|
|
24
43
|
const createIfMissing = args?.createIfMissing === true;
|
|
25
44
|
if (!rawPath) return { success: false, error: 'path required' };
|
|
26
45
|
|
|
27
|
-
const config =
|
|
46
|
+
const config = loadWorkspaceConfig();
|
|
47
|
+
if ('error' in config) return { success: false, error: config.error };
|
|
28
48
|
const result = W.addWorkspaceEntry(config, rawPath, label, { createIfMissing });
|
|
29
49
|
if ('error' in result) return { success: false, error: result.error };
|
|
30
50
|
|
|
31
|
-
|
|
51
|
+
const saveResult = persistWorkspaceConfig(result.config);
|
|
52
|
+
if ('error' in saveResult) return { success: false, error: saveResult.error };
|
|
32
53
|
const state = W.getWorkspaceState(result.config);
|
|
33
54
|
return { success: true, entry: result.entry, ...state };
|
|
34
55
|
}
|
|
@@ -37,12 +58,14 @@ export function handleWorkspaceRemove(args: any): WorkspaceCommandResult {
|
|
|
37
58
|
const id = (args?.id || '').trim();
|
|
38
59
|
if (!id) return { success: false, error: 'id required' };
|
|
39
60
|
|
|
40
|
-
const config =
|
|
61
|
+
const config = loadWorkspaceConfig();
|
|
62
|
+
if ('error' in config) return { success: false, error: config.error };
|
|
41
63
|
const removed = (config.workspaces || []).find(w => w.id === id);
|
|
42
64
|
const result = W.removeWorkspaceEntry(config, id);
|
|
43
65
|
if ('error' in result) return { success: false, error: result.error };
|
|
44
66
|
|
|
45
|
-
|
|
67
|
+
const saveResult = persistWorkspaceConfig(result.config);
|
|
68
|
+
if ('error' in saveResult) return { success: false, error: saveResult.error };
|
|
46
69
|
const state = W.getWorkspaceState(result.config);
|
|
47
70
|
return { success: true, removedId: id, ...state };
|
|
48
71
|
}
|
|
@@ -50,10 +73,12 @@ export function handleWorkspaceRemove(args: any): WorkspaceCommandResult {
|
|
|
50
73
|
export function handleWorkspaceSetDefault(args: any): WorkspaceCommandResult {
|
|
51
74
|
const clear = args?.clear === true || args?.id === null || args?.id === '';
|
|
52
75
|
if (clear) {
|
|
53
|
-
const config =
|
|
76
|
+
const config = loadWorkspaceConfig();
|
|
77
|
+
if ('error' in config) return { success: false, error: config.error };
|
|
54
78
|
const result = W.setDefaultWorkspaceId(config, null);
|
|
55
79
|
if ('error' in result) return { success: false, error: result.error };
|
|
56
|
-
|
|
80
|
+
const saveResult = persistWorkspaceConfig(result.config);
|
|
81
|
+
if ('error' in saveResult) return { success: false, error: saveResult.error };
|
|
57
82
|
const state = W.getWorkspaceState(result.config);
|
|
58
83
|
return {
|
|
59
84
|
success: true,
|
|
@@ -70,7 +95,9 @@ export function handleWorkspaceSetDefault(args: any): WorkspaceCommandResult {
|
|
|
70
95
|
return { success: false, error: 'id or path required (or clear: true)' };
|
|
71
96
|
}
|
|
72
97
|
|
|
73
|
-
|
|
98
|
+
const configResult = loadWorkspaceConfig();
|
|
99
|
+
if ('error' in configResult) return { success: false, error: configResult.error };
|
|
100
|
+
let config = configResult;
|
|
74
101
|
let nextId: string;
|
|
75
102
|
|
|
76
103
|
if (pathArg) {
|
|
@@ -89,7 +116,8 @@ export function handleWorkspaceSetDefault(args: any): WorkspaceCommandResult {
|
|
|
89
116
|
const result = W.setDefaultWorkspaceId(config, nextId);
|
|
90
117
|
if ('error' in result) return { success: false, error: result.error };
|
|
91
118
|
|
|
92
|
-
|
|
119
|
+
const saveResult = persistWorkspaceConfig(result.config);
|
|
120
|
+
if ('error' in saveResult) return { success: false, error: saveResult.error };
|
|
93
121
|
const state = W.getWorkspaceState(result.config);
|
|
94
122
|
return { success: true, ...state };
|
|
95
123
|
}
|
|
@@ -93,10 +93,44 @@ export async function detectCLIs(providerLoader?: ProviderLoader): Promise<CLIIn
|
|
|
93
93
|
return results;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
/** Detect specific CLI */
|
|
96
|
+
/** Detect specific CLI — only probes the one requested provider */
|
|
97
97
|
export async function detectCLI(cliId: string, providerLoader?: ProviderLoader): Promise<CLIInfo | null> {
|
|
98
|
-
// Resolve alias
|
|
99
98
|
const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
|
|
99
|
+
|
|
100
|
+
if (providerLoader) {
|
|
101
|
+
const cliList = providerLoader.getCliDetectionList();
|
|
102
|
+
const target = cliList.find((c) => c.id === resolvedId);
|
|
103
|
+
if (target) {
|
|
104
|
+
const platform = os.platform();
|
|
105
|
+
const whichCmd = platform === 'win32' ? 'where' : 'which';
|
|
106
|
+
try {
|
|
107
|
+
const pathResult = await execAsync(`${whichCmd} ${target.command}`);
|
|
108
|
+
if (!pathResult) return null;
|
|
109
|
+
const firstPath = pathResult.split('\n')[0];
|
|
110
|
+
let version: string | undefined;
|
|
111
|
+
try {
|
|
112
|
+
const versionCommands = [
|
|
113
|
+
target.versionCommand,
|
|
114
|
+
`${target.command} --version`,
|
|
115
|
+
`${target.command} -V`,
|
|
116
|
+
`${target.command} -v`,
|
|
117
|
+
].filter((v): v is string => !!v);
|
|
118
|
+
for (const versionCommand of versionCommands) {
|
|
119
|
+
const versionResult = await execAsync(versionCommand, 3000);
|
|
120
|
+
if (versionResult) {
|
|
121
|
+
version = parseVersion(versionResult);
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch { }
|
|
126
|
+
return { ...target, installed: true, version, path: firstPath };
|
|
127
|
+
} catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Fallback: full scan for unknown provider IDs
|
|
100
134
|
const all = await detectCLIs(providerLoader);
|
|
101
135
|
return all.find((c) => c.id === resolvedId && c.installed) || null;
|
|
102
136
|
}
|