Package the portable DSH profiles, local plugins, desktop integration, Firefox wrapper, and ownership-aware installer lifecycle. Bundle checksum-pinned OpenVSCode, editor extensions, translation assets, and cloudflared so unreliable upstream artifact downloads cannot break a clean installation.
92 lines
3.8 KiB
JavaScript
92 lines
3.8 KiB
JavaScript
import net from 'node:net'
|
|
import { randomUUID } from 'node:crypto'
|
|
import { readdir, realpath } from 'node:fs/promises'
|
|
import { isAbsolute, join } from 'node:path'
|
|
import bridgeProtocol from '../editor-file-bridge/extension.js'
|
|
|
|
const { workspaceSocketPrefix, validateBridgeDirectory, MAX_MESSAGE_BYTES, REQUEST_TIMEOUT_MILLISECONDS } = bridgeProtocol
|
|
|
|
function requestFileOpen(socketPath, request, signal) {
|
|
return new Promise((resolve, reject) => {
|
|
if (signal.aborted) {
|
|
reject(signal.reason)
|
|
return
|
|
}
|
|
const socket = net.createConnection(socketPath)
|
|
let connected = false
|
|
let settled = false
|
|
let buffered = Buffer.alloc(0)
|
|
const finish = (error, opened) => {
|
|
if (settled) return
|
|
settled = true
|
|
signal.removeEventListener('abort', abort)
|
|
socket.destroy()
|
|
if (error) reject(error)
|
|
else resolve(opened)
|
|
}
|
|
const abort = () => finish(signal.reason || new Error('The editor request was cancelled.'))
|
|
signal.addEventListener('abort', abort, { once: true })
|
|
socket.once('connect', () => {
|
|
connected = true
|
|
socket.write(`${JSON.stringify(request)}\n`)
|
|
})
|
|
socket.on('data', chunk => {
|
|
buffered = Buffer.concat([buffered, chunk])
|
|
if (buffered.length > MAX_MESSAGE_BYTES) {
|
|
finish(new Error('The editor acknowledgement is too large.'))
|
|
return
|
|
}
|
|
const newline = buffered.indexOf(10)
|
|
if (newline === -1) return
|
|
let response
|
|
try {
|
|
response = JSON.parse(buffered.subarray(0, newline).toString('utf8'))
|
|
} catch {
|
|
finish(new Error('The editor returned an invalid acknowledgement.'))
|
|
return
|
|
}
|
|
if (response?.requestId !== request.requestId || typeof response.ok !== 'boolean') {
|
|
finish(new Error('The editor returned an unexpected acknowledgement.'))
|
|
} else if (!response.ok) {
|
|
finish(new Error(typeof response.error === 'string' ? response.error : 'The existing editor could not open the file.'))
|
|
} else {
|
|
finish(null, true)
|
|
}
|
|
})
|
|
socket.once('error', error => {
|
|
if (!connected && ['ECONNREFUSED', 'ENOENT'].includes(error.code)) finish(null, false)
|
|
else finish(error)
|
|
})
|
|
socket.once('close', () => {
|
|
if (!settled) finish(new Error('The existing editor disconnected before acknowledging the file.'))
|
|
})
|
|
})
|
|
}
|
|
|
|
export async function openFileInExistingWorkspace({ bridgeDirectory, workspaceDirectory, filePath, signal }) {
|
|
signal?.throwIfAborted()
|
|
if (!bridgeDirectory) return { opened: false }
|
|
if (!isAbsolute(workspaceDirectory) || !isAbsolute(filePath)) throw new Error('Editor workspace and file paths must be absolute.')
|
|
const canonicalWorkspace = await realpath(workspaceDirectory)
|
|
let directory
|
|
let entries
|
|
try {
|
|
directory = await validateBridgeDirectory(bridgeDirectory)
|
|
entries = await readdir(directory, { withFileTypes: true })
|
|
} catch (error) {
|
|
if (error.code === 'ENOENT') return { opened: false }
|
|
throw error
|
|
}
|
|
const prefix = workspaceSocketPrefix(canonicalWorkspace)
|
|
const sockets = entries.filter(entry => entry.isSocket() && entry.name.startsWith(prefix) && entry.name.endsWith('.sock')).map(entry => entry.name).sort()
|
|
const request = { kind: 'open-file', workspaceDirectory: canonicalWorkspace, filePath, requestId: randomUUID() }
|
|
if (Buffer.byteLength(JSON.stringify(request)) + 1 > MAX_MESSAGE_BYTES) throw new Error('The editor request is too large.')
|
|
const deadline = AbortSignal.timeout(REQUEST_TIMEOUT_MILLISECONDS)
|
|
const requestSignal = signal ? AbortSignal.any([signal, deadline]) : deadline
|
|
for (const name of sockets) {
|
|
requestSignal.throwIfAborted()
|
|
if (await requestFileOpen(join(directory, name), request, requestSignal)) return { opened: true }
|
|
}
|
|
requestSignal.throwIfAborted()
|
|
return { opened: false }
|
|
}
|