171 lines
4.3 KiB
JavaScript
171 lines
4.3 KiB
JavaScript
/**
|
|
* Booter session helpers: guest env prefix maps and TTY readline setup.
|
|
*/
|
|
import {
|
|
createBareReadlineQuestion,
|
|
createStreamLineReader,
|
|
looksLikeInteractiveStdin
|
|
} from './cli-readline.js'
|
|
import { resolveStdio } from './resolve-stdio.js'
|
|
import { bareOsHostBooterWarn } from './bare-os-host-booter-log.js'
|
|
|
|
/**
|
|
* @param {Record<string, string>} env
|
|
* @returns {string[]}
|
|
*/
|
|
export function parseUnionReadPrefixes(env) {
|
|
const raw = env && env.BARE_OS_VFS_UNION_PREFIXES
|
|
if (raw == null || !String(raw).trim()) return []
|
|
return String(raw)
|
|
.split(/[,:]+/)
|
|
.map((s) => s.trim())
|
|
.filter((s) => s.startsWith('/'))
|
|
}
|
|
|
|
/**
|
|
* @param {Record<string, string>} env
|
|
* @returns {string[]}
|
|
*/
|
|
export function parseUnionWriteDenyPrefixes(env) {
|
|
const raw = env && env.BARE_OS_VFS_UNION_WRITE_DENY
|
|
if (raw == null || !String(raw).trim()) return []
|
|
return String(raw)
|
|
.split(/[,:]+/)
|
|
.map((s) => s.trim())
|
|
.filter((s) => s.startsWith('/'))
|
|
}
|
|
|
|
/**
|
|
* @param {Record<string, string>} env
|
|
* @returns {Map<string, number> | null}
|
|
*/
|
|
export function parseIpcChannelMaxBytesMap(env) {
|
|
const raw = env && env.BARE_OS_IPC_CHANNEL_MAX_BYTES
|
|
if (raw == null || !String(raw).trim()) return null
|
|
try {
|
|
const o = JSON.parse(String(raw))
|
|
if (!o || typeof o !== 'object') return null
|
|
const m = new Map()
|
|
for (const [k, v] of Object.entries(o)) {
|
|
const n = Number(v)
|
|
if (
|
|
typeof k === 'string' &&
|
|
/^[a-zA-Z0-9._-]+$/.test(k) &&
|
|
Number.isFinite(n) &&
|
|
n > 0
|
|
) {
|
|
m.set(k, Math.min(Math.floor(n), 16 * 1024 * 1024))
|
|
}
|
|
}
|
|
return m.size ? m : null
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @returns {Promise<{
|
|
* readLine: (p: string) => Promise<string | null>,
|
|
* interactiveAvailable: boolean,
|
|
* skipInteractive: boolean,
|
|
* stdout: import('stream').Writable | null,
|
|
* stdin: import('stream').Readable | null
|
|
* }>}
|
|
*/
|
|
export async function createReadLine() {
|
|
const { stdin, stdout } = await resolveStdio()
|
|
|
|
const skipInteractive = globalThis.process?.env?.BARE_OS_SKIP_REPL === '1'
|
|
if (skipInteractive) {
|
|
return {
|
|
readLine: async () => null,
|
|
interactiveAvailable: false,
|
|
skipInteractive: true,
|
|
stdout,
|
|
stdin
|
|
}
|
|
}
|
|
|
|
if (!stdin || !stdout) {
|
|
bareOsHostBooterWarn(
|
|
'stdio_missing',
|
|
'No stdin/stdout (neither process nor bare-stdio). Cannot run interactive shell.'
|
|
)
|
|
return {
|
|
readLine: async () => null,
|
|
interactiveAvailable: false,
|
|
skipInteractive: false,
|
|
stdout,
|
|
stdin
|
|
}
|
|
}
|
|
|
|
const ttyRawCapable =
|
|
Boolean(stdin.isTTY) &&
|
|
typeof stdin.setRawMode === 'function' &&
|
|
typeof stdout.write === 'function'
|
|
|
|
/** Prefer fish-style raw editor on TTY — bare-readline / stream reader only (no Node readline). */
|
|
if (ttyRawCapable) {
|
|
/** Lazy: avoid bare-readline attaching to stdin before createFishReadLine runs. */
|
|
let ttyFallback = undefined
|
|
const lazyRawReadLine = async (prompt) => {
|
|
if (ttyFallback === undefined) {
|
|
try {
|
|
ttyFallback = await createBareReadlineQuestion(stdin, stdout)
|
|
} catch {
|
|
ttyFallback =
|
|
looksLikeInteractiveStdin(stdin) &&
|
|
typeof stdout.write === 'function'
|
|
? createStreamLineReader(stdin, stdout)
|
|
: false
|
|
}
|
|
}
|
|
if (ttyFallback === false) return null
|
|
return ttyFallback(prompt)
|
|
}
|
|
return {
|
|
readLine: lazyRawReadLine,
|
|
interactiveAvailable: true,
|
|
skipInteractive: false,
|
|
stdout,
|
|
stdin
|
|
}
|
|
}
|
|
|
|
try {
|
|
const readLine = await createBareReadlineQuestion(stdin, stdout)
|
|
return {
|
|
readLine,
|
|
interactiveAvailable: true,
|
|
skipInteractive: false,
|
|
stdout,
|
|
stdin
|
|
}
|
|
} catch {
|
|
/* bare-readline failed to load */
|
|
}
|
|
|
|
if (looksLikeInteractiveStdin(stdin) && typeof stdout.write === 'function') {
|
|
return {
|
|
readLine: createStreamLineReader(stdin, stdout),
|
|
interactiveAvailable: true,
|
|
skipInteractive: false,
|
|
stdout,
|
|
stdin
|
|
}
|
|
}
|
|
|
|
bareOsHostBooterWarn(
|
|
'line_input_unavailable',
|
|
'No line input. Set BARE_OS_SKIP_REPL=1 to exit after boot.'
|
|
)
|
|
return {
|
|
readLine: async () => null,
|
|
interactiveAvailable: false,
|
|
skipInteractive: false,
|
|
stdout,
|
|
stdin
|
|
}
|
|
}
|