@ai-ecoverse/slicc-kernel 1.26.6 → 1.28.0
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/README.md +62 -4
- package/dist/driver-worker.js +1 -1
- package/dist/driver-worker.js.map +1 -1
- package/dist/index.js +221 -12
- package/dist/index.js.map +4 -4
- package/dist/kernel-worker.js +634 -51
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +1 -1
- package/dist/node-driver-worker.js.map +1 -1
- package/dist/node-process-worker.js +89 -20
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +547 -49
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +89 -20
- package/dist/process-worker.js.map +4 -4
- package/dist/testing.js +223 -0
- package/dist/testing.js.map +7 -0
- package/package.json +4 -3
|
@@ -398,7 +398,7 @@ async function loopbackFetch(dial, input, options) {
|
|
|
398
398
|
}
|
|
399
399
|
|
|
400
400
|
// src/client/protocol.ts
|
|
401
|
-
var PROTOCOL = [1,
|
|
401
|
+
var PROTOCOL = [1, 7];
|
|
402
402
|
function locksOf(scope = globalThis) {
|
|
403
403
|
const locks = scope.navigator?.locks;
|
|
404
404
|
return typeof locks?.request === "function" ? locks : void 0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/node-driver-worker.ts", "../src/cdp/port.ts", "../src/kernel/net/remote-transport.ts", "../src/client/dial-stream.ts", "../src/client/loopback-fetch.ts", "../src/client/protocol.ts", "../src/client/attach.ts", "../src/mount/protocol.ts", "../src/mount/driver.ts", "../src/mount/host.ts"],
|
|
4
|
-
"sourcesContent": ["import { parentPort } from 'node:worker_threads';\nimport { type DriverStart, hostDriver } from './mount/host.ts';\n\nparentPort?.once('message', (start: DriverStart) =>\n hostDriver(\n start,\n (code) => import(`data:text/javascript;base64,${Buffer.from(code).toString('base64')}`)\n )\n);\n", "import type { CdpConnection, CdpHook, CdpRequest } from './types.ts';\n\nexport interface CdpPortLike {\n postMessage(message: unknown): void;\n addEventListener(type: 'message', handler: (event: MessageEvent) => void): void;\n start?(): void;\n close(): void;\n}\n\nexport type CdpWire = { open: true } | { error: string } | { m: string } | { close: string | null };\n\nexport interface CdpOpen {\n cdpOpen: CdpRequest & { registration?: number };\n port: CdpPortLike;\n}\n\nexport function serveCdpPort(\n port: CdpPortLike,\n hook: CdpHook,\n request: CdpRequest,\n onDone?: () => void\n): () => void {\n let conn: CdpConnection | undefined;\n let done = false;\n const end = () => {\n done = true;\n port.close();\n onDone?.();\n };\n const finish = (reason: string | null) => {\n if (done) return;\n port.postMessage({ close: reason });\n end();\n };\n port.addEventListener('message', ({ data }: MessageEvent<{ m?: string; close?: true }>) => {\n if (data.m !== undefined) conn?.send(data.m);\n else if (data.close && !done) {\n end();\n conn?.close();\n }\n });\n port.start?.();\n new Promise<CdpConnection>((resolve) => resolve(hook(request))).then(\n (opened) => {\n conn = opened;\n if (done) return opened.close();\n opened.onmessage = (m) => {\n if (!done) port.postMessage({ m });\n };\n opened.onclose = (reason) => finish(reason ?? null);\n port.postMessage({ open: true });\n },\n (e: unknown) => {\n if (done) return;\n port.postMessage({ error: e instanceof Error ? e.message : String(e) });\n end();\n }\n );\n return () => {\n if (done) return;\n conn?.close();\n finish('the CDP host is gone');\n };\n}\n\nexport function portCdpHook(\n ask: (request: CdpRequest, port: CdpPortLike) => void,\n gone?: Promise<unknown>\n): CdpHook {\n return (request) =>\n new Promise((resolve, reject) => {\n const { port1, port2 } = new MessageChannel();\n let closed = false;\n const conn: CdpConnection = {\n send: (m) => {\n if (!closed) port1.postMessage({ m });\n },\n onmessage: null,\n onclose: null,\n close: () => {\n if (closed) return;\n closed = true;\n port1.postMessage({ close: true });\n port1.close();\n },\n };\n port1.addEventListener('message', ({ data }: MessageEvent<CdpWire>) => {\n if ('m' in data) conn.onmessage?.(data.m);\n else if ('open' in data) resolve(conn);\n else if ('error' in data) {\n closed = true;\n port1.close();\n reject(new Error(data.error));\n } else if (!closed) {\n closed = true;\n port1.close();\n conn.onclose?.(data.close ?? undefined);\n }\n });\n port1.start();\n void gone?.then(() => {\n if (closed) return;\n closed = true;\n port1.close();\n reject(new Error('the CDP host is gone'));\n conn.onclose?.('the CDP host is gone');\n });\n ask(request, port2 as unknown as CdpPortLike);\n });\n}\n", "import type {\n HeaderList,\n RealmTransport,\n RealmTransportRequest,\n RealmTransportResponse,\n RealmTransportTraits,\n} from './transport.ts';\n\nexport type TransportCall =\n | {\n net: 'fetch';\n nid: number;\n url: string;\n method: string;\n headers: HeaderList;\n body?: Uint8Array;\n }\n | { net: 'read'; nid: number }\n | { net: 'cancel'; nid: number };\n\nexport type TransportReply =\n | { net: 'head'; nid: number; status: number; statusText: string; headers: HeaderList }\n | { net: 'chunk'; nid: number; bytes: Uint8Array }\n | { net: 'end'; nid: number }\n | { net: 'error'; nid: number; message: string; status?: number; code?: string };\n\nexport interface TransportPort {\n postMessage(message: TransportCall): void;\n}\n\nexport class TransportError extends Error {\n readonly status: number | undefined;\n readonly code: string | undefined;\n\n constructor(message: string, status?: number, code?: string) {\n super(message);\n this.status = status;\n this.code = code;\n }\n}\n\ninterface Waiter {\n resolve(reply: TransportReply): void;\n reject(error: Error): void;\n}\n\nexport class RemoteTransport implements RealmTransport {\n readonly traits: RealmTransportTraits;\n\n private readonly port: TransportPort;\n\n private readonly waiting = new Map<number, Waiter>();\n\n private nextId = 0;\n\n private failure: Error | undefined;\n\n constructor(port: TransportPort, traits: RealmTransportTraits) {\n this.port = port;\n this.traits = traits;\n }\n\n receive(reply: TransportReply): void {\n const waiter = this.waiting.get(reply.nid);\n if (!waiter) return;\n this.waiting.delete(reply.nid);\n if (reply.net === 'error') {\n waiter.reject(new TransportError(reply.message, reply.status, reply.code));\n } else waiter.resolve(reply);\n }\n\n fail(error: Error): void {\n this.failure = error;\n for (const waiter of this.waiting.values()) waiter.reject(error);\n this.waiting.clear();\n }\n\n private ask(call: TransportCall): Promise<TransportReply> {\n if (this.failure) return Promise.reject(this.failure);\n return new Promise((resolve, reject) => {\n this.waiting.set(call.nid, { resolve, reject });\n this.port.postMessage(call);\n });\n }\n\n private cancel(nid: number): void {\n const waiter = this.waiting.get(nid);\n this.waiting.delete(nid);\n waiter?.reject(new TransportError('the request was cancelled'));\n this.port.postMessage({ net: 'cancel', nid });\n }\n\n async fetch(request: RealmTransportRequest): Promise<RealmTransportResponse> {\n const nid = ++this.nextId;\n const { signal } = request;\n signal.throwIfAborted();\n let finished = false;\n const abort = () => {\n finished = true;\n this.cancel(nid);\n };\n signal.addEventListener('abort', abort, { once: true });\n const finish = () => {\n if (finished) return;\n finished = true;\n signal.removeEventListener('abort', abort);\n };\n let head: TransportReply;\n try {\n head = await this.ask({\n net: 'fetch',\n nid,\n url: request.url,\n method: request.method,\n headers: request.headers,\n ...(request.body ? { body: request.body } : {}),\n });\n } catch (e) {\n finish();\n throw e;\n }\n const { status, statusText, headers } = head as Extract<TransportReply, { net: 'head' }>;\n const read = async (): Promise<Uint8Array | undefined> => {\n if (finished) return undefined;\n const reply = await this.ask({ net: 'read', nid }).catch((e: unknown) => {\n finish();\n throw e;\n });\n if (reply.net === 'chunk') return reply.bytes;\n finish();\n return undefined;\n };\n const cancel = async () => {\n if (finished) return;\n finish();\n this.cancel(nid);\n };\n return {\n status,\n statusText,\n headers,\n cancel,\n body: {\n [Symbol.asyncIterator]: () => ({\n next: async () => {\n const bytes = await read();\n return bytes ? { value: bytes, done: false } : { value: undefined, done: true };\n },\n return: async () => {\n await cancel();\n return { value: undefined, done: true };\n },\n }),\n },\n };\n }\n}\n", "export interface DialledSocket {\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n close(): void;\n}\n\nexport interface DialOptions {\n port: number;\n host?: string;\n}\n\ninterface StreamPort {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(type: 'message', handler: (event: MessageEvent) => void): void;\n start?(): void;\n close(): void;\n}\n\ntype DialMessage = Uint8Array | { end: true } | { ack: true } | { error: string };\n\nexport interface DialHandle extends DialledSocket {\n fail(error: Error): void;\n}\n\nexport function errorWithCode(message: string): Error & { code?: string } {\n const code = /^E[A-Z]+/.exec(message)?.[0];\n return Object.assign(new Error(message), code ? { code } : {});\n}\n\nexport function dialStream(port: StreamPort, onClose?: () => void): DialHandle {\n let closed = false;\n let reader: ReadableStreamDefaultController<Uint8Array> | undefined;\n const acks: Array<{ resolve: () => void; reject: (error: Error) => void }> = [];\n const readable = new ReadableStream<Uint8Array>({\n start(controller) {\n reader = controller;\n },\n pull() {\n if (!closed) port.postMessage({ more: true });\n },\n cancel() {\n reader = undefined;\n close();\n },\n });\n const finish = (fail?: Error) => {\n const controller = reader;\n reader = undefined;\n for (const ack of acks.splice(0)) {\n if (fail) ack.reject(fail);\n else ack.resolve();\n }\n if (!controller) return;\n if (fail) controller.error(fail);\n else controller.close();\n };\n function shut(): void {\n if (closed) return;\n closed = true;\n port.close();\n onClose?.();\n }\n function close(): void {\n if (!closed) port.postMessage({ close: true });\n shut();\n finish();\n }\n port.addEventListener('message', ({ data }: MessageEvent<DialMessage>) => {\n if (data instanceof Uint8Array) reader?.enqueue(data);\n else if ('ack' in data) acks.shift()?.resolve();\n else if ('end' in data) finish();\n else finish(errorWithCode(data.error));\n });\n port.start?.();\n const writable = new WritableStream<Uint8Array>({\n write(chunk) {\n if (closed) throw errorWithCode('EPIPE: the connection is closed');\n const copy = chunk.slice();\n const acked = new Promise<void>((resolve, reject) => acks.push({ resolve, reject }));\n port.postMessage(copy, [copy.buffer]);\n return acked;\n },\n close() {\n if (!closed) port.postMessage({ end: true });\n },\n abort() {\n close();\n },\n });\n const fail = (error: Error) => {\n shut();\n finish(error);\n };\n return { readable, writable, close, fail };\n}\n", "import { type DialledSocket, type DialOptions, errorWithCode } from './dial-stream.ts';\n\nexport interface LoopbackFetchOptions extends DialOptions {\n signal?: AbortSignal;\n}\n\nconst OWN_HEADERS = new Set(['host', 'connection', 'content-length', 'transfer-encoding']);\nconst encoder = new TextEncoder();\nconst latin1 = new TextDecoder('latin1');\n\nclass ByteSource {\n private buffered = new Uint8Array(0);\n private readonly reader: ReadableStreamDefaultReader<Uint8Array>;\n\n constructor(reader: ReadableStreamDefaultReader<Uint8Array>) {\n this.reader = reader;\n }\n\n private async more(): Promise<boolean> {\n const { done, value } = await this.reader.read();\n if (done) return false;\n const joined = new Uint8Array(this.buffered.length + value.length);\n joined.set(this.buffered);\n joined.set(value, this.buffered.length);\n this.buffered = joined;\n return true;\n }\n\n async until(marker: string): Promise<Uint8Array | undefined> {\n const needle = encoder.encode(marker);\n for (;;) {\n const at = indexOf(this.buffered, needle);\n if (at >= 0) return this.take(at + needle.length);\n if (!(await this.more())) return undefined;\n }\n }\n\n async some(max: number): Promise<Uint8Array | undefined> {\n if (this.buffered.length === 0 && !(await this.more())) return undefined;\n return this.take(Math.min(max, this.buffered.length));\n }\n\n async exactly(count: number): Promise<Uint8Array | undefined> {\n while (this.buffered.length < count) if (!(await this.more())) return undefined;\n return this.take(count);\n }\n\n private take(count: number): Uint8Array {\n const out = this.buffered.slice(0, count);\n this.buffered = this.buffered.slice(count);\n return out;\n }\n}\n\nfunction indexOf(haystack: Uint8Array, needle: Uint8Array): number {\n outer: for (let i = 0; i + needle.length <= haystack.length; i++) {\n for (let j = 0; j < needle.length; j++) if (haystack[i + j] !== needle[j]) continue outer;\n return i;\n }\n return -1;\n}\n\ninterface ResponseHead {\n status: number;\n statusText: string;\n headers: Array<[string, string]>;\n}\n\nfunction parseHead(bytes: Uint8Array): ResponseHead {\n const [statusLine = '', ...lines] = latin1.decode(bytes).split('\\r\\n');\n const match = /^HTTP\\/1\\.[01] (\\d{3})(?: (.*))?$/.exec(statusLine);\n if (!match) throw errorWithCode(`EPROTO: not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);\n const headers: Array<[string, string]> = [];\n for (const line of lines) {\n const colon = line.indexOf(':');\n if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);\n }\n return { status: Number(match[1]), statusText: match[2] ?? '', headers };\n}\n\nfunction header(head: ResponseHead, name: string): string | undefined {\n return head.headers.find(([key]) => key.toLowerCase() === name)?.[1];\n}\n\nasync function* chunked(source: ByteSource): AsyncGenerator<Uint8Array> {\n for (;;) {\n const line = await source.until('\\r\\n');\n if (!line) throw errorWithCode('EPROTO: the chunked body ended early');\n const size = Number.parseInt(latin1.decode(line).trim().split(';')[0] ?? '', 16);\n if (!Number.isFinite(size)) throw errorWithCode('EPROTO: a malformed chunk size');\n if (size === 0) return;\n const data = await source.exactly(size);\n if (!data || !(await source.exactly(2))) throw errorWithCode('EPROTO: a chunk ended early');\n yield data;\n }\n}\n\nasync function* counted(source: ByteSource, length: number): AsyncGenerator<Uint8Array> {\n let left = length;\n while (left > 0) {\n const data = await source.some(left);\n if (!data) throw errorWithCode('EPROTO: the body ended early');\n left -= data.length;\n yield data;\n }\n}\n\nasync function* untilClose(source: ByteSource): AsyncGenerator<Uint8Array> {\n for (let data = await source.some(65536); data; data = await source.some(65536)) yield data;\n}\n\nfunction bodyOf(source: ByteSource, head: ResponseHead, method: string) {\n if (method === 'HEAD' || head.status === 204 || head.status === 304) return undefined;\n if (header(head, 'transfer-encoding')?.toLowerCase().includes('chunked')) return chunked(source);\n const length = header(head, 'content-length');\n if (length === undefined) return untilClose(source);\n const size = Number(length);\n if (!/^\\d+$/.test(length.trim()) || !Number.isSafeInteger(size)) {\n throw errorWithCode(`EPROTO: a malformed Content-Length: ${length}`);\n }\n return counted(source, size);\n}\n\nfunction stream(chunks: AsyncGenerator<Uint8Array>, socket: DialledSocket, signal?: AbortSignal) {\n return new ReadableStream<Uint8Array>({\n start(controller) {\n signal?.addEventListener('abort', () => controller.error(signal.reason), { once: true });\n },\n async pull(controller) {\n try {\n const next = await chunks.next();\n if (next.done) {\n controller.close();\n socket.close();\n } else controller.enqueue(next.value);\n } catch (err) {\n controller.error(err);\n socket.close();\n }\n },\n cancel() {\n socket.close();\n },\n });\n}\n\nasync function requestBytes(request: Request): Promise<Uint8Array> {\n const url = new URL(request.url);\n const body = request.body ? new Uint8Array(await request.arrayBuffer()) : undefined;\n const lines = [`${request.method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];\n request.headers.forEach((value, name) => {\n if (!OWN_HEADERS.has(name)) lines.push(`${name}: ${value}`);\n });\n if (body) lines.push(`Content-Length: ${body.length}`);\n lines.push('Connection: close', '', '');\n const head = encoder.encode(lines.join('\\r\\n'));\n const out = new Uint8Array(head.length + (body?.length ?? 0));\n out.set(head);\n if (body) out.set(body, head.length);\n return out;\n}\n\nexport async function loopbackFetch(\n dial: (options: DialOptions) => Promise<DialledSocket>,\n input: RequestInfo | URL,\n options: LoopbackFetchOptions\n): Promise<Response> {\n options.signal?.throwIfAborted();\n const request = input instanceof Request ? input : new Request(input);\n const bytes = await requestBytes(request);\n const socket = await dial({\n port: options.port,\n ...(options.host ? { host: options.host } : {}),\n });\n options.signal?.addEventListener('abort', () => socket.close(), { once: true });\n try {\n const writer = socket.writable.getWriter();\n await writer.write(bytes);\n writer.releaseLock();\n const source = new ByteSource(socket.readable.getReader());\n let head: ResponseHead;\n do {\n const raw = await source.until('\\r\\n\\r\\n');\n if (!raw) throw errorWithCode('ECONNRESET: the connection closed before a response');\n head = parseHead(raw);\n } while (head.status >= 100 && head.status < 200);\n const chunks = bodyOf(source, head, request.method);\n if (!chunks) socket.close();\n return new Response(chunks ? stream(chunks, socket, options.signal) : null, {\n status: head.status,\n statusText: head.statusText,\n headers: head.headers,\n });\n } catch (err) {\n socket.close();\n throw err;\n }\n}\n", "import type { WatchChange } from '../fs/watch.ts';\nimport type { TransportCall, TransportReply } from '../kernel/net/remote-transport.ts';\nimport type { RealmTransportTraits } from '../kernel/net/transport.ts';\nimport type { MountSpec } from '../mount/mount-fs.ts';\n\nexport const PROTOCOL: readonly [number, number] = [1, 5];\n\nexport interface ClientHello {\n protocol: readonly [number, number];\n lock?: string;\n}\n\nexport interface KernelHello {\n protocol: readonly [number, number];\n lock?: string;\n traits?: RealmTransportTraits;\n error?: string;\n}\n\nexport interface SpawnRequestOptions {\n cwd?: string;\n env?: Record<string, string>;\n stdin?: Uint8Array;\n pgid?: number;\n}\n\nexport interface TerminalRequestOptions {\n cwd?: string;\n env?: Record<string, string>;\n cols?: number;\n rows?: number;\n}\n\nexport type TerminalAction =\n | { action: 'write'; bytes: Uint8Array }\n | { action: 'resize'; cols: number; rows: number }\n | { action: 'signal'; signal: string }\n | { action: 'close' };\n\nexport const FS_METHODS = [\n 'readFile',\n 'writeFile',\n 'stat',\n 'lstat',\n 'readdir',\n 'mkdir',\n 'rm',\n 'rename',\n 'realpath',\n 'symlink',\n 'readlink',\n 'exists',\n] as const;\n\nexport type FsMethod = (typeof FS_METHODS)[number];\n\nexport type ClientCall =\n | { op: 'spawn'; argv: string[]; options: SpawnRequestOptions }\n | { op: 'open-terminal'; argv: string[]; options: TerminalRequestOptions }\n | ({ op: 'terminal'; terminal: number } & TerminalAction)\n | { op: 'kill'; pid: number; signal: string }\n | { op: 'ps' }\n | { op: 'fs'; method: FsMethod; args: unknown[] }\n | { op: 'watch'; paths: string[]; recursive: boolean }\n | { op: 'unwatch'; watch: number }\n | { op: 'mount'; spec: MountSpec }\n | { op: 'umount'; target: string }\n | { op: 'mounts' }\n | { op: 'dial'; port: number; host?: string }\n | { op: 'serve-cdp'; runtime?: string }\n | { op: 'unserve-cdp'; registration: number }\n | { op: 'detach'; kill?: boolean };\n\nexport type ClientRequest = ClientCall & { id: number };\n\nexport interface ClientReply {\n id: number;\n result?: unknown;\n error?: string;\n code?: string;\n fd?: 1 | 2;\n bytes?: Uint8Array;\n started?: number;\n}\n\nexport type ClientMessage = { hello: ClientHello } | ClientRequest | TransportCall;\n\nexport interface WatchEvent {\n watch: number;\n change: WatchChange;\n}\n\nexport type KernelMessage = { hello: KernelHello } | ClientReply | WatchEvent | TransportReply;\n\nexport interface ProcessEntry {\n pid: number;\n ppid: number;\n pgid: number;\n sid: number;\n argv: string[];\n tty: string | null;\n started: number;\n state: 'S' | 'Z';\n memory: number;\n}\n\nexport interface MessagePortLike {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(type: 'message' | 'close', handler: (event: MessageEvent) => void): void;\n start?(): void;\n close?(): void;\n}\n\nexport interface LockManagerLike {\n request(name: string, callback: () => Promise<unknown> | unknown): Promise<unknown>;\n}\n\nexport function locksOf(scope: object = globalThis): LockManagerLike | undefined {\n const locks = (scope as { navigator?: { locks?: LockManagerLike } }).navigator?.locks;\n return typeof locks?.request === 'function' ? locks : undefined;\n}\n\nexport function versionError(theirs: unknown): string | undefined {\n const major = Array.isArray(theirs) ? theirs[0] : undefined;\n if (major === PROTOCOL[0]) return undefined;\n const named = typeof major === 'number' ? `${major}.x` : 'without a version';\n return `slicc-kernel client protocol ${named} is not supported: this side speaks ${PROTOCOL.join('.')}`;\n}\n", "import { type CdpOpen, serveCdpPort } from '../cdp/port.ts';\nimport type { CdpHook } from '../cdp/types.ts';\nimport type { FsStat } from '../fs/types.ts';\nimport type { WatchChange } from '../fs/watch.ts';\nimport { RemoteTransport, type TransportReply } from '../kernel/net/remote-transport.ts';\nimport type {\n RealmTransport,\n RealmTransportRequest,\n RealmTransportResponse,\n} from '../kernel/net/transport.ts';\nimport type { MountEntry, MountSpec } from '../mount/mount-fs.ts';\nimport { type DialledSocket, type DialOptions, dialStream } from './dial-stream.ts';\nimport { type LoopbackFetchOptions, loopbackFetch } from './loopback-fetch.ts';\nimport {\n type ClientCall,\n type ClientReply,\n type FsMethod,\n type KernelHello,\n type LockManagerLike,\n locksOf,\n type MessagePortLike,\n PROTOCOL,\n type ProcessEntry,\n type TerminalAction,\n versionError,\n} from './protocol.ts';\n\nexport type { WatchChange } from '../fs/watch.ts';\nexport type { ProcessEntry } from './protocol.ts';\n\nexport class KernelGoneError extends Error {\n constructor(message = 'the slicc-kernel this client was attached to is gone') {\n super(message);\n this.name = 'KernelGoneError';\n }\n}\n\nexport class KernelCallError extends Error {\n readonly code: string | undefined;\n\n constructor(message: string, code?: string) {\n super(message);\n this.name = 'KernelCallError';\n this.code = code;\n }\n}\n\nexport interface AttachOptions {\n timeoutMs?: number;\n locks?: LockManagerLike | null;\n}\n\nexport interface SpawnOptions {\n cwd?: string;\n env?: Record<string, string>;\n stdin?: string | Uint8Array;\n group?: 'new';\n pgid?: number;\n onStdout?: (bytes: Uint8Array) => void;\n onStderr?: (bytes: Uint8Array) => void;\n}\n\nexport interface SpawnedProcess {\n readonly pid: number;\n readonly pgid: number;\n readonly exited: Promise<number>;\n signal(name?: string): Promise<void>;\n}\n\nexport interface ClientRunOptions {\n cwd?: string;\n env?: Record<string, string>;\n stdin?: string | Uint8Array;\n onStdout?: (text: string) => void;\n onStderr?: (text: string) => void;\n}\n\nexport interface ClientRunResult {\n pid: number;\n status: number;\n stdout: string;\n stderr: string;\n}\n\nexport interface ClientTerminalOptions {\n cwd?: string;\n env?: Record<string, string>;\n cols?: number;\n rows?: number;\n onData?: (bytes: Uint8Array) => void;\n}\n\nexport interface ClientTerminal {\n readonly pid: number;\n readonly exited: Promise<number>;\n onData: ((bytes: Uint8Array) => void) | null;\n write(data: string | Uint8Array): void;\n resize(cols: number, rows: number): void;\n signal(name: string): void;\n close(): void;\n}\n\nexport interface ClientFs {\n readFile(path: string): Promise<Uint8Array>;\n readText(path: string): Promise<string>;\n writeFile(path: string, data: string | Uint8Array): Promise<void>;\n stat(path: string): Promise<FsStat>;\n lstat(path: string): Promise<FsStat>;\n readdir(path: string): Promise<string[]>;\n mkdir(path: string): Promise<void>;\n rm(path: string, options?: { force?: boolean }): Promise<void>;\n rename(from: string, to: string): Promise<void>;\n realpath(path: string): Promise<string>;\n symlink(target: string, path: string): Promise<void>;\n readlink(path: string): Promise<string>;\n exists(path: string): Promise<boolean>;\n watch(\n paths: string[],\n options: { recursive?: boolean },\n onChange: (change: WatchChange) => void\n ): Promise<{ close(): void }>;\n}\n\nexport type ClientFetchRequest = Omit<RealmTransportRequest, 'signal'> & { signal?: AbortSignal };\n\nexport interface KernelClient {\n readonly protocol: readonly [number, number];\n readonly transport: RealmTransport;\n readonly closed: Promise<Error>;\n spawn(argv: string[], options?: SpawnOptions): Promise<SpawnedProcess>;\n run(argv: string[], options?: ClientRunOptions): Promise<ClientRunResult>;\n openTerminal(argv: string[], options?: ClientTerminalOptions): Promise<ClientTerminal>;\n ps(): Promise<ProcessEntry[]>;\n mount(spec: MountSpec): Promise<MountEntry>;\n umount(target: string): Promise<void>;\n mounts(): Promise<MountEntry[]>;\n kill(pid: number, signal?: string): Promise<void>;\n fetch(request: ClientFetchRequest): Promise<RealmTransportResponse>;\n dial(options: DialOptions): Promise<DialledSocket>;\n loopbackFetch(input: RequestInfo | URL, options: LoopbackFetchOptions): Promise<Response>;\n readonly fs: ClientFs;\n serveCdp(hook: CdpHook, options?: { runtime?: string }): Promise<{ close(): void }>;\n close(options?: { kill?: boolean }): Promise<void>;\n}\n\ninterface Pending {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n output?: (fd: 1 | 2, bytes: Uint8Array) => void;\n started?: (pid: number) => void;\n}\n\ntype Handlers = Omit<Pending, 'resolve' | 'reject'>;\n\nconst encoder = new TextEncoder();\nconst bytesOf = (data: string | Uint8Array) =>\n typeof data === 'string' ? encoder.encode(data) : data;\n\nconst UNAVAILABLE = {\n manualRedirects: false,\n encodedBodies: false,\n maxRequestBody: 0,\n unavailable: true,\n} as const;\n\nasync function holdLock(\n locks: LockManagerLike | undefined\n): Promise<{ name?: string; release(): void }> {\n if (!locks) return { release() {} };\n const name = `slicc-kernel-client:${crypto.randomUUID()}`;\n const granted = Promise.withResolvers<void>();\n const held = Promise.withResolvers<void>();\n void locks.request(name, () => {\n granted.resolve();\n return held.promise;\n });\n await granted.promise;\n return { name, release: () => held.resolve() };\n}\n\nfunction concat(chunks: Uint8Array[]): Uint8Array {\n const out = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));\n let at = 0;\n for (const chunk of chunks) {\n out.set(chunk, at);\n at += chunk.length;\n }\n return out;\n}\n\nclass Channel {\n readonly greeted = Promise.withResolvers<KernelHello>();\n readonly ended = Promise.withResolvers<Error>();\n remote: RemoteTransport | undefined;\n failure: Error | undefined;\n private readonly pending = new Map<number, Pending>();\n readonly watching = new Map<number, (change: WatchChange) => void>();\n readonly cdpHooks = new Map<number, CdpHook>();\n private readonly cdpOpen = new Set<() => void>();\n protocol: readonly [number, number] = PROTOCOL;\n private nextId = 0;\n private readonly port: MessagePortLike;\n private readonly release: () => void;\n\n constructor(port: MessagePortLike, release: () => void) {\n this.port = port;\n this.release = release;\n this.greeted.promise.catch(() => undefined);\n port.addEventListener('message', (event) => this.receive(event.data));\n port.addEventListener('close', () => this.end(new KernelGoneError()));\n port.start?.();\n }\n\n post(message: object): void {\n if (!this.failure) this.port.postMessage(message);\n }\n\n end(error: Error): void {\n if (this.failure) return;\n this.failure = error;\n this.greeted.reject(error);\n this.remote?.fail(error);\n for (const close of [...this.cdpOpen]) close();\n this.cdpOpen.clear();\n for (const call of this.pending.values()) call.reject(error);\n this.pending.clear();\n this.release();\n this.port.close?.();\n this.ended.resolve(error);\n }\n\n request(call: ClientCall, handlers: Handlers = {}): { id: number; done: Promise<unknown> } {\n const id = ++this.nextId;\n const { failure } = this;\n if (failure) return { id, done: Promise.reject(failure) };\n const done = new Promise((resolve, reject) => {\n this.pending.set(id, { resolve, reject, ...handlers });\n this.port.postMessage({ ...call, id });\n });\n return { id, done };\n }\n\n call(call: ClientCall): Promise<unknown> {\n return this.request(call).done;\n }\n\n private receive(data: {\n hello?: KernelHello;\n net?: unknown;\n bye?: string;\n watch?: number;\n change?: WatchChange;\n cdpOpen?: CdpOpen['cdpOpen'];\n port?: CdpOpen['port'];\n }): void {\n if (data.cdpOpen && data.port) {\n this.openCdp(data as CdpOpen);\n return;\n }\n if (data.watch !== undefined) {\n this.watching.get(data.watch)?.(data.change as WatchChange);\n return;\n }\n if (data.bye !== undefined) {\n this.end(new KernelGoneError(data.bye));\n return;\n }\n if (data.hello) {\n this.greeted.resolve(data.hello);\n return;\n }\n if (data.net !== undefined) {\n this.remote?.receive(data as TransportReply);\n return;\n }\n const reply = data as ClientReply;\n const call = this.pending.get(reply.id);\n if (!call) return;\n if (reply.fd !== undefined) {\n call.output?.(reply.fd, reply.bytes as Uint8Array);\n return;\n }\n if (reply.started !== undefined) {\n call.started?.(reply.started);\n return;\n }\n this.pending.delete(reply.id);\n if (reply.error !== undefined) call.reject(new KernelCallError(reply.error, reply.code));\n else call.resolve(reply.result);\n }\n\n private openCdp({ cdpOpen: { registration, ...request }, port }: CdpOpen): void {\n const hook = registration === undefined ? undefined : this.cdpHooks.get(registration);\n if (!hook || this.failure) {\n port.postMessage({ error: 'this client no longer serves CDP' });\n port.close();\n return;\n }\n const close = serveCdpPort(port, hook, request, () => this.cdpOpen.delete(close));\n this.cdpOpen.add(close);\n }\n}\n\nasync function serveCdpOn(\n channel: Channel,\n hook: CdpHook,\n options: { runtime?: string }\n): Promise<{ close(): void }> {\n await since(channel, 5, 'CDP serving', async () => {});\n const req = {\n op: 'serve-cdp' as const,\n ...(options.runtime ? { runtime: options.runtime } : {}),\n };\n const { id, done } = channel.request(req);\n channel.cdpHooks.set(id, hook);\n try {\n await done;\n } catch (error) {\n channel.cdpHooks.delete(id);\n throw error;\n }\n return {\n close: () => {\n if (!channel.cdpHooks.delete(id)) return;\n void channel.call({ op: 'unserve-cdp', registration: id }).catch(() => undefined);\n },\n };\n}\n\nasync function dialOn(channel: Channel, { port, host }: DialOptions): Promise<DialledSocket> {\n const req = { op: 'dial' as const, port, ...(host ? { host } : {}) };\n return dialStream((await since(channel, 4, 'dial', () => channel.call(req))) as MessagePort);\n}\n\nasync function spawnOn(\n channel: Channel,\n argv: string[],\n opts: SpawnOptions\n): Promise<SpawnedProcess> {\n const { onStdout, onStderr, stdin, group: _, ...rest } = opts;\n if (opts.pgid !== undefined)\n await since(channel, 3, 'spawn into a process group', async () => {});\n const started = Promise.withResolvers<number>();\n const options = { ...rest, ...(stdin !== undefined ? { stdin: bytesOf(stdin) } : {}) };\n const { done } = channel.request(\n { op: 'spawn', argv, options },\n {\n output: (fd, bytes) => (fd === 1 ? onStdout : onStderr)?.(bytes),\n started: started.resolve,\n }\n );\n done.catch(started.reject);\n const pid = await started.promise;\n const pgid = opts.pgid ?? pid;\n return {\n pid,\n pgid,\n exited: done as Promise<number>,\n signal: async (name = 'SIGTERM') =>\n void (await channel.call({ op: 'kill', pid: -pgid, signal: name })),\n };\n}\n\nasync function runOn(\n channel: Channel,\n argv: string[],\n opts: ClientRunOptions\n): Promise<ClientRunResult> {\n const { onStdout, onStderr, ...rest } = opts;\n const chunks: Record<1 | 2, Uint8Array[]> = { 1: [], 2: [] };\n const live = { 1: new TextDecoder(), 2: new TextDecoder() };\n const tee = (fd: 1 | 2, on?: (text: string) => void) => (bytes: Uint8Array) => {\n chunks[fd].push(bytes);\n on?.(live[fd].decode(bytes, { stream: true }));\n };\n const child = await spawnOn(channel, argv, {\n ...rest,\n onStdout: tee(1, onStdout),\n onStderr: tee(2, onStderr),\n });\n const status = await child.exited;\n const text = (fd: 1 | 2) => new TextDecoder().decode(concat(chunks[fd]));\n return { pid: child.pid, status, stdout: text(1), stderr: text(2) };\n}\n\nasync function terminalOn(\n channel: Channel,\n argv: string[],\n opts: ClientTerminalOptions\n): Promise<ClientTerminal> {\n const { onData, ...options } = opts;\n let listener = onData ?? null;\n const backlog: Uint8Array[] = [];\n const started = Promise.withResolvers<number>();\n const { id, done } = channel.request(\n { op: 'open-terminal', argv, options },\n {\n output: (_fd, bytes) => (listener ? listener(bytes) : backlog.push(bytes)),\n started: started.resolve,\n }\n );\n done.catch(started.reject);\n const pid = await started.promise;\n const send = (action: TerminalAction) =>\n void channel.call({ op: 'terminal', terminal: id, ...action }).catch(() => undefined);\n return {\n pid,\n exited: done as Promise<number>,\n get onData() {\n return listener;\n },\n set onData(next) {\n listener = next;\n if (next) for (const bytes of backlog.splice(0)) next(bytes);\n },\n write: (data) => send({ action: 'write', bytes: bytesOf(data) }),\n resize: (cols, rows) => send({ action: 'resize', cols, rows }),\n signal: (name) => send({ action: 'signal', signal: name }),\n close: () => send({ action: 'close' }),\n };\n}\n\nfunction fsOn(channel: Channel): ClientFs {\n const fs = (method: FsMethod, ...args: unknown[]) => channel.call({ op: 'fs', method, args });\n const done = async (method: FsMethod, ...args: unknown[]) => void (await fs(method, ...args));\n return {\n readFile: (path) => fs('readFile', path) as Promise<Uint8Array>,\n readText: async (path) => new TextDecoder().decode((await fs('readFile', path)) as Uint8Array),\n writeFile: (path, data) => done('writeFile', path, data),\n stat: (path) => fs('stat', path) as Promise<FsStat>,\n lstat: (path) => fs('lstat', path) as Promise<FsStat>,\n readdir: (path) => fs('readdir', path) as Promise<string[]>,\n mkdir: (path) => done('mkdir', path),\n rm: (path, o = {}) => done('rm', path, o.force === true),\n rename: (from, to) => done('rename', from, to),\n realpath: (path) => fs('realpath', path) as Promise<string>,\n symlink: (target, path) => done('symlink', target, path),\n readlink: (path) => fs('readlink', path) as Promise<string>,\n exists: (path) => fs('exists', path) as Promise<boolean>,\n async watch(paths, options, onChange) {\n if (channel.protocol[1] < 1) {\n throw new KernelCallError(\n `this kernel speaks protocol ${channel.protocol.join('.')}, which has no watch`,\n 'ENOSYS'\n );\n }\n const { id, done } = channel.request({\n op: 'watch',\n paths,\n recursive: options.recursive === true,\n });\n channel.watching.set(id, onChange);\n try {\n await done;\n } catch (error) {\n channel.watching.delete(id);\n throw error;\n }\n return {\n close: () => {\n if (!channel.watching.delete(id)) return;\n void channel.call({ op: 'unwatch', watch: id }).catch(() => undefined);\n },\n };\n },\n };\n}\n\nfunction since(channel: Channel, minor: number, what: string, run: () => Promise<unknown>) {\n if (channel.protocol[1] < minor) {\n return Promise.reject(\n new KernelCallError(\n `this kernel speaks protocol ${channel.protocol.join('.')}, which has no ${what}`,\n 'ENOSYS'\n )\n );\n }\n return run();\n}\n\nasync function greet(\n channel: Channel,\n lock: string | undefined,\n timeoutMs: number\n): Promise<KernelHello> {\n channel.post({ hello: { protocol: PROTOCOL, ...(lock ? { lock } : {}) } });\n const timer = setTimeout(\n () =>\n channel.end(\n new KernelGoneError(`no slicc-kernel answered on this port within ${timeoutMs} ms`)\n ),\n timeoutMs\n );\n let hello: KernelHello;\n try {\n hello = await channel.greeted.promise;\n } finally {\n clearTimeout(timer);\n }\n const refused = hello.error ?? versionError(hello.protocol);\n if (refused) {\n const error = new Error(refused);\n channel.end(error);\n throw error;\n }\n return hello;\n}\n\nexport async function attachKernel(\n port: MessagePortLike,\n options: AttachOptions = {}\n): Promise<KernelClient> {\n const locks = options.locks === null ? undefined : (options.locks ?? locksOf());\n const own = await holdLock(locks);\n const channel = new Channel(port, own.release);\n const hello = await greet(channel, own.name, options.timeoutMs ?? 10_000);\n channel.protocol = hello.protocol;\n if (hello.lock && locks) void locks.request(hello.lock, () => channel.end(new KernelGoneError()));\n const transport = new RemoteTransport(\n { postMessage: (call) => channel.post(call) },\n hello.traits ?? UNAVAILABLE\n );\n channel.remote = transport;\n return {\n protocol: hello.protocol,\n transport,\n closed: channel.ended.promise,\n spawn: (argv, opts = {}) => spawnOn(channel, argv, opts),\n run: (argv, opts = {}) => runOn(channel, argv, opts),\n openTerminal: (argv, opts = {}) => terminalOn(channel, argv, opts),\n ps: () => channel.call({ op: 'ps' }) as Promise<ProcessEntry[]>,\n mount: (spec) =>\n since(channel, 2, 'mount', () => channel.call({ op: 'mount', spec })) as Promise<MountEntry>,\n umount: async (target) =>\n void (await since(channel, 2, 'umount', () => channel.call({ op: 'umount', target }))),\n mounts: () =>\n since(channel, 2, 'mounts', () => channel.call({ op: 'mounts' })) as Promise<MountEntry[]>,\n kill: async (pid, signal = 'SIGTERM') => void (await channel.call({ op: 'kill', pid, signal })),\n fetch: (req) => {\n const { signal, ...rest } = req;\n return transport.fetch({ ...rest, signal: signal ?? new AbortController().signal });\n },\n dial: (options) => dialOn(channel, options),\n loopbackFetch: (input, options) => loopbackFetch((o) => dialOn(channel, o), input, options),\n fs: fsOn(channel),\n serveCdp: (hook, options = {}) => serveCdpOn(channel, hook, options),\n async close(o = {}) {\n if (channel.failure) return;\n await channel\n .call({ op: 'detach', ...(o.kill ? { kill: true } : {}) })\n .catch(() => undefined);\n channel.end(new KernelGoneError('this client is closed'));\n },\n };\n}\n", "export const DRIVER_PROTOCOL: readonly [number, number] = [1, 1];\n\nexport interface DriverCapabilities {\n readonly?: boolean;\n symlinks?: boolean;\n chmod?: boolean;\n ranges?: boolean;\n listingStats?: boolean;\n caseInsensitive?: boolean;\n normalization?: 'none' | 'nfc' | 'nfd-insensitive';\n maxIo?: number;\n maxFile?: number;\n attrTtl?: number;\n entryTtl?: number;\n}\n\nexport interface DriverAttr {\n kind: 'file' | 'directory' | 'symlink';\n size: number;\n mtime: number;\n mode?: number;\n ino?: number;\n etag?: string;\n}\n\nexport interface DriverEntry {\n name: string;\n kind: DriverAttr['kind'];\n attr?: DriverAttr;\n}\n\nexport interface DriverStatfs {\n bsize: number;\n blocks: number;\n bfree: number;\n}\n\nexport interface MountRequestInfo {\n source: string;\n options: Record<string, string>;\n}\n\nexport type DriverCall =\n | { op: 'getattr'; path: string }\n | { op: 'readdir'; path: string }\n | {\n op: 'open';\n path: string;\n write: boolean;\n create: boolean;\n truncate: boolean;\n exclusive: boolean;\n ifMatch?: string;\n }\n | { op: 'read'; fh: number; offset: number; size: number }\n | { op: 'write'; fh: number; offset: number; bytes: Uint8Array }\n | { op: 'release'; fh: number }\n | { op: 'mkdir'; path: string }\n | { op: 'rmdir'; path: string }\n | { op: 'unlink'; path: string }\n | { op: 'rename'; from: string; to: string }\n | { op: 'symlink'; target: string; path: string }\n | { op: 'readlink'; path: string }\n | { op: 'setattr'; path: string; mode?: number; mtime?: number; size?: number }\n | { op: 'statfs' };\n\nexport type DriverRequest = DriverCall & { id: number };\n\nexport interface DriverReply {\n id: number;\n result?: unknown;\n errno?: string;\n message?: string;\n}\n\nexport interface KernelDriverHello {\n protocol: readonly [number, number];\n mount: MountRequestInfo;\n}\n\nexport interface DriverHello {\n protocol: readonly [number, number];\n capabilities?: DriverCapabilities;\n error?: string;\n errno?: string;\n}\n\nexport type KernelToDriver = { hello: KernelDriverHello } | DriverRequest;\n\nexport type DriverToKernel = { hello: DriverHello } | DriverReply | { invalidate: string[] | true };\n\nexport function driverVersionError(theirs: unknown): string | undefined {\n const major = Array.isArray(theirs) ? theirs[0] : undefined;\n if (major === DRIVER_PROTOCOL[0]) return undefined;\n const named = typeof major === 'number' ? `${major}.x` : 'without a version';\n return `slicc-kernel driver protocol ${named} is not supported: this side speaks ${DRIVER_PROTOCOL.join('.')}`;\n}\n", "import {\n DRIVER_PROTOCOL,\n type DriverAttr,\n type DriverCapabilities,\n type DriverEntry,\n type DriverRequest,\n type DriverStatfs,\n driverVersionError,\n type KernelDriverHello,\n type MountRequestInfo,\n} from './protocol.ts';\n\nexport type {\n DriverAttr,\n DriverCapabilities,\n DriverEntry,\n DriverStatfs,\n MountRequestInfo,\n} from './protocol.ts';\n\nexport interface OpenFlags {\n write: boolean;\n create: boolean;\n truncate: boolean;\n exclusive: boolean;\n ifMatch?: string;\n}\n\nexport interface FilesystemHandlers {\n mount?(\n info: MountRequestInfo\n ): Promise<DriverCapabilities | undefined> | DriverCapabilities | undefined;\n getattr(path: string): Promise<DriverAttr>;\n readdir(path: string): Promise<DriverEntry[]>;\n open(path: string, flags: OpenFlags): Promise<number>;\n read(fh: number, offset: number, size: number): Promise<Uint8Array>;\n write(fh: number, offset: number, bytes: Uint8Array): Promise<void>;\n release(fh: number): Promise<void>;\n mkdir(path: string): Promise<void>;\n rmdir(path: string): Promise<void>;\n unlink(path: string): Promise<void>;\n rename(from: string, to: string): Promise<void>;\n symlink?(target: string, path: string): Promise<void>;\n readlink?(path: string): Promise<string>;\n setattr?(path: string, attr: { mode?: number; mtime?: number; size?: number }): Promise<void>;\n statfs?(): Promise<DriverStatfs>;\n}\n\nexport interface DriverPort {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(type: 'message', handler: (event: MessageEvent) => void): void;\n start?(): void;\n}\n\nexport interface ServedFilesystem {\n invalidate(paths: string[] | true): void;\n}\n\nexport function fsError(code: string, message = code): Error & { code: string } {\n return Object.assign(new Error(message), { code });\n}\n\nasync function dispatch(h: FilesystemHandlers, req: DriverRequest): Promise<unknown> {\n switch (req.op) {\n case 'getattr':\n return h.getattr(req.path);\n case 'readdir':\n return h.readdir(req.path);\n case 'open':\n return h.open(req.path, {\n write: req.write,\n create: req.create,\n truncate: req.truncate,\n exclusive: req.exclusive,\n ...(req.ifMatch !== undefined ? { ifMatch: req.ifMatch } : {}),\n });\n case 'read':\n return h.read(req.fh, req.offset, req.size);\n case 'write':\n return h.write(req.fh, req.offset, req.bytes);\n case 'release':\n return h.release(req.fh);\n case 'mkdir':\n return h.mkdir(req.path);\n case 'rmdir':\n return h.rmdir(req.path);\n case 'unlink':\n return h.unlink(req.path);\n case 'rename':\n return h.rename(req.from, req.to);\n case 'symlink':\n if (!h.symlink) throw fsError('EPERM', 'this file system has no symlinks');\n return h.symlink(req.target, req.path);\n case 'readlink':\n if (!h.readlink) throw fsError('EINVAL', `${req.path} is not a symlink`);\n return h.readlink(req.path);\n case 'setattr':\n return h.setattr?.(req.path, {\n ...(req.mode !== undefined ? { mode: req.mode } : {}),\n ...(req.mtime !== undefined ? { mtime: req.mtime } : {}),\n ...(req.size !== undefined ? { size: req.size } : {}),\n });\n case 'statfs':\n return h.statfs?.() ?? null;\n default:\n throw fsError('ENOSYS', `unknown driver op ${(req as { op: string }).op}`);\n }\n}\n\nexport function serveFilesystem(\n port: DriverPort,\n handlers: FilesystemHandlers,\n capabilities: DriverCapabilities = {},\n options: { owned?: boolean } = {}\n): ServedFilesystem {\n let open = false;\n const reply = (message: object, transfer: Transferable[] = []) =>\n port.postMessage(message, transfer);\n async function hello(message: KernelDriverHello): Promise<void> {\n const refused = driverVersionError(message.protocol);\n if (refused) {\n reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused, errno: 'EPROTO' } });\n return;\n }\n try {\n const refined = await handlers.mount?.(message.mount);\n open = true;\n reply({\n hello: { protocol: DRIVER_PROTOCOL, capabilities: { ...capabilities, ...refined } },\n });\n } catch (err) {\n const code = (err as { code?: unknown })?.code;\n reply({\n hello: {\n protocol: DRIVER_PROTOCOL,\n error: err instanceof Error ? err.message : String(err),\n errno: typeof code === 'string' ? code : 'EIO',\n },\n });\n }\n }\n port.addEventListener('message', (event) => {\n const data = event.data as { hello?: KernelDriverHello } & DriverRequest;\n if (data.hello) {\n void hello(data.hello);\n return;\n }\n if (!open) return;\n dispatch(handlers, data).then(\n (result) => {\n if (result instanceof Uint8Array) {\n const whole = result.byteOffset === 0 && result.byteLength === result.buffer.byteLength;\n const bytes = options.owned && whole ? result : result.slice();\n reply({ id: data.id, result: bytes }, [bytes.buffer]);\n } else reply({ id: data.id, result: result ?? null });\n },\n (err: unknown) => {\n const code = (err as { code?: unknown })?.code;\n reply({\n id: data.id,\n errno: typeof code === 'string' ? code : 'EIO',\n message: err instanceof Error ? err.message : String(err),\n });\n }\n );\n });\n port.start?.();\n return { invalidate: (paths) => reply({ invalidate: paths }) };\n}\n", "import { attachKernel } from '../client/attach.ts';\nimport type { MessagePortLike } from '../client/protocol.ts';\nimport type { RealmTransportRequest, RealmTransportResponse } from '../kernel/net/transport.ts';\nimport { type FilesystemHandlers, serveFilesystem } from './driver.ts';\nimport { DRIVER_PROTOCOL, type DriverCapabilities } from './protocol.ts';\n\nexport interface DriverStart {\n code: string;\n driver: MessagePortLike;\n client: MessagePortLike;\n}\n\nexport interface DriverEnv {\n fetch(\n request: Omit<RealmTransportRequest, 'signal'> & { signal?: AbortSignal }\n ): Promise<RealmTransportResponse>;\n}\n\nexport type DriverFactory = (\n env: DriverEnv\n) =>\n | Promise<{ handlers: FilesystemHandlers; capabilities?: DriverCapabilities }>\n | { handlers: FilesystemHandlers; capabilities?: DriverCapabilities };\n\nexport async function hostDriver(\n start: DriverStart,\n load: (code: string) => Promise<{ default?: unknown }>\n): Promise<void> {\n try {\n const factory = (await load(start.code)).default;\n if (typeof factory !== 'function') {\n throw new Error('a slicc file system module must export a default function');\n }\n const client = await attachKernel(start.client, { locks: null });\n const made = await (factory as DriverFactory)({ fetch: (request) => client.fetch(request) });\n serveFilesystem(start.driver, made.handlers, made.capabilities ?? {});\n } catch (err) {\n start.driver.postMessage({\n hello: {\n protocol: DRIVER_PROTOCOL,\n error: `the driver did not start: ${err instanceof Error ? err.message : String(err)}`,\n errno: 'EIO',\n },\n });\n }\n}\n"],
|
|
4
|
+
"sourcesContent": ["import { parentPort } from 'node:worker_threads';\nimport { type DriverStart, hostDriver } from './mount/host.ts';\n\nparentPort?.once('message', (start: DriverStart) =>\n hostDriver(\n start,\n (code) => import(`data:text/javascript;base64,${Buffer.from(code).toString('base64')}`)\n )\n);\n", "import type { CdpConnection, CdpHook, CdpRequest } from './types.ts';\n\nexport interface CdpPortLike {\n postMessage(message: unknown): void;\n addEventListener(type: 'message', handler: (event: MessageEvent) => void): void;\n start?(): void;\n close(): void;\n}\n\nexport type CdpWire = { open: true } | { error: string } | { m: string } | { close: string | null };\n\nexport interface CdpOpen {\n cdpOpen: CdpRequest & { registration?: number };\n port: CdpPortLike;\n}\n\nexport function serveCdpPort(\n port: CdpPortLike,\n hook: CdpHook,\n request: CdpRequest,\n onDone?: () => void\n): () => void {\n let conn: CdpConnection | undefined;\n let done = false;\n const end = () => {\n done = true;\n port.close();\n onDone?.();\n };\n const finish = (reason: string | null) => {\n if (done) return;\n port.postMessage({ close: reason });\n end();\n };\n port.addEventListener('message', ({ data }: MessageEvent<{ m?: string; close?: true }>) => {\n if (data.m !== undefined) conn?.send(data.m);\n else if (data.close && !done) {\n end();\n conn?.close();\n }\n });\n port.start?.();\n new Promise<CdpConnection>((resolve) => resolve(hook(request))).then(\n (opened) => {\n conn = opened;\n if (done) return opened.close();\n opened.onmessage = (m) => {\n if (!done) port.postMessage({ m });\n };\n opened.onclose = (reason) => finish(reason ?? null);\n port.postMessage({ open: true });\n },\n (e: unknown) => {\n if (done) return;\n port.postMessage({ error: e instanceof Error ? e.message : String(e) });\n end();\n }\n );\n return () => {\n if (done) return;\n conn?.close();\n finish('the CDP host is gone');\n };\n}\n\nexport function portCdpHook(\n ask: (request: CdpRequest, port: CdpPortLike) => void,\n gone?: Promise<unknown>\n): CdpHook {\n return (request) =>\n new Promise((resolve, reject) => {\n const { port1, port2 } = new MessageChannel();\n let closed = false;\n const conn: CdpConnection = {\n send: (m) => {\n if (!closed) port1.postMessage({ m });\n },\n onmessage: null,\n onclose: null,\n close: () => {\n if (closed) return;\n closed = true;\n port1.postMessage({ close: true });\n port1.close();\n },\n };\n port1.addEventListener('message', ({ data }: MessageEvent<CdpWire>) => {\n if ('m' in data) conn.onmessage?.(data.m);\n else if ('open' in data) resolve(conn);\n else if ('error' in data) {\n closed = true;\n port1.close();\n reject(new Error(data.error));\n } else if (!closed) {\n closed = true;\n port1.close();\n conn.onclose?.(data.close ?? undefined);\n }\n });\n port1.start();\n void gone?.then(() => {\n if (closed) return;\n closed = true;\n port1.close();\n reject(new Error('the CDP host is gone'));\n conn.onclose?.('the CDP host is gone');\n });\n ask(request, port2 as unknown as CdpPortLike);\n });\n}\n", "import type {\n HeaderList,\n RealmTransport,\n RealmTransportRequest,\n RealmTransportResponse,\n RealmTransportTraits,\n} from './transport.ts';\n\nexport type TransportCall =\n | {\n net: 'fetch';\n nid: number;\n url: string;\n method: string;\n headers: HeaderList;\n body?: Uint8Array;\n }\n | { net: 'read'; nid: number }\n | { net: 'cancel'; nid: number };\n\nexport type TransportReply =\n | { net: 'head'; nid: number; status: number; statusText: string; headers: HeaderList }\n | { net: 'chunk'; nid: number; bytes: Uint8Array }\n | { net: 'end'; nid: number }\n | { net: 'error'; nid: number; message: string; status?: number; code?: string };\n\nexport interface TransportPort {\n postMessage(message: TransportCall): void;\n}\n\nexport class TransportError extends Error {\n readonly status: number | undefined;\n readonly code: string | undefined;\n\n constructor(message: string, status?: number, code?: string) {\n super(message);\n this.status = status;\n this.code = code;\n }\n}\n\ninterface Waiter {\n resolve(reply: TransportReply): void;\n reject(error: Error): void;\n}\n\nexport class RemoteTransport implements RealmTransport {\n readonly traits: RealmTransportTraits;\n\n private readonly port: TransportPort;\n\n private readonly waiting = new Map<number, Waiter>();\n\n private nextId = 0;\n\n private failure: Error | undefined;\n\n constructor(port: TransportPort, traits: RealmTransportTraits) {\n this.port = port;\n this.traits = traits;\n }\n\n receive(reply: TransportReply): void {\n const waiter = this.waiting.get(reply.nid);\n if (!waiter) return;\n this.waiting.delete(reply.nid);\n if (reply.net === 'error') {\n waiter.reject(new TransportError(reply.message, reply.status, reply.code));\n } else waiter.resolve(reply);\n }\n\n fail(error: Error): void {\n this.failure = error;\n for (const waiter of this.waiting.values()) waiter.reject(error);\n this.waiting.clear();\n }\n\n private ask(call: TransportCall): Promise<TransportReply> {\n if (this.failure) return Promise.reject(this.failure);\n return new Promise((resolve, reject) => {\n this.waiting.set(call.nid, { resolve, reject });\n this.port.postMessage(call);\n });\n }\n\n private cancel(nid: number): void {\n const waiter = this.waiting.get(nid);\n this.waiting.delete(nid);\n waiter?.reject(new TransportError('the request was cancelled'));\n this.port.postMessage({ net: 'cancel', nid });\n }\n\n async fetch(request: RealmTransportRequest): Promise<RealmTransportResponse> {\n const nid = ++this.nextId;\n const { signal } = request;\n signal.throwIfAborted();\n let finished = false;\n const abort = () => {\n finished = true;\n this.cancel(nid);\n };\n signal.addEventListener('abort', abort, { once: true });\n const finish = () => {\n if (finished) return;\n finished = true;\n signal.removeEventListener('abort', abort);\n };\n let head: TransportReply;\n try {\n head = await this.ask({\n net: 'fetch',\n nid,\n url: request.url,\n method: request.method,\n headers: request.headers,\n ...(request.body ? { body: request.body } : {}),\n });\n } catch (e) {\n finish();\n throw e;\n }\n const { status, statusText, headers } = head as Extract<TransportReply, { net: 'head' }>;\n const read = async (): Promise<Uint8Array | undefined> => {\n if (finished) return undefined;\n const reply = await this.ask({ net: 'read', nid }).catch((e: unknown) => {\n finish();\n throw e;\n });\n if (reply.net === 'chunk') return reply.bytes;\n finish();\n return undefined;\n };\n const cancel = async () => {\n if (finished) return;\n finish();\n this.cancel(nid);\n };\n return {\n status,\n statusText,\n headers,\n cancel,\n body: {\n [Symbol.asyncIterator]: () => ({\n next: async () => {\n const bytes = await read();\n return bytes ? { value: bytes, done: false } : { value: undefined, done: true };\n },\n return: async () => {\n await cancel();\n return { value: undefined, done: true };\n },\n }),\n },\n };\n }\n}\n", "export interface DialledSocket {\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n close(): void;\n}\n\nexport interface DialOptions {\n port: number;\n host?: string;\n}\n\ninterface StreamPort {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(type: 'message', handler: (event: MessageEvent) => void): void;\n start?(): void;\n close(): void;\n}\n\ntype DialMessage = Uint8Array | { end: true } | { ack: true } | { error: string };\n\nexport interface DialHandle extends DialledSocket {\n fail(error: Error): void;\n}\n\nexport function errorWithCode(message: string): Error & { code?: string } {\n const code = /^E[A-Z]+/.exec(message)?.[0];\n return Object.assign(new Error(message), code ? { code } : {});\n}\n\nexport function dialStream(port: StreamPort, onClose?: () => void): DialHandle {\n let closed = false;\n let reader: ReadableStreamDefaultController<Uint8Array> | undefined;\n const acks: Array<{ resolve: () => void; reject: (error: Error) => void }> = [];\n const readable = new ReadableStream<Uint8Array>({\n start(controller) {\n reader = controller;\n },\n pull() {\n if (!closed) port.postMessage({ more: true });\n },\n cancel() {\n reader = undefined;\n close();\n },\n });\n const finish = (fail?: Error) => {\n const controller = reader;\n reader = undefined;\n for (const ack of acks.splice(0)) {\n if (fail) ack.reject(fail);\n else ack.resolve();\n }\n if (!controller) return;\n if (fail) controller.error(fail);\n else controller.close();\n };\n function shut(): void {\n if (closed) return;\n closed = true;\n port.close();\n onClose?.();\n }\n function close(): void {\n if (!closed) port.postMessage({ close: true });\n shut();\n finish();\n }\n port.addEventListener('message', ({ data }: MessageEvent<DialMessage>) => {\n if (data instanceof Uint8Array) reader?.enqueue(data);\n else if ('ack' in data) acks.shift()?.resolve();\n else if ('end' in data) finish();\n else finish(errorWithCode(data.error));\n });\n port.start?.();\n const writable = new WritableStream<Uint8Array>({\n write(chunk) {\n if (closed) throw errorWithCode('EPIPE: the connection is closed');\n const copy = chunk.slice();\n const acked = new Promise<void>((resolve, reject) => acks.push({ resolve, reject }));\n port.postMessage(copy, [copy.buffer]);\n return acked;\n },\n close() {\n if (!closed) port.postMessage({ end: true });\n },\n abort() {\n close();\n },\n });\n const fail = (error: Error) => {\n shut();\n finish(error);\n };\n return { readable, writable, close, fail };\n}\n", "import { type DialledSocket, type DialOptions, errorWithCode } from './dial-stream.ts';\n\nexport interface LoopbackFetchOptions extends DialOptions {\n signal?: AbortSignal;\n}\n\nconst OWN_HEADERS = new Set(['host', 'connection', 'content-length', 'transfer-encoding']);\nconst encoder = new TextEncoder();\nconst latin1 = new TextDecoder('latin1');\n\nclass ByteSource {\n private buffered = new Uint8Array(0);\n private readonly reader: ReadableStreamDefaultReader<Uint8Array>;\n\n constructor(reader: ReadableStreamDefaultReader<Uint8Array>) {\n this.reader = reader;\n }\n\n private async more(): Promise<boolean> {\n const { done, value } = await this.reader.read();\n if (done) return false;\n const joined = new Uint8Array(this.buffered.length + value.length);\n joined.set(this.buffered);\n joined.set(value, this.buffered.length);\n this.buffered = joined;\n return true;\n }\n\n async until(marker: string): Promise<Uint8Array | undefined> {\n const needle = encoder.encode(marker);\n for (;;) {\n const at = indexOf(this.buffered, needle);\n if (at >= 0) return this.take(at + needle.length);\n if (!(await this.more())) return undefined;\n }\n }\n\n async some(max: number): Promise<Uint8Array | undefined> {\n if (this.buffered.length === 0 && !(await this.more())) return undefined;\n return this.take(Math.min(max, this.buffered.length));\n }\n\n async exactly(count: number): Promise<Uint8Array | undefined> {\n while (this.buffered.length < count) if (!(await this.more())) return undefined;\n return this.take(count);\n }\n\n private take(count: number): Uint8Array {\n const out = this.buffered.slice(0, count);\n this.buffered = this.buffered.slice(count);\n return out;\n }\n}\n\nfunction indexOf(haystack: Uint8Array, needle: Uint8Array): number {\n outer: for (let i = 0; i + needle.length <= haystack.length; i++) {\n for (let j = 0; j < needle.length; j++) if (haystack[i + j] !== needle[j]) continue outer;\n return i;\n }\n return -1;\n}\n\ninterface ResponseHead {\n status: number;\n statusText: string;\n headers: Array<[string, string]>;\n}\n\nfunction parseHead(bytes: Uint8Array): ResponseHead {\n const [statusLine = '', ...lines] = latin1.decode(bytes).split('\\r\\n');\n const match = /^HTTP\\/1\\.[01] (\\d{3})(?: (.*))?$/.exec(statusLine);\n if (!match) throw errorWithCode(`EPROTO: not an HTTP/1.x response: ${statusLine.slice(0, 80)}`);\n const headers: Array<[string, string]> = [];\n for (const line of lines) {\n const colon = line.indexOf(':');\n if (colon > 0) headers.push([line.slice(0, colon).trim(), line.slice(colon + 1).trim()]);\n }\n return { status: Number(match[1]), statusText: match[2] ?? '', headers };\n}\n\nfunction header(head: ResponseHead, name: string): string | undefined {\n return head.headers.find(([key]) => key.toLowerCase() === name)?.[1];\n}\n\nasync function* chunked(source: ByteSource): AsyncGenerator<Uint8Array> {\n for (;;) {\n const line = await source.until('\\r\\n');\n if (!line) throw errorWithCode('EPROTO: the chunked body ended early');\n const size = Number.parseInt(latin1.decode(line).trim().split(';')[0] ?? '', 16);\n if (!Number.isFinite(size)) throw errorWithCode('EPROTO: a malformed chunk size');\n if (size === 0) return;\n const data = await source.exactly(size);\n if (!data || !(await source.exactly(2))) throw errorWithCode('EPROTO: a chunk ended early');\n yield data;\n }\n}\n\nasync function* counted(source: ByteSource, length: number): AsyncGenerator<Uint8Array> {\n let left = length;\n while (left > 0) {\n const data = await source.some(left);\n if (!data) throw errorWithCode('EPROTO: the body ended early');\n left -= data.length;\n yield data;\n }\n}\n\nasync function* untilClose(source: ByteSource): AsyncGenerator<Uint8Array> {\n for (let data = await source.some(65536); data; data = await source.some(65536)) yield data;\n}\n\nfunction bodyOf(source: ByteSource, head: ResponseHead, method: string) {\n if (method === 'HEAD' || head.status === 204 || head.status === 304) return undefined;\n if (header(head, 'transfer-encoding')?.toLowerCase().includes('chunked')) return chunked(source);\n const length = header(head, 'content-length');\n if (length === undefined) return untilClose(source);\n const size = Number(length);\n if (!/^\\d+$/.test(length.trim()) || !Number.isSafeInteger(size)) {\n throw errorWithCode(`EPROTO: a malformed Content-Length: ${length}`);\n }\n return counted(source, size);\n}\n\nfunction stream(chunks: AsyncGenerator<Uint8Array>, socket: DialledSocket, signal?: AbortSignal) {\n return new ReadableStream<Uint8Array>({\n start(controller) {\n signal?.addEventListener('abort', () => controller.error(signal.reason), { once: true });\n },\n async pull(controller) {\n try {\n const next = await chunks.next();\n if (next.done) {\n controller.close();\n socket.close();\n } else controller.enqueue(next.value);\n } catch (err) {\n controller.error(err);\n socket.close();\n }\n },\n cancel() {\n socket.close();\n },\n });\n}\n\nasync function requestBytes(request: Request): Promise<Uint8Array> {\n const url = new URL(request.url);\n const body = request.body ? new Uint8Array(await request.arrayBuffer()) : undefined;\n const lines = [`${request.method} ${url.pathname}${url.search} HTTP/1.1`, `Host: ${url.host}`];\n request.headers.forEach((value, name) => {\n if (!OWN_HEADERS.has(name)) lines.push(`${name}: ${value}`);\n });\n if (body) lines.push(`Content-Length: ${body.length}`);\n lines.push('Connection: close', '', '');\n const head = encoder.encode(lines.join('\\r\\n'));\n const out = new Uint8Array(head.length + (body?.length ?? 0));\n out.set(head);\n if (body) out.set(body, head.length);\n return out;\n}\n\nexport async function loopbackFetch(\n dial: (options: DialOptions) => Promise<DialledSocket>,\n input: RequestInfo | URL,\n options: LoopbackFetchOptions\n): Promise<Response> {\n options.signal?.throwIfAborted();\n const request = input instanceof Request ? input : new Request(input);\n const bytes = await requestBytes(request);\n const socket = await dial({\n port: options.port,\n ...(options.host ? { host: options.host } : {}),\n });\n options.signal?.addEventListener('abort', () => socket.close(), { once: true });\n try {\n const writer = socket.writable.getWriter();\n await writer.write(bytes);\n writer.releaseLock();\n const source = new ByteSource(socket.readable.getReader());\n let head: ResponseHead;\n do {\n const raw = await source.until('\\r\\n\\r\\n');\n if (!raw) throw errorWithCode('ECONNRESET: the connection closed before a response');\n head = parseHead(raw);\n } while (head.status >= 100 && head.status < 200);\n const chunks = bodyOf(source, head, request.method);\n if (!chunks) socket.close();\n return new Response(chunks ? stream(chunks, socket, options.signal) : null, {\n status: head.status,\n statusText: head.statusText,\n headers: head.headers,\n });\n } catch (err) {\n socket.close();\n throw err;\n }\n}\n", "import type { WatchChange } from '../fs/watch.ts';\nimport type { TransportCall, TransportReply } from '../kernel/net/remote-transport.ts';\nimport type { RealmTransportTraits } from '../kernel/net/transport.ts';\nimport type { MountSpec } from '../mount/mount-fs.ts';\n\nexport const PROTOCOL: readonly [number, number] = [1, 7];\n\nexport interface ClientHello {\n protocol: readonly [number, number];\n lock?: string;\n}\n\nexport interface KernelHello {\n protocol: readonly [number, number];\n lock?: string;\n traits?: RealmTransportTraits;\n error?: string;\n}\n\nexport interface SpawnRequestOptions {\n cwd?: string;\n env?: Record<string, string>;\n stdin?: Uint8Array;\n pgid?: number;\n}\n\nexport interface TerminalRequestOptions {\n cwd?: string;\n env?: Record<string, string>;\n cols?: number;\n rows?: number;\n}\n\nexport type TerminalAction =\n | { action: 'write'; bytes: Uint8Array }\n | { action: 'resize'; cols: number; rows: number }\n | { action: 'signal'; signal: string }\n | { action: 'close' };\n\nexport const FS_METHODS = [\n 'readFile',\n 'writeFile',\n 'stat',\n 'lstat',\n 'readdir',\n 'mkdir',\n 'rm',\n 'rename',\n 'realpath',\n 'symlink',\n 'readlink',\n 'exists',\n] as const;\n\nexport type FsMethod = (typeof FS_METHODS)[number];\n\nexport type ClientCall =\n | { op: 'spawn'; argv: string[]; options: SpawnRequestOptions }\n | { op: 'open-terminal'; argv: string[]; options: TerminalRequestOptions }\n | ({ op: 'terminal'; terminal: number } & TerminalAction)\n | { op: 'kill'; pid: number; signal: string }\n | { op: 'ps' }\n | { op: 'fs'; method: FsMethod; args: unknown[] }\n | { op: 'watch'; paths: string[]; recursive: boolean }\n | { op: 'unwatch'; watch: number }\n | { op: 'mount'; spec: MountSpec }\n | { op: 'umount'; target: string }\n | { op: 'mounts' }\n | { op: 'dial'; port: number; host?: string }\n | { op: 'serve-cdp'; runtime?: string }\n | { op: 'unserve-cdp'; registration: number }\n | { op: 'detach'; kill?: boolean };\n\nexport type ClientRequest = ClientCall & { id: number };\n\nexport interface ClientReply {\n id: number;\n result?: unknown;\n error?: string;\n code?: string;\n fd?: 1 | 2;\n bytes?: Uint8Array;\n started?: number;\n}\n\nexport type ClientMessage = { hello: ClientHello } | ClientRequest | TransportCall;\n\nexport interface WatchEvent {\n watch: number;\n change: WatchChange;\n}\n\nexport type KernelMessage = { hello: KernelHello } | ClientReply | WatchEvent | TransportReply;\n\nexport interface ProcessEntry {\n pid: number;\n ppid: number;\n pgid: number;\n sid: number;\n argv: string[];\n tty: string | null;\n started: number;\n state: 'S' | 'Z';\n memory: number;\n}\n\nexport interface MessagePortLike {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(type: 'message' | 'close', handler: (event: MessageEvent) => void): void;\n start?(): void;\n close?(): void;\n}\n\nexport interface LockManagerLike {\n request(name: string, callback: () => Promise<unknown> | unknown): Promise<unknown>;\n}\n\nexport function locksOf(scope: object = globalThis): LockManagerLike | undefined {\n const locks = (scope as { navigator?: { locks?: LockManagerLike } }).navigator?.locks;\n return typeof locks?.request === 'function' ? locks : undefined;\n}\n\nexport function versionError(theirs: unknown): string | undefined {\n const major = Array.isArray(theirs) ? theirs[0] : undefined;\n if (major === PROTOCOL[0]) return undefined;\n const named = typeof major === 'number' ? `${major}.x` : 'without a version';\n return `slicc-kernel client protocol ${named} is not supported: this side speaks ${PROTOCOL.join('.')}`;\n}\n", "import { type CdpOpen, serveCdpPort } from '../cdp/port.ts';\nimport type { CdpHook } from '../cdp/types.ts';\nimport type { FsStat } from '../fs/types.ts';\nimport type { WatchChange } from '../fs/watch.ts';\nimport { RemoteTransport, type TransportReply } from '../kernel/net/remote-transport.ts';\nimport type {\n RealmTransport,\n RealmTransportRequest,\n RealmTransportResponse,\n} from '../kernel/net/transport.ts';\nimport type { MountEntry, MountSpec } from '../mount/mount-fs.ts';\nimport { type DialledSocket, type DialOptions, dialStream } from './dial-stream.ts';\nimport { type LoopbackFetchOptions, loopbackFetch } from './loopback-fetch.ts';\nimport {\n type ClientCall,\n type ClientReply,\n type FsMethod,\n type KernelHello,\n type LockManagerLike,\n locksOf,\n type MessagePortLike,\n PROTOCOL,\n type ProcessEntry,\n type TerminalAction,\n versionError,\n} from './protocol.ts';\n\nexport type { WatchChange } from '../fs/watch.ts';\nexport type { ProcessEntry } from './protocol.ts';\n\nexport class KernelGoneError extends Error {\n constructor(message = 'the slicc-kernel this client was attached to is gone') {\n super(message);\n this.name = 'KernelGoneError';\n }\n}\n\nexport class KernelCallError extends Error {\n readonly code: string | undefined;\n\n constructor(message: string, code?: string) {\n super(message);\n this.name = 'KernelCallError';\n this.code = code;\n }\n}\n\nexport interface AttachOptions {\n timeoutMs?: number;\n locks?: LockManagerLike | null;\n}\n\nexport interface SpawnOptions {\n cwd?: string;\n env?: Record<string, string>;\n stdin?: string | Uint8Array;\n group?: 'new';\n pgid?: number;\n onStdout?: (bytes: Uint8Array) => void;\n onStderr?: (bytes: Uint8Array) => void;\n}\n\nexport interface SpawnedProcess {\n readonly pid: number;\n readonly pgid: number;\n readonly exited: Promise<number>;\n signal(name?: string): Promise<void>;\n}\n\nexport interface ClientRunOptions {\n cwd?: string;\n env?: Record<string, string>;\n stdin?: string | Uint8Array;\n onStdout?: (text: string) => void;\n onStderr?: (text: string) => void;\n}\n\nexport interface ClientRunResult {\n pid: number;\n status: number;\n stdout: string;\n stderr: string;\n}\n\nexport interface ClientTerminalOptions {\n cwd?: string;\n env?: Record<string, string>;\n cols?: number;\n rows?: number;\n onData?: (bytes: Uint8Array) => void;\n}\n\nexport interface ClientTerminal {\n readonly pid: number;\n readonly exited: Promise<number>;\n onData: ((bytes: Uint8Array) => void) | null;\n write(data: string | Uint8Array): void;\n resize(cols: number, rows: number): void;\n signal(name: string): void;\n close(): void;\n}\n\nexport interface ClientFs {\n readFile(path: string): Promise<Uint8Array>;\n readText(path: string): Promise<string>;\n writeFile(path: string, data: string | Uint8Array): Promise<void>;\n stat(path: string): Promise<FsStat>;\n lstat(path: string): Promise<FsStat>;\n readdir(path: string): Promise<string[]>;\n mkdir(path: string): Promise<void>;\n rm(path: string, options?: { force?: boolean }): Promise<void>;\n rename(from: string, to: string): Promise<void>;\n realpath(path: string): Promise<string>;\n symlink(target: string, path: string): Promise<void>;\n readlink(path: string): Promise<string>;\n exists(path: string): Promise<boolean>;\n watch(\n paths: string[],\n options: { recursive?: boolean },\n onChange: (change: WatchChange) => void\n ): Promise<{ close(): void }>;\n}\n\nexport type ClientFetchRequest = Omit<RealmTransportRequest, 'signal'> & { signal?: AbortSignal };\n\nexport interface KernelClient {\n readonly protocol: readonly [number, number];\n readonly transport: RealmTransport;\n readonly closed: Promise<Error>;\n spawn(argv: string[], options?: SpawnOptions): Promise<SpawnedProcess>;\n run(argv: string[], options?: ClientRunOptions): Promise<ClientRunResult>;\n openTerminal(argv: string[], options?: ClientTerminalOptions): Promise<ClientTerminal>;\n ps(): Promise<ProcessEntry[]>;\n mount(spec: MountSpec): Promise<MountEntry>;\n umount(target: string): Promise<void>;\n mounts(): Promise<MountEntry[]>;\n kill(pid: number, signal?: string): Promise<void>;\n fetch(request: ClientFetchRequest): Promise<RealmTransportResponse>;\n dial(options: DialOptions): Promise<DialledSocket>;\n loopbackFetch(input: RequestInfo | URL, options: LoopbackFetchOptions): Promise<Response>;\n readonly fs: ClientFs;\n serveCdp(hook: CdpHook, options?: { runtime?: string }): Promise<{ close(): void }>;\n close(options?: { kill?: boolean }): Promise<void>;\n}\n\ninterface Pending {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n output?: (fd: 1 | 2, bytes: Uint8Array) => void;\n started?: (pid: number) => void;\n}\n\ntype Handlers = Omit<Pending, 'resolve' | 'reject'>;\n\nconst encoder = new TextEncoder();\nconst bytesOf = (data: string | Uint8Array) =>\n typeof data === 'string' ? encoder.encode(data) : data;\n\nconst UNAVAILABLE = {\n manualRedirects: false,\n encodedBodies: false,\n maxRequestBody: 0,\n unavailable: true,\n} as const;\n\nasync function holdLock(\n locks: LockManagerLike | undefined\n): Promise<{ name?: string; release(): void }> {\n if (!locks) return { release() {} };\n const name = `slicc-kernel-client:${crypto.randomUUID()}`;\n const granted = Promise.withResolvers<void>();\n const held = Promise.withResolvers<void>();\n void locks.request(name, () => {\n granted.resolve();\n return held.promise;\n });\n await granted.promise;\n return { name, release: () => held.resolve() };\n}\n\nfunction concat(chunks: Uint8Array[]): Uint8Array {\n const out = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));\n let at = 0;\n for (const chunk of chunks) {\n out.set(chunk, at);\n at += chunk.length;\n }\n return out;\n}\n\nclass Channel {\n readonly greeted = Promise.withResolvers<KernelHello>();\n readonly ended = Promise.withResolvers<Error>();\n remote: RemoteTransport | undefined;\n failure: Error | undefined;\n private readonly pending = new Map<number, Pending>();\n readonly watching = new Map<number, (change: WatchChange) => void>();\n readonly cdpHooks = new Map<number, CdpHook>();\n private readonly cdpOpen = new Set<() => void>();\n protocol: readonly [number, number] = PROTOCOL;\n private nextId = 0;\n private readonly port: MessagePortLike;\n private readonly release: () => void;\n\n constructor(port: MessagePortLike, release: () => void) {\n this.port = port;\n this.release = release;\n this.greeted.promise.catch(() => undefined);\n port.addEventListener('message', (event) => this.receive(event.data));\n port.addEventListener('close', () => this.end(new KernelGoneError()));\n port.start?.();\n }\n\n post(message: object): void {\n if (!this.failure) this.port.postMessage(message);\n }\n\n end(error: Error): void {\n if (this.failure) return;\n this.failure = error;\n this.greeted.reject(error);\n this.remote?.fail(error);\n for (const close of [...this.cdpOpen]) close();\n this.cdpOpen.clear();\n for (const call of this.pending.values()) call.reject(error);\n this.pending.clear();\n this.release();\n this.port.close?.();\n this.ended.resolve(error);\n }\n\n request(call: ClientCall, handlers: Handlers = {}): { id: number; done: Promise<unknown> } {\n const id = ++this.nextId;\n const { failure } = this;\n if (failure) return { id, done: Promise.reject(failure) };\n const done = new Promise((resolve, reject) => {\n this.pending.set(id, { resolve, reject, ...handlers });\n this.port.postMessage({ ...call, id });\n });\n return { id, done };\n }\n\n call(call: ClientCall): Promise<unknown> {\n return this.request(call).done;\n }\n\n private receive(data: {\n hello?: KernelHello;\n net?: unknown;\n bye?: string;\n watch?: number;\n change?: WatchChange;\n cdpOpen?: CdpOpen['cdpOpen'];\n port?: CdpOpen['port'];\n }): void {\n if (data.cdpOpen && data.port) {\n this.openCdp(data as CdpOpen);\n return;\n }\n if (data.watch !== undefined) {\n this.watching.get(data.watch)?.(data.change as WatchChange);\n return;\n }\n if (data.bye !== undefined) {\n this.end(new KernelGoneError(data.bye));\n return;\n }\n if (data.hello) {\n this.greeted.resolve(data.hello);\n return;\n }\n if (data.net !== undefined) {\n this.remote?.receive(data as TransportReply);\n return;\n }\n const reply = data as ClientReply;\n const call = this.pending.get(reply.id);\n if (!call) return;\n if (reply.fd !== undefined) {\n call.output?.(reply.fd, reply.bytes as Uint8Array);\n return;\n }\n if (reply.started !== undefined) {\n call.started?.(reply.started);\n return;\n }\n this.pending.delete(reply.id);\n if (reply.error !== undefined) call.reject(new KernelCallError(reply.error, reply.code));\n else call.resolve(reply.result);\n }\n\n private openCdp({ cdpOpen: { registration, ...request }, port }: CdpOpen): void {\n const hook = registration === undefined ? undefined : this.cdpHooks.get(registration);\n if (!hook || this.failure) {\n port.postMessage({ error: 'this client no longer serves CDP' });\n port.close();\n return;\n }\n const close = serveCdpPort(port, hook, request, () => this.cdpOpen.delete(close));\n this.cdpOpen.add(close);\n }\n}\n\nasync function serveCdpOn(\n channel: Channel,\n hook: CdpHook,\n options: { runtime?: string }\n): Promise<{ close(): void }> {\n await since(channel, 5, 'CDP serving', async () => {});\n const req = {\n op: 'serve-cdp' as const,\n ...(options.runtime ? { runtime: options.runtime } : {}),\n };\n const { id, done } = channel.request(req);\n channel.cdpHooks.set(id, hook);\n try {\n await done;\n } catch (error) {\n channel.cdpHooks.delete(id);\n throw error;\n }\n return {\n close: () => {\n if (!channel.cdpHooks.delete(id)) return;\n void channel.call({ op: 'unserve-cdp', registration: id }).catch(() => undefined);\n },\n };\n}\n\nasync function dialOn(channel: Channel, { port, host }: DialOptions): Promise<DialledSocket> {\n const req = { op: 'dial' as const, port, ...(host ? { host } : {}) };\n return dialStream((await since(channel, 4, 'dial', () => channel.call(req))) as MessagePort);\n}\n\nasync function spawnOn(\n channel: Channel,\n argv: string[],\n opts: SpawnOptions\n): Promise<SpawnedProcess> {\n const { onStdout, onStderr, stdin, group: _, ...rest } = opts;\n if (opts.pgid !== undefined)\n await since(channel, 3, 'spawn into a process group', async () => {});\n const started = Promise.withResolvers<number>();\n const options = { ...rest, ...(stdin !== undefined ? { stdin: bytesOf(stdin) } : {}) };\n const { done } = channel.request(\n { op: 'spawn', argv, options },\n {\n output: (fd, bytes) => (fd === 1 ? onStdout : onStderr)?.(bytes),\n started: started.resolve,\n }\n );\n done.catch(started.reject);\n const pid = await started.promise;\n const pgid = opts.pgid ?? pid;\n return {\n pid,\n pgid,\n exited: done as Promise<number>,\n signal: async (name = 'SIGTERM') =>\n void (await channel.call({ op: 'kill', pid: -pgid, signal: name })),\n };\n}\n\nasync function runOn(\n channel: Channel,\n argv: string[],\n opts: ClientRunOptions\n): Promise<ClientRunResult> {\n const { onStdout, onStderr, ...rest } = opts;\n const chunks: Record<1 | 2, Uint8Array[]> = { 1: [], 2: [] };\n const live = { 1: new TextDecoder(), 2: new TextDecoder() };\n const tee = (fd: 1 | 2, on?: (text: string) => void) => (bytes: Uint8Array) => {\n chunks[fd].push(bytes);\n on?.(live[fd].decode(bytes, { stream: true }));\n };\n const child = await spawnOn(channel, argv, {\n ...rest,\n onStdout: tee(1, onStdout),\n onStderr: tee(2, onStderr),\n });\n const status = await child.exited;\n const text = (fd: 1 | 2) => new TextDecoder().decode(concat(chunks[fd]));\n return { pid: child.pid, status, stdout: text(1), stderr: text(2) };\n}\n\nasync function terminalOn(\n channel: Channel,\n argv: string[],\n opts: ClientTerminalOptions\n): Promise<ClientTerminal> {\n const { onData, ...options } = opts;\n let listener = onData ?? null;\n const backlog: Uint8Array[] = [];\n const started = Promise.withResolvers<number>();\n const { id, done } = channel.request(\n { op: 'open-terminal', argv, options },\n {\n output: (_fd, bytes) => (listener ? listener(bytes) : backlog.push(bytes)),\n started: started.resolve,\n }\n );\n done.catch(started.reject);\n const pid = await started.promise;\n const send = (action: TerminalAction) =>\n void channel.call({ op: 'terminal', terminal: id, ...action }).catch(() => undefined);\n return {\n pid,\n exited: done as Promise<number>,\n get onData() {\n return listener;\n },\n set onData(next) {\n listener = next;\n if (next) for (const bytes of backlog.splice(0)) next(bytes);\n },\n write: (data) => send({ action: 'write', bytes: bytesOf(data) }),\n resize: (cols, rows) => send({ action: 'resize', cols, rows }),\n signal: (name) => send({ action: 'signal', signal: name }),\n close: () => send({ action: 'close' }),\n };\n}\n\nfunction fsOn(channel: Channel): ClientFs {\n const fs = (method: FsMethod, ...args: unknown[]) => channel.call({ op: 'fs', method, args });\n const done = async (method: FsMethod, ...args: unknown[]) => void (await fs(method, ...args));\n return {\n readFile: (path) => fs('readFile', path) as Promise<Uint8Array>,\n readText: async (path) => new TextDecoder().decode((await fs('readFile', path)) as Uint8Array),\n writeFile: (path, data) => done('writeFile', path, data),\n stat: (path) => fs('stat', path) as Promise<FsStat>,\n lstat: (path) => fs('lstat', path) as Promise<FsStat>,\n readdir: (path) => fs('readdir', path) as Promise<string[]>,\n mkdir: (path) => done('mkdir', path),\n rm: (path, o = {}) => done('rm', path, o.force === true),\n rename: (from, to) => done('rename', from, to),\n realpath: (path) => fs('realpath', path) as Promise<string>,\n symlink: (target, path) => done('symlink', target, path),\n readlink: (path) => fs('readlink', path) as Promise<string>,\n exists: (path) => fs('exists', path) as Promise<boolean>,\n async watch(paths, options, onChange) {\n if (channel.protocol[1] < 1) {\n throw new KernelCallError(\n `this kernel speaks protocol ${channel.protocol.join('.')}, which has no watch`,\n 'ENOSYS'\n );\n }\n const { id, done } = channel.request({\n op: 'watch',\n paths,\n recursive: options.recursive === true,\n });\n channel.watching.set(id, onChange);\n try {\n await done;\n } catch (error) {\n channel.watching.delete(id);\n throw error;\n }\n return {\n close: () => {\n if (!channel.watching.delete(id)) return;\n void channel.call({ op: 'unwatch', watch: id }).catch(() => undefined);\n },\n };\n },\n };\n}\n\nfunction since(channel: Channel, minor: number, what: string, run: () => Promise<unknown>) {\n if (channel.protocol[1] < minor) {\n return Promise.reject(\n new KernelCallError(\n `this kernel speaks protocol ${channel.protocol.join('.')}, which has no ${what}`,\n 'ENOSYS'\n )\n );\n }\n return run();\n}\n\nasync function greet(\n channel: Channel,\n lock: string | undefined,\n timeoutMs: number\n): Promise<KernelHello> {\n channel.post({ hello: { protocol: PROTOCOL, ...(lock ? { lock } : {}) } });\n const timer = setTimeout(\n () =>\n channel.end(\n new KernelGoneError(`no slicc-kernel answered on this port within ${timeoutMs} ms`)\n ),\n timeoutMs\n );\n let hello: KernelHello;\n try {\n hello = await channel.greeted.promise;\n } finally {\n clearTimeout(timer);\n }\n const refused = hello.error ?? versionError(hello.protocol);\n if (refused) {\n const error = new Error(refused);\n channel.end(error);\n throw error;\n }\n return hello;\n}\n\nexport async function attachKernel(\n port: MessagePortLike,\n options: AttachOptions = {}\n): Promise<KernelClient> {\n const locks = options.locks === null ? undefined : (options.locks ?? locksOf());\n const own = await holdLock(locks);\n const channel = new Channel(port, own.release);\n const hello = await greet(channel, own.name, options.timeoutMs ?? 10_000);\n channel.protocol = hello.protocol;\n if (hello.lock && locks) void locks.request(hello.lock, () => channel.end(new KernelGoneError()));\n const transport = new RemoteTransport(\n { postMessage: (call) => channel.post(call) },\n hello.traits ?? UNAVAILABLE\n );\n channel.remote = transport;\n return {\n protocol: hello.protocol,\n transport,\n closed: channel.ended.promise,\n spawn: (argv, opts = {}) => spawnOn(channel, argv, opts),\n run: (argv, opts = {}) => runOn(channel, argv, opts),\n openTerminal: (argv, opts = {}) => terminalOn(channel, argv, opts),\n ps: () => channel.call({ op: 'ps' }) as Promise<ProcessEntry[]>,\n mount: (spec) =>\n since(channel, 2, 'mount', () => channel.call({ op: 'mount', spec })) as Promise<MountEntry>,\n umount: async (target) =>\n void (await since(channel, 2, 'umount', () => channel.call({ op: 'umount', target }))),\n mounts: () =>\n since(channel, 2, 'mounts', () => channel.call({ op: 'mounts' })) as Promise<MountEntry[]>,\n kill: async (pid, signal = 'SIGTERM') => void (await channel.call({ op: 'kill', pid, signal })),\n fetch: (req) => {\n const { signal, ...rest } = req;\n return transport.fetch({ ...rest, signal: signal ?? new AbortController().signal });\n },\n dial: (options) => dialOn(channel, options),\n loopbackFetch: (input, options) => loopbackFetch((o) => dialOn(channel, o), input, options),\n fs: fsOn(channel),\n serveCdp: (hook, options = {}) => serveCdpOn(channel, hook, options),\n async close(o = {}) {\n if (channel.failure) return;\n await channel\n .call({ op: 'detach', ...(o.kill ? { kill: true } : {}) })\n .catch(() => undefined);\n channel.end(new KernelGoneError('this client is closed'));\n },\n };\n}\n", "export const DRIVER_PROTOCOL: readonly [number, number] = [1, 1];\n\nexport interface DriverCapabilities {\n readonly?: boolean;\n symlinks?: boolean;\n chmod?: boolean;\n ranges?: boolean;\n listingStats?: boolean;\n caseInsensitive?: boolean;\n normalization?: 'none' | 'nfc' | 'nfd-insensitive';\n maxIo?: number;\n maxFile?: number;\n attrTtl?: number;\n entryTtl?: number;\n}\n\nexport interface DriverAttr {\n kind: 'file' | 'directory' | 'symlink';\n size: number;\n mtime: number;\n mode?: number;\n ino?: number;\n etag?: string;\n}\n\nexport interface DriverEntry {\n name: string;\n kind: DriverAttr['kind'];\n attr?: DriverAttr;\n}\n\nexport interface DriverStatfs {\n bsize: number;\n blocks: number;\n bfree: number;\n}\n\nexport interface MountRequestInfo {\n source: string;\n options: Record<string, string>;\n}\n\nexport type DriverCall =\n | { op: 'getattr'; path: string }\n | { op: 'readdir'; path: string }\n | {\n op: 'open';\n path: string;\n write: boolean;\n create: boolean;\n truncate: boolean;\n exclusive: boolean;\n ifMatch?: string;\n }\n | { op: 'read'; fh: number; offset: number; size: number }\n | { op: 'write'; fh: number; offset: number; bytes: Uint8Array }\n | { op: 'release'; fh: number }\n | { op: 'mkdir'; path: string }\n | { op: 'rmdir'; path: string }\n | { op: 'unlink'; path: string }\n | { op: 'rename'; from: string; to: string }\n | { op: 'symlink'; target: string; path: string }\n | { op: 'readlink'; path: string }\n | { op: 'setattr'; path: string; mode?: number; mtime?: number; size?: number }\n | { op: 'statfs' };\n\nexport type DriverRequest = DriverCall & { id: number };\n\nexport interface DriverReply {\n id: number;\n result?: unknown;\n errno?: string;\n message?: string;\n}\n\nexport interface KernelDriverHello {\n protocol: readonly [number, number];\n mount: MountRequestInfo;\n}\n\nexport interface DriverHello {\n protocol: readonly [number, number];\n capabilities?: DriverCapabilities;\n error?: string;\n errno?: string;\n}\n\nexport type KernelToDriver = { hello: KernelDriverHello } | DriverRequest;\n\nexport type DriverToKernel = { hello: DriverHello } | DriverReply | { invalidate: string[] | true };\n\nexport function driverVersionError(theirs: unknown): string | undefined {\n const major = Array.isArray(theirs) ? theirs[0] : undefined;\n if (major === DRIVER_PROTOCOL[0]) return undefined;\n const named = typeof major === 'number' ? `${major}.x` : 'without a version';\n return `slicc-kernel driver protocol ${named} is not supported: this side speaks ${DRIVER_PROTOCOL.join('.')}`;\n}\n", "import {\n DRIVER_PROTOCOL,\n type DriverAttr,\n type DriverCapabilities,\n type DriverEntry,\n type DriverRequest,\n type DriverStatfs,\n driverVersionError,\n type KernelDriverHello,\n type MountRequestInfo,\n} from './protocol.ts';\n\nexport type {\n DriverAttr,\n DriverCapabilities,\n DriverEntry,\n DriverStatfs,\n MountRequestInfo,\n} from './protocol.ts';\n\nexport interface OpenFlags {\n write: boolean;\n create: boolean;\n truncate: boolean;\n exclusive: boolean;\n ifMatch?: string;\n}\n\nexport interface FilesystemHandlers {\n mount?(\n info: MountRequestInfo\n ): Promise<DriverCapabilities | undefined> | DriverCapabilities | undefined;\n getattr(path: string): Promise<DriverAttr>;\n readdir(path: string): Promise<DriverEntry[]>;\n open(path: string, flags: OpenFlags): Promise<number>;\n read(fh: number, offset: number, size: number): Promise<Uint8Array>;\n write(fh: number, offset: number, bytes: Uint8Array): Promise<void>;\n release(fh: number): Promise<void>;\n mkdir(path: string): Promise<void>;\n rmdir(path: string): Promise<void>;\n unlink(path: string): Promise<void>;\n rename(from: string, to: string): Promise<void>;\n symlink?(target: string, path: string): Promise<void>;\n readlink?(path: string): Promise<string>;\n setattr?(path: string, attr: { mode?: number; mtime?: number; size?: number }): Promise<void>;\n statfs?(): Promise<DriverStatfs>;\n}\n\nexport interface DriverPort {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(type: 'message', handler: (event: MessageEvent) => void): void;\n start?(): void;\n}\n\nexport interface ServedFilesystem {\n invalidate(paths: string[] | true): void;\n}\n\nexport function fsError(code: string, message = code): Error & { code: string } {\n return Object.assign(new Error(message), { code });\n}\n\nasync function dispatch(h: FilesystemHandlers, req: DriverRequest): Promise<unknown> {\n switch (req.op) {\n case 'getattr':\n return h.getattr(req.path);\n case 'readdir':\n return h.readdir(req.path);\n case 'open':\n return h.open(req.path, {\n write: req.write,\n create: req.create,\n truncate: req.truncate,\n exclusive: req.exclusive,\n ...(req.ifMatch !== undefined ? { ifMatch: req.ifMatch } : {}),\n });\n case 'read':\n return h.read(req.fh, req.offset, req.size);\n case 'write':\n return h.write(req.fh, req.offset, req.bytes);\n case 'release':\n return h.release(req.fh);\n case 'mkdir':\n return h.mkdir(req.path);\n case 'rmdir':\n return h.rmdir(req.path);\n case 'unlink':\n return h.unlink(req.path);\n case 'rename':\n return h.rename(req.from, req.to);\n case 'symlink':\n if (!h.symlink) throw fsError('EPERM', 'this file system has no symlinks');\n return h.symlink(req.target, req.path);\n case 'readlink':\n if (!h.readlink) throw fsError('EINVAL', `${req.path} is not a symlink`);\n return h.readlink(req.path);\n case 'setattr':\n return h.setattr?.(req.path, {\n ...(req.mode !== undefined ? { mode: req.mode } : {}),\n ...(req.mtime !== undefined ? { mtime: req.mtime } : {}),\n ...(req.size !== undefined ? { size: req.size } : {}),\n });\n case 'statfs':\n return h.statfs?.() ?? null;\n default:\n throw fsError('ENOSYS', `unknown driver op ${(req as { op: string }).op}`);\n }\n}\n\nexport function serveFilesystem(\n port: DriverPort,\n handlers: FilesystemHandlers,\n capabilities: DriverCapabilities = {},\n options: { owned?: boolean } = {}\n): ServedFilesystem {\n let open = false;\n const reply = (message: object, transfer: Transferable[] = []) =>\n port.postMessage(message, transfer);\n async function hello(message: KernelDriverHello): Promise<void> {\n const refused = driverVersionError(message.protocol);\n if (refused) {\n reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused, errno: 'EPROTO' } });\n return;\n }\n try {\n const refined = await handlers.mount?.(message.mount);\n open = true;\n reply({\n hello: { protocol: DRIVER_PROTOCOL, capabilities: { ...capabilities, ...refined } },\n });\n } catch (err) {\n const code = (err as { code?: unknown })?.code;\n reply({\n hello: {\n protocol: DRIVER_PROTOCOL,\n error: err instanceof Error ? err.message : String(err),\n errno: typeof code === 'string' ? code : 'EIO',\n },\n });\n }\n }\n port.addEventListener('message', (event) => {\n const data = event.data as { hello?: KernelDriverHello } & DriverRequest;\n if (data.hello) {\n void hello(data.hello);\n return;\n }\n if (!open) return;\n dispatch(handlers, data).then(\n (result) => {\n if (result instanceof Uint8Array) {\n const whole = result.byteOffset === 0 && result.byteLength === result.buffer.byteLength;\n const bytes = options.owned && whole ? result : result.slice();\n reply({ id: data.id, result: bytes }, [bytes.buffer]);\n } else reply({ id: data.id, result: result ?? null });\n },\n (err: unknown) => {\n const code = (err as { code?: unknown })?.code;\n reply({\n id: data.id,\n errno: typeof code === 'string' ? code : 'EIO',\n message: err instanceof Error ? err.message : String(err),\n });\n }\n );\n });\n port.start?.();\n return { invalidate: (paths) => reply({ invalidate: paths }) };\n}\n", "import { attachKernel } from '../client/attach.ts';\nimport type { MessagePortLike } from '../client/protocol.ts';\nimport type { RealmTransportRequest, RealmTransportResponse } from '../kernel/net/transport.ts';\nimport { type FilesystemHandlers, serveFilesystem } from './driver.ts';\nimport { DRIVER_PROTOCOL, type DriverCapabilities } from './protocol.ts';\n\nexport interface DriverStart {\n code: string;\n driver: MessagePortLike;\n client: MessagePortLike;\n}\n\nexport interface DriverEnv {\n fetch(\n request: Omit<RealmTransportRequest, 'signal'> & { signal?: AbortSignal }\n ): Promise<RealmTransportResponse>;\n}\n\nexport type DriverFactory = (\n env: DriverEnv\n) =>\n | Promise<{ handlers: FilesystemHandlers; capabilities?: DriverCapabilities }>\n | { handlers: FilesystemHandlers; capabilities?: DriverCapabilities };\n\nexport async function hostDriver(\n start: DriverStart,\n load: (code: string) => Promise<{ default?: unknown }>\n): Promise<void> {\n try {\n const factory = (await load(start.code)).default;\n if (typeof factory !== 'function') {\n throw new Error('a slicc file system module must export a default function');\n }\n const client = await attachKernel(start.client, { locks: null });\n const made = await (factory as DriverFactory)({ fetch: (request) => client.fetch(request) });\n serveFilesystem(start.driver, made.handlers, made.capabilities ?? {});\n } catch (err) {\n start.driver.postMessage({\n hello: {\n protocol: DRIVER_PROTOCOL,\n error: `the driver did not start: ${err instanceof Error ? err.message : String(err)}`,\n errno: 'EIO',\n },\n });\n }\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["fail", "encoder", "done", "options"]
|
|
7
7
|
}
|
|
@@ -2955,6 +2955,7 @@ function createSocketKernel(deps) {
|
|
|
2955
2955
|
name: (fd, peer) => guard(() => json({ op: "sock-name", fd: kfd(fd), peer })),
|
|
2956
2956
|
getopt: (fd, level, name) => guard(() => ({ value: json({ op: "sock-getopt", fd: kfd(fd), level, name }) })),
|
|
2957
2957
|
setopt: (fd, level, name, value2) => guard(() => done({ op: "sock-setopt", fd: kfd(fd), level, name, value: value2 })),
|
|
2958
|
+
resolve: (name, family = 4) => guard(() => json({ op: "sock-resolve", name, family })),
|
|
2958
2959
|
send: (fd, bytes, flags) => guard(() => {
|
|
2959
2960
|
const stream = socketAt(fd);
|
|
2960
2961
|
const nonblock = flags.dontwait === true || (stream.flags & O_NONBLOCK) !== 0;
|
|
@@ -6341,6 +6342,67 @@ var init_wasix_process = __esm({
|
|
|
6341
6342
|
}
|
|
6342
6343
|
});
|
|
6343
6344
|
|
|
6345
|
+
// src/kernel/net/routes.ts
|
|
6346
|
+
function ipv4Bytes(text) {
|
|
6347
|
+
const parts = text.split(".");
|
|
6348
|
+
if (parts.length !== 4 || !parts.every((p) => /^\d{1,3}$/.test(p))) return void 0;
|
|
6349
|
+
const bytes = parts.map(Number);
|
|
6350
|
+
return bytes.every((b) => b <= 255) ? bytes : void 0;
|
|
6351
|
+
}
|
|
6352
|
+
function ipv6Groups(text) {
|
|
6353
|
+
if (!text.includes(":")) return void 0;
|
|
6354
|
+
let canonical;
|
|
6355
|
+
try {
|
|
6356
|
+
canonical = new URL(`http://[${text}]/`).hostname.slice(1, -1);
|
|
6357
|
+
} catch {
|
|
6358
|
+
return void 0;
|
|
6359
|
+
}
|
|
6360
|
+
const [head, tail] = canonical.split("::");
|
|
6361
|
+
const part = (s) => s ? s.split(":").map((h) => Number.parseInt(h, 16)) : [];
|
|
6362
|
+
const front = part(head);
|
|
6363
|
+
const back = part(tail);
|
|
6364
|
+
return tail === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
|
|
6365
|
+
}
|
|
6366
|
+
function toBits(values, width) {
|
|
6367
|
+
return values.reduce((acc, v) => acc << BigInt(width) | BigInt(v), 0n);
|
|
6368
|
+
}
|
|
6369
|
+
function addressBits(host) {
|
|
6370
|
+
const v4 = ipv4Bytes(host);
|
|
6371
|
+
if (v4) return { v6: false, bits: toBits(v4, 8) };
|
|
6372
|
+
const v6 = ipv6Groups(host);
|
|
6373
|
+
return v6 ? { v6: true, bits: toBits(v6, 16) } : void 0;
|
|
6374
|
+
}
|
|
6375
|
+
function parsePrefix(text) {
|
|
6376
|
+
const parts = text.trim().split("/");
|
|
6377
|
+
if (parts.length > 2) return void 0;
|
|
6378
|
+
const [address = "", size] = parts;
|
|
6379
|
+
const parsed = addressBits(address);
|
|
6380
|
+
if (!parsed) return void 0;
|
|
6381
|
+
const max = parsed.v6 ? 128 : 32;
|
|
6382
|
+
const length = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
|
|
6383
|
+
if (length < 0 || length > max) return void 0;
|
|
6384
|
+
return { ...parsed, length };
|
|
6385
|
+
}
|
|
6386
|
+
var HOST_ADDRESS, RESERVED;
|
|
6387
|
+
var init_routes = __esm({
|
|
6388
|
+
"src/kernel/net/routes.ts"() {
|
|
6389
|
+
"use strict";
|
|
6390
|
+
init_loopback_names();
|
|
6391
|
+
HOST_ADDRESS = HOST_LOOPBACK_ADDRESS.join(".");
|
|
6392
|
+
RESERVED = [
|
|
6393
|
+
"0.0.0.0/8",
|
|
6394
|
+
"127.0.0.0/8",
|
|
6395
|
+
"169.254.0.0/16",
|
|
6396
|
+
"224.0.0.0/4",
|
|
6397
|
+
"240.0.0.0/4",
|
|
6398
|
+
"::/96",
|
|
6399
|
+
"::ffff:0:0/96",
|
|
6400
|
+
"fe80::/10",
|
|
6401
|
+
"ff00::/8"
|
|
6402
|
+
].map((p) => parsePrefix(p));
|
|
6403
|
+
}
|
|
6404
|
+
});
|
|
6405
|
+
|
|
6344
6406
|
// src/process/wasi/wasix-sockets.ts
|
|
6345
6407
|
function readAddr(view, ptr) {
|
|
6346
6408
|
const tag = view.getUint8(ptr);
|
|
@@ -6382,25 +6444,27 @@ function writeAddr(view, ptr, addr) {
|
|
|
6382
6444
|
);
|
|
6383
6445
|
return;
|
|
6384
6446
|
}
|
|
6385
|
-
view.setUint8(ptr, INET4);
|
|
6386
6447
|
view.setUint16(ptr + 2, addr.port, true);
|
|
6448
|
+
const groups = ipv4(addr.host) ? void 0 : ipv6Groups(addr.host);
|
|
6449
|
+
if (groups) {
|
|
6450
|
+
view.setUint8(ptr, INET6);
|
|
6451
|
+
groups.forEach((g, i) => void view.setUint16(ptr + 4 + i * 2, g));
|
|
6452
|
+
return;
|
|
6453
|
+
}
|
|
6454
|
+
view.setUint8(ptr, INET4);
|
|
6387
6455
|
const octets = ipv4(addr.host) ?? [127, 0, 0, 1];
|
|
6388
6456
|
octets.forEach((b, i) => void view.setUint8(ptr + 4 + i, b));
|
|
6389
6457
|
}
|
|
6458
|
+
function ipBytes(host) {
|
|
6459
|
+
const groups = ipv4(host) ? void 0 : ipv6Groups(host);
|
|
6460
|
+
return groups ? groups.flatMap((g) => [g >> 8, g & 255]) : ipv4(host);
|
|
6461
|
+
}
|
|
6390
6462
|
function ipv4(host) {
|
|
6391
6463
|
const parts = host.split(".");
|
|
6392
6464
|
if (parts.length !== 4) return void 0;
|
|
6393
6465
|
const bytes = parts.map((p) => /^\d{1,3}$/.test(p) ? Number(p) : Number.NaN);
|
|
6394
6466
|
return bytes.every((b) => b >= 0 && b <= 255) ? bytes : void 0;
|
|
6395
6467
|
}
|
|
6396
|
-
function resolveName(name) {
|
|
6397
|
-
if (name === "localhost" || name === "localhost.localdomain" || name === "ip6-localhost") {
|
|
6398
|
-
return [127, 0, 0, 1];
|
|
6399
|
-
}
|
|
6400
|
-
if (name === "::1") return [127, 0, 0, 1];
|
|
6401
|
-
if (name.toLowerCase() === HOST_LOOPBACK) return [...HOST_LOOPBACK_ADDRESS];
|
|
6402
|
-
return ipv4(name);
|
|
6403
|
-
}
|
|
6404
6468
|
function wasixSocketImports(host, preview1) {
|
|
6405
6469
|
const { mem, fds, o } = host;
|
|
6406
6470
|
const call2 = (req) => o.kernel.call(req);
|
|
@@ -6492,14 +6556,19 @@ function wasixSocketImports(host, preview1) {
|
|
|
6492
6556
|
sock_set_opt_time: (fd) => void socket(fd),
|
|
6493
6557
|
sock_send_file: () => E.NOSYS,
|
|
6494
6558
|
resolve: (name, len, _port, addrs, naddrs, out) => {
|
|
6495
|
-
const
|
|
6496
|
-
|
|
6559
|
+
const found = call2({ op: "sock-resolve", name: mem.string(name, len), family: 0 });
|
|
6560
|
+
const ips = found.map(ipBytes).filter((ip) => ip !== void 0);
|
|
6561
|
+
if (ips.length === 0) throw new WasiError("ENOENT");
|
|
6497
6562
|
if (naddrs < 1) throw new WasiError("EINVAL");
|
|
6498
6563
|
const view = mem.view();
|
|
6499
|
-
|
|
6500
|
-
view.
|
|
6501
|
-
|
|
6502
|
-
|
|
6564
|
+
const count = Math.min(ips.length, naddrs);
|
|
6565
|
+
new Uint8Array(view.buffer, addrs, ADDR_IP_SIZE * count).fill(0);
|
|
6566
|
+
for (let i = 0; i < count; i++) {
|
|
6567
|
+
const at = addrs + i * ADDR_IP_SIZE;
|
|
6568
|
+
view.setUint8(at, ips[i].length === 4 ? INET4 : INET6);
|
|
6569
|
+
new Uint8Array(view.buffer, at + 2, ips[i].length).set(ips[i]);
|
|
6570
|
+
}
|
|
6571
|
+
view.setUint32(out, count, true);
|
|
6503
6572
|
}
|
|
6504
6573
|
};
|
|
6505
6574
|
}
|
|
@@ -6507,7 +6576,7 @@ var INET4, INET6, UNIX, STREAM, ADDR_IP_SIZE, LOOPBACK, OPTIONS;
|
|
|
6507
6576
|
var init_wasix_sockets = __esm({
|
|
6508
6577
|
"src/process/wasi/wasix-sockets.ts"() {
|
|
6509
6578
|
"use strict";
|
|
6510
|
-
|
|
6579
|
+
init_routes();
|
|
6511
6580
|
init_wasi_abi();
|
|
6512
6581
|
init_wasi_files();
|
|
6513
6582
|
INET4 = 1;
|
|
@@ -7016,7 +7085,7 @@ function firstOther(foreign) {
|
|
|
7016
7085
|
function accepted(imp, scope2) {
|
|
7017
7086
|
const { module, name, kind } = imp;
|
|
7018
7087
|
if (module === PREVIEW1 || module === WASIX) return true;
|
|
7019
|
-
if (!
|
|
7088
|
+
if (!RESERVED2.has(module) && (scope2.extended || scope2.wasi && kind === "function")) return true;
|
|
7020
7089
|
if (scope2.pie && (module === "GOT.mem" || module === "GOT.func")) return true;
|
|
7021
7090
|
if (scope2.pie && module === "env" && kind !== "memory") return true;
|
|
7022
7091
|
if (kind === "memory" && scope2.memory?.module === module && scope2.memory.name === name)
|
|
@@ -7044,7 +7113,7 @@ function unsupportedImport(module, memory, extended = false, foreign = {}) {
|
|
|
7044
7113
|
}
|
|
7045
7114
|
function linkImports(module, preview1, wasix, memory, threads, program = {}, foreign, slicc = {}) {
|
|
7046
7115
|
for (const ns of Object.keys(program)) {
|
|
7047
|
-
if (
|
|
7116
|
+
if (RESERVED2.has(ns) || ns === SLICC)
|
|
7048
7117
|
throw new Error(`the imports module may not define ${ns}`);
|
|
7049
7118
|
}
|
|
7050
7119
|
const imports = {
|
|
@@ -7352,7 +7421,7 @@ async function runWasiThread(init, port2) {
|
|
|
7352
7421
|
throw e;
|
|
7353
7422
|
}
|
|
7354
7423
|
}
|
|
7355
|
-
var TRAPPED, PREVIEW1, WASIX, SLICC,
|
|
7424
|
+
var TRAPPED, PREVIEW1, WASIX, SLICC, RESERVED2, noErrno, FALLBACK_MAXIMUM_PAGES, LinkSync, DL_GEN, BACKTRACE_FRAMES;
|
|
7356
7425
|
var init_wasi_runtime = __esm({
|
|
7357
7426
|
"src/process/wasi/wasi-runtime.ts"() {
|
|
7358
7427
|
"use strict";
|
|
@@ -7378,7 +7447,7 @@ var init_wasi_runtime = __esm({
|
|
|
7378
7447
|
PREVIEW1 = "wasi_snapshot_preview1";
|
|
7379
7448
|
WASIX = "wasix_32v1";
|
|
7380
7449
|
SLICC = "slicc";
|
|
7381
|
-
|
|
7450
|
+
RESERVED2 = RESERVED_NAMESPACES;
|
|
7382
7451
|
noErrno = (key) => `imports ${key}: its result cannot carry ENOSYS, and this host does not provide it`;
|
|
7383
7452
|
FALLBACK_MAXIMUM_PAGES = 32768;
|
|
7384
7453
|
LinkSync = class {
|