Files
bare-operating-system/packages/bare-os-booter/test.shell.js
T
2026-08-18 18:11:34 -04:00

2616 lines
83 KiB
JavaScript

import {
test,
b4a,
Hyperdrive,
Corestore,
mkdirSync,
rmSync,
readFile,
readdir,
path,
fileURLToPath,
PassThrough,
XTERM_CLEAR_SCROLLBACK_AND_VIEWPORT,
runKernelFromSource,
runBinCommand,
createGitFsFromVfs,
runGitCli,
createStreamLineReader,
sanitizeInteractiveShellLine,
createVfs,
BARE_OS_PROC_FILE_TO_ID_HYPERCORE_PACK_HRPC_LIFECYCLE,
parseHdmsPairArgv,
hcCrypto,
idEnc,
decodeRwShareOffer,
decodeShareOffer,
HDMS_AUTOPASS_SHARE_KEY,
HDMS_AUTOPASS_SHARE_RW_KEY,
normalizeWriterSecretHex,
buildBareOsHypercorePackHrpcLifecycleProcJson,
buildBareOsPearCorestoreHrpcProcJson,
buildBareOsPearInspectLoggerTlsProcJson,
buildBareOsBareRuntimeProtoMuxProcJson,
buildBareOsBootGraphProcJson,
pathnameExpandShellWord,
createBareOsIpc,
BareOsSwarmConnectionManager,
createBareOsLifecycleStateMachine,
runTransactionalLifecycleHooks,
bareOsRegisterCorestoreSuspendResumeHooks,
bareOsInstallCorestoreSuspendResumeHooks,
bareOsFindOrphanProcessRows,
topologicalOrderKernelExtensions,
resolveKernelExtensionsFull,
createBareOsProtomuxAliasRegistry,
bareOsHttpUrlAllowed,
bareOsParseBareAclText,
bareOsAclDeniesSubject,
raceWithAbortAndTimeout,
tokenize,
expandWord,
execShellLine,
expandArgvAliases,
defaultShellAliases,
loadBarerc,
BARERC_SKELETON,
splitTokensBySemicolon,
splitTokensByAndOr,
bareOsShellAstSnapshot,
planShellRedirections,
buildShellExecutionGraph,
dispatchShellTrapSignal,
BARE_OS_EXIT_STATUS_ENV,
syncBareOsExitStatusEnv,
getBareOsPipelineLimits,
DEFAULT_PIPELINE_MAX_STAGES,
tokenizeBareShellLineDetailed,
decodeBareOsDollarQuote,
applyBareOsThemeFromEnv,
bareOsListThemeNames,
bareParseLsColors,
bareSerializeLsColors,
bareLsColorOpenSgrFromMap,
bareDefaultDircolorsDatabase,
bareParseDircolorsDatabase,
BARE_OS_CTX_API_VERSION,
buildBareOsRuntimeCaps,
bareOsBareModulesEnabled,
bareOsBareModuleManifestEmbeddedRef,
buildBareCtxObjectFromHost,
buildPearCtxObjectFromHost,
loadBareModuleManifest,
maybeMergeBareFromDrive,
maybeBareOsBuildBinManifest,
createFishReadLine,
fuzzyMatch,
stripAnsi,
parseHistoryFile,
formatHistoryFile,
dedupeConsecutiveHistory,
searchHistoryEntries,
shouldPersistShellHistoryCommand,
scrubShellHistoryEntries,
SHELL_BUILTINS,
resolveShellPromptHookSegment,
parseCompletionContext,
rankCompletionItems,
suggestGhostFromHistory,
suggestGhostFromFs,
ghostReplaceCurrentWord,
longestCommonCompletionPrefix,
completeLine,
levenshtein,
fieldMatches,
dowFieldMatches,
parseCronLine,
jobMatchesDate,
bareInitdReadinessSnapshot,
bareInitdShutdownActiveUnitsReverse,
getBareServiceRuntime,
registerBareInitdDisposer,
registerKernelShutdownHook,
runKernelShutdownHooks,
startBareInitd,
stopBareInitd,
getLastBareInitdDagSnapshotJson,
appendVarLog,
ensureBareOsVarLogTree,
KERNEL_CONSOLE_LOG,
BOOT_LOG,
bareOsKernelMetricsReset,
bareOsKernelMetricsSnapshot,
parseUnitDropInText,
bareOsHyperswarmOptsFromEnv,
bareOsBootJoinPeerHexFromEnv,
bareOsBootInitPrefetchEnabled,
bareOsBootWarmBinDownloadDiffEnabled,
bareOsProcessTableSnapshot,
buildBareOsSyscallsProcJson,
bareOsParseSendmsgScmRights,
wantPosixSocketScmRights,
parseBareOsHrpcAllowlistJson,
bareOsHrpcAllowlistDeniesRoute,
buildBareOsProtomuxExtensionsProcJson,
createBareOsDiskOsBridge,
buildBareOsReplicationLiveSketch,
evaluateBareOsPeerAdmission,
bareOsFormatPeerAdmissionAuditEvent,
bareOsPeerAdmissionAuditRateAllow,
parseDelegateAllowSet,
isDelegateKindAllowed,
bareOsAuditAppend,
bareOsAuditVerifyRows,
bareOsDrainFcntlLockWaiters,
buildBareOsReplicationOperatorSurfaceProcJson,
DEFAULT_CURL_USER_AGENT,
DEFAULT_WGET_USER_AGENT,
filenameFromContentDisposition,
encodeUstarHeader,
runTarCli,
require,
__dirname,
TAR_BLK,
concatTarParts,
headerFromInit,
testCorestoreDir,
testCtx,
personalHomeBacking,
readBuiltBin
} from './test/_helpers.js'
test('tokenize handles quotes and ops', async (t) => {
const tok = tokenize('ls -la | cat > out')
t.ok(tok.some((x) => x.type === 'op' && x.value === '|'))
t.ok(tok.some((x) => x.type === 'op' && x.value === '>'))
const w = tokenize("echo 'a b'")
const words = w.filter((x) => x.type === 'word').map((x) => x.value)
t.is(words.join(','), 'echo,a b')
})
test('tokenize records quote parts for glob (literal * in quotes)', async (t) => {
const u = tokenize('echo *')
const uwords = u.filter((x) => x.type === 'word')
const star = uwords[uwords.length - 1]
t.ok(star && star.type === 'word' && star.parts)
t.is(star.parts.length, 1)
t.is(star.parts[0].q, 'u')
t.is(star.parts[0].t, '*')
const sq = tokenize("echo '*'")
const sqw = sq.filter((x) => x.type === 'word')
t.is(sqw[0].value, 'echo')
const lit = sqw[1]
t.is(lit.parts.length, 1)
t.is(lit.parts[0].q, 's')
t.is(lit.parts[0].t, '*')
})
test('tokenize keeps arithmetic expansion in one word token', async (t) => {
const toks = tokenize('n=$((n+1)); echo "$((n+2))"')
const words = toks.filter((x) => x.type === 'word').map((x) => x.value)
t.ok(words.includes('n=$((n+1))'))
t.ok(words.includes('$((n+2))'))
t.is(toks.filter((x) => x.type === 'op' && x.value === '(').length, 0)
t.is(toks.filter((x) => x.type === 'op' && x.value === ')').length, 0)
})
test('tokenize recognizes <<- heredoc operator', async (t) => {
const toks = tokenize('cat <<- EOF')
t.ok(toks.some((x) => x.type === 'op' && x.value === '<<-'))
})
test('tokenize emits compound ;; ;& |& >& operators', (t) => {
const a = tokenize('echo ok;; echo x')
t.ok(a.some((x) => x.type === 'op' && x.value === ';;'))
t.ok(tokenize('a;&b').some((x) => x.type === 'op' && x.value === ';&'))
t.ok(tokenize('a|&b').some((x) => x.type === 'op' && x.value === '|&'))
t.ok(tokenize('a>&2').some((x) => x.type === 'op' && x.value === '>&'))
})
test('decodeBareOsDollarQuote handles ANSI escapes', (t) => {
t.is(decodeBareOsDollarQuote(String.raw`a\n\t\\'\x41`), "a\n\t\\'A")
})
test('tokenize parses $\'…\' as decoded single-quoted segment', (t) => {
const toks = tokenize(`echo $'hi\\nx'`)
const echoTok = toks.find((x) => x.type === 'word' && x.value.includes('hi'))
t.ok(echoTok && echoTok.parts)
const sq = echoTok.parts.filter((p) => p.q === 's')
t.ok(sq.some((p) => p.t === 'hi\nx'))
})
test('execShellLine $\'…\' echo and cmdsubst $(…) / backticks when enabled', async (t) => {
const dir = testCorestoreDir('shansi')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('sans'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/echo',
b4a.from(`async function run(ctx, argv) {
ctx.console.log(argv.slice(1).join(' '))
}
`)
)
const logs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (...a) => logs.push(a.join(' ')),
error: (...a) => logs.push('e:' + a.join(' '))
}
ctx.vfs.env.BARE_OS_SHELL_CMDSUBST = '1'
ctx.exitCode = 0
await execShellLine(ctx, `echo $'a\\tb'`)
t.ok(logs.some((l) => l === 'a\tb'), logs.join('|'))
logs.length = 0
await execShellLine(ctx, 'echo "$(echo hi)"')
t.ok(logs.some((l) => l.includes('hi')), logs.join('|'))
logs.length = 0
await execShellLine(ctx, 'echo "`echo lo`"')
t.ok(logs.some((l) => l.includes('lo')), logs.join('|'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine command substitution is enabled by default', async (t) => {
const dir = testCorestoreDir('shcmdsub-default')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pcmdsubdef'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/echo',
b4a.from(`async function run(ctx, argv) {
ctx.console.log(argv.slice(1).join(' '))
}
`)
)
const logs = []
const ctx = testCtx(drive, personal)
delete ctx.vfs.env.BARE_OS_SHELL_CMDSUBST
ctx.console = {
log: (...a) => logs.push(a.join(' ')),
error: (...a) => logs.push('e:' + a.join(' '))
}
await execShellLine(ctx, 'echo "$(echo hi)"')
t.ok(logs.some((l) => l.includes('hi')), logs.join('|'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine brace range expansion supports {1..5}', async (t) => {
const dir = testCorestoreDir('sh-brace-range')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pbrace'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/echo',
b4a.from(`async function run(ctx, argv) {
ctx.console.log(argv.slice(1).join(' '))
}
`)
)
const lines = []
const ctx = testCtx(drive, personal)
ctx.vfs.env.BARE_OS_SHELL_BRACE_EXPANSION = '1'
ctx.console = { log: (m) => lines.push(String(m)), error: () => {} }
await execShellLine(ctx, 'echo {1..5}')
const joined = lines.join('|')
t.ok(!joined.includes('1..5'), joined)
t.ok(joined.includes('1'), joined)
t.ok(joined.includes('5'), joined)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine arithmetic command form (( 2 + 3 ))', async (t) => {
const dir = testCorestoreDir('sh-arith-cmd')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('parithcmd'))
await drive.ready()
await personal.ready()
const ctx = testCtx(drive, personal)
await execShellLine(ctx, '(( 2 + 3 ))')
t.is(ctx.exitCode, 0)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('expandWord supports ${#var} length and ${var:offset} when v3', (t) => {
const env = {
X: 'abcde',
BARE_OS_SHELL_PARAM_EXPANSION: '1',
BARE_OS_SHELL_PARAM_EXPANSION_V3: '1'
}
t.is(expandWord('${#X}', env), '5')
t.is(expandWord('${X:2}', env), 'cde')
t.is(expandWord('${X:2:2}', env), 'cd')
})
test('expandWord indirect ${!a} when enabled', (t) => {
const env = {
ref: 'X',
X: 'hi',
BARE_OS_SHELL_PARAM_EXPANSION: '1',
BARE_OS_SHELL_INDIRECT_EXPANSION: '1'
}
t.is(expandWord('${!ref}', env), 'hi')
})
test('tokenizeBareShellLineDetailed records span and mode metadata', async (t) => {
const rows = tokenizeBareShellLineDetailed(`echo 'a b' "c d" $((1+2)) <<EOF`)
t.ok(rows.length >= 4)
t.ok(rows.some((r) => r.mode === 'single'))
t.ok(rows.some((r) => r.mode === 'double'))
t.ok(rows.some((r) => r.mode === 'arith' || r.value.includes('))')))
t.ok(rows.every((r) => Number.isInteger(r.start) && Number.isInteger(r.end)))
t.ok(rows.every((r) => r.end >= r.start))
})
test('tokenizeBareShellLineDetailed deterministic output', async (t) => {
const src = `if [ "$x" = "y" ]; then echo ok; fi`
const a = JSON.stringify(tokenizeBareShellLineDetailed(src))
const b = JSON.stringify(tokenizeBareShellLineDetailed(src))
t.is(a, b)
})
test('bareOsShellAstSnapshot produces stable schema and tokenized pipeline', async (t) => {
const snap = bareOsShellAstSnapshot('echo hi | cat')
t.is(snap.schema, 1)
t.is(snap.line, 'echo hi | cat')
t.ok(Array.isArray(snap.tokens) && snap.tokens.length >= 3)
t.ok(Array.isArray(snap.pipeline) && snap.pipeline.length === 2)
t.ok(Array.isArray(snap.diagnosticTokens) && snap.diagnosticTokens.length >= 3)
})
test('planShellRedirections returns normalized redirects', async (t) => {
const p = planShellRedirections({
redirIn: null,
redirOut: { type: 'word', value: '/tmp/o', parts: [] },
redirAppend: false,
redirErr: null,
redirErrAppend: false,
mergeStderrToStdout: true,
redirHereDoc: null
})
t.is(p.stdin, 'inherit')
t.is(p.stdout, 'truncate')
t.is(p.stderr, 'stdout')
})
test('buildShellExecutionGraph models lists and pipelines', async (t) => {
const g = buildShellExecutionGraph('echo a | cat && echo b; echo c')
t.is(g.schema, 1)
t.is(g.listCount, 2)
t.ok(Array.isArray(g.lists) && g.lists.length === 2)
t.is(g.lists[0].segments[0].pipelineLength, 2)
t.is(g.lists[0].andOrOps[0], '&&')
})
test('execShellLine expansion trace captures stages when enabled', async (t) => {
const dir = testCorestoreDir('shexpansiontrace')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pexpansiontrace'))
await drive.ready()
await personal.ready()
const ctx = testCtx(drive, personal)
ctx.vfs.env.BARE_OS_SHELL_EXPANSION_TRACE = '1'
ctx.vfs.env.BARE_OS_SHELL_PARAM_EXPANSION = '1'
await execShellLine(ctx, 'A=ok; echo ${A:-no}')
t.ok(Array.isArray(ctx.shellExpansionTrace))
t.ok(ctx.shellExpansionTrace.some((r) => r.stage === 'expand-pre'))
t.ok(ctx.shellExpansionTrace.some((r) => r.stage === 'split-glob'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine stores execution graph when dump mode is enabled', async (t) => {
const dir = testCorestoreDir('shexecgraphdump')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pexecgraphdump'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/echo',
b4a.from(`async function run(ctx, argv){ ctx.console.log(argv.slice(1).join(' ')) }`)
)
const ctx = testCtx(drive, personal)
ctx.vfs.env.BARE_OS_SHELL_EXEC_GRAPH_DUMP = '1'
await execShellLine(ctx, 'echo one | echo two && echo three')
t.ok(ctx.shellLastExecGraph && ctx.shellLastExecGraph.schema === 1)
t.ok(ctx.shellLastExecGraph.listCount >= 1)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine test [ builtins and $(( $var + … ))', async (t) => {
const dir = testCorestoreDir('shbrackettest')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pshbrk'))
await drive.ready()
await personal.ready()
await drive.put('/bin/test', b4a.from(await readBuiltBin('test')))
const ctx = testCtx(drive, personal)
ctx.runBinCommand = function (argv, ro) {
return runBinCommand(this, argv, ro)
}
ctx.exitCode = 0
await execShellLine(ctx, 'a=1; test 2 -eq $((a+1))')
t.is(ctx.exitCode, 0)
ctx.exitCode = 0
await execShellLine(ctx, 'a=1; [ 2 -eq $(($a+1)) ]')
t.is(ctx.exitCode, 0)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('expandWord arithmetic honors $var inside $(( ))', (t) => {
t.is(expandWord('$(( $a + 1 ))', { a: '2' }), '3')
t.is(expandWord('$(( $a+1 ))', { a: '2' }), '3')
})
test('expandWord expansion-depth guard env is accepted', (t) => {
const env = {
BARE_OS_SHELL_PARAM_EXPANSION: '1',
BARE_OS_SHELL_PARAM_EXPANSION_V2: '1',
BARE_OS_SHELL_EXPANSION_MAX_DEPTH: '1'
}
t.is(expandWord('${A:=x}', env), 'x')
})
test('expandWord strict POSIX arithmetic emits strict-token diagnostics', (t) => {
const env = { BARE_OS_SHELL_POSIX_MODE: '1' }
t.exception(
() => expandWord('$((1 + bad,2))', env),
/POSIX mode strict arithmetic/
)
})
test('execShellLine pathname glob and noglob', async (t) => {
const dir = testCorestoreDir('shglob')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('sglob'))
await drive.ready()
await personal.ready()
const echo = `
async function run(ctx, argv) {
ctx.console.log(argv.slice(1).join(' '))
}
`
await drive.put('/bin/echo', b4a.from(echo))
const ctx = testCtx(drive, personal)
await ctx.vfs.writeFile('/home/user/a.txt', b4a.from('x'))
await ctx.vfs.writeFile('/home/user/b.txt', b4a.from('y'))
const logs = []
ctx.console = {
log: (...a) => logs.push(a.join(' ')),
error: (...a) => logs.push(a.join(' '))
}
await ctx.vfs.chdir('/home/user')
await execShellLine(ctx, 'echo *.txt')
t.ok(
logs.some((l) => l.includes('a.txt') && l.includes('b.txt')),
logs.join('|')
)
logs.length = 0
await execShellLine(ctx, "echo '*.txt'")
t.ok(logs.some((l) => l === '*.txt'), logs.join('|'))
logs.length = 0
ctx.vfs.env.BARE_OS_SHELL_NOGLOB = '1'
await execShellLine(ctx, 'echo *.txt')
t.ok(logs.some((l) => l === '*.txt'))
delete ctx.vfs.env.BARE_OS_SHELL_NOGLOB
await execShellLine(ctx, 'set -f; echo *.txt')
t.ok(logs.some((l) => l === '*.txt'))
await execShellLine(ctx, 'set +f; echo *.txt')
t.ok(logs.some((l) => l.includes('a.txt')))
logs.length = 0
await execShellLine(ctx, 'echo ~user')
t.ok(logs.some((l) => l === '/home/user'), logs.join('|'))
logs.length = 0
await execShellLine(ctx, 'echo ~other')
t.ok(logs.some((l) => l === '/home/other'), logs.join('|'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine BARE_OS_SHELL_POSIX_MODE grouped list', async (t) => {
const dir = testCorestoreDir('shposixgroup')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('spg'))
await drive.ready()
await personal.ready()
const echo = `
async function run(ctx, argv) {
ctx.console.log(argv.slice(1).join(' '))
}
`
await drive.put('/bin/echo', b4a.from(echo))
const ctx = testCtx(drive, personal)
const logs = []
ctx.console = {
log: (...a) => logs.push(a.join(' ')),
error: (...a) => logs.push(a.join(' '))
}
ctx.vfs.env.BARE_OS_SHELL_POSIX_MODE = '1'
await ctx.vfs.chdir('/home/user')
await execShellLine(ctx, '( echo hi )')
t.ok(logs.some((l) => l === 'hi'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine BARE_OS_SHELL_GROUPING runs parenthesized list without POSIX mode', async (t) => {
const dir = testCorestoreDir('shgroupingonly')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('sgo'))
await drive.ready()
await personal.ready()
const echo = `
async function run(ctx, argv) {
ctx.console.log(argv.slice(1).join(' '))
}
`
await drive.put('/bin/echo', b4a.from(echo))
const ctx = testCtx(drive, personal)
const logs = []
ctx.console = {
log: (...a) => logs.push(a.join(' ')),
error: (...a) => logs.push(a.join(' '))
}
delete ctx.vfs.env.BARE_OS_SHELL_POSIX_MODE
ctx.vfs.env.BARE_OS_SHELL_GROUPING = '1'
await ctx.vfs.chdir('/home/user')
await execShellLine(ctx, '( echo hi )')
t.ok(logs.some((l) => l === 'hi'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine select is unsupported with clear error', async (t) => {
const dir = testCorestoreDir('shselectstub')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('sels'))
await drive.ready()
await personal.ready()
const ctx = testCtx(drive, personal)
const logs = []
ctx.console = {
log: (...a) => logs.push(a.join(' ')),
error: (...a) => logs.push(a.join(' '))
}
ctx.exitCode = 0
await execShellLine(ctx, 'select x in a b; do echo x; done')
t.ok(logs.some((l) => l.includes('select') && l.includes('unsupported')))
t.is(ctx.exitCode, 2)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine rejects stray reserved word at statement start', async (t) => {
const dir = testCorestoreDir('shmisres')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('smr'))
await drive.ready()
await personal.ready()
const ctx = testCtx(drive, personal)
const logs = []
ctx.console = {
log: (...a) => logs.push(a.join(' ')),
error: (...a) => logs.push(a.join(' '))
}
ctx.exitCode = 0
await execShellLine(ctx, 'then echo x')
t.ok(logs.some((l) => l.includes("reserved word 'then'")))
t.is(ctx.exitCode, 2)
logs.length = 0
ctx.exitCode = 0
await execShellLine(ctx, 'fi')
t.ok(logs.some((l) => l.includes("reserved word 'fi'")))
t.is(ctx.exitCode, 2)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine [[ rejects by default and supports == when gated', async (t) => {
const dir = testCorestoreDir('shdblbr')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('sdb'))
await drive.ready()
await personal.ready()
const ctx = testCtx(drive, personal)
const logs = []
ctx.console = {
log: (...a) => logs.push(a.join(' ')),
error: (...a) => logs.push(a.join(' '))
}
ctx.exitCode = 0
await execShellLine(ctx, '[[ a == a ]]')
t.ok(logs.some((l) => l.includes('not supported')))
t.is(ctx.exitCode, 2)
logs.length = 0
ctx.vfs.env.BARE_OS_SHELL_DOUBLE_BRACKET = '1'
await execShellLine(ctx, '[[ x == x ]]')
t.is(ctx.exitCode, 0)
await execShellLine(ctx, '[[ x == y ]]')
t.is(ctx.exitCode, 1)
await execShellLine(ctx, '[[ x != y ]]')
t.is(ctx.exitCode, 0)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine applies alias expansion before shell function dispatch', async (t) => {
const dir = testCorestoreDir('shaliasfn')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('safn'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/echo',
b4a.from(`async function run(ctx, argv) {
ctx.console.log(argv.slice(1).join(' '))
}
`)
)
const lines = []
const ctx = testCtx(drive, personal)
ctx.console = {
log(s) {
lines.push(String(s))
},
error(s) {
lines.push('e:' + String(s))
}
}
ctx.exitCode = 0
await execShellLine(
ctx,
"bos_fn_a() { echo FN; }; alias bos_fn_a='echo AL'; bos_fn_a"
)
t.is(ctx.exitCode, 0)
t.ok(lines.includes('AL'))
t.ok(!lines.includes('FN'))
lines.length = 0
ctx.exitCode = 0
await execShellLine(
ctx,
"bos_fn_b() { echo FB; }; alias bos_call_b='bos_fn_b'; bos_call_b"
)
t.is(ctx.exitCode, 0)
t.ok(lines.includes('FB'))
lines.length = 0
ctx.exitCode = 0
await execShellLine(ctx, 'bos_fn_c() { echo FC; }; bos_fn_c')
t.is(ctx.exitCode, 0)
t.ok(lines.includes('FC'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine rejects process substitution with exit 2', async (t) => {
const dir = testCorestoreDir('shprosubst')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('psub'))
await drive.ready()
await personal.ready()
const ctx = testCtx(drive, personal)
const logs = []
ctx.console = {
log: (...a) => logs.push(a.join(' ')),
error: (...a) => logs.push(a.join(' '))
}
ctx.exitCode = 0
await execShellLine(ctx, 'echo hi <(echo x)')
t.ok(logs.some((l) => l.includes('process substitution')))
t.is(ctx.exitCode, 2)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('expandWord reads env', async (t) => {
t.is(expandWord('x${HOME}y', { HOME: '/h' }), 'x/hy')
})
test('expandWord $? and ${?} use BARE_OS_EXIT_STATUS', async (t) => {
const env = { [BARE_OS_EXIT_STATUS_ENV]: '7', HOME: '/h' }
t.is(expandWord('code=$?', env), 'code=7')
t.is(expandWord('c=${?}', env), 'c=7')
t.is(expandWord('missing=$?', {}), 'missing=0')
})
test('expandWord param expansion v2 (## %% :=)', async (t) => {
const base = {
BARE_OS_SHELL_PARAM_EXPANSION: '1',
BARE_OS_SHELL_PARAM_EXPANSION_V2: '1',
P: '/usr/bin/foo',
Q: 'caba'
}
t.is(expandWord('${P##*/}', base), 'foo')
t.is(expandWord('${P#*/}', base), 'usr/bin/foo')
t.is(expandWord('${Q%%ba}', base), 'ca')
t.is(expandWord('${Q%ba}', base), 'ca')
const assign = { ...base, EMPTY: '' }
t.is(expandWord('${EMPTY:=set}', assign), 'set')
t.is(assign.EMPTY, 'set')
})
/** POSIX-style parameter expansion vectors (Issue 7 subset; env-gated V3). */
test('expandWord param expansion v3 (:- :+ :?)', async (t) => {
const base = {
BARE_OS_SHELL_PARAM_EXPANSION: '1',
BARE_OS_SHELL_PARAM_EXPANSION_V3: '1',
HOME: '/home/u'
}
t.is(expandWord('${UNSET:-/default}', base), '/default')
t.is(expandWord('${EMPTY:-/alt}', { ...base, EMPTY: '' }), '/alt')
t.is(expandWord('${SET:-ignored}', { ...base, SET: 'ok' }), 'ok')
t.is(expandWord('${SET:+present}', { ...base, SET: 'v' }), 'present')
t.is(expandWord('${UNSET:+absent}', base), '')
t.exception(
() => expandWord('${UNSET:?hard fail}', base),
/hard fail|parameter null or unset/
)
t.exception(
() => expandWord('${EMPTY:?msg}', { ...base, EMPTY: '' }),
/msg|parameter null or unset/
)
})
test('syncBareOsExitStatusEnv and execShellLine update env', async (t) => {
const dir = testCorestoreDir('exstat')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pex'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/false',
b4a.from(`async function run(ctx) { ctx.exitCode = 1 }`)
)
await drive.put(
'/bin/true',
b4a.from(`async function run(ctx) { ctx.exitCode = 0 }`)
)
const ctx = testCtx(drive, personal)
ctx.exitCode = 0
syncBareOsExitStatusEnv(ctx)
t.is(ctx.vfs.env[BARE_OS_EXIT_STATUS_ENV], '0')
await execShellLine(ctx, 'false')
t.is(ctx.vfs.env[BARE_OS_EXIT_STATUS_ENV], '1')
await execShellLine(ctx, 'true')
t.is(ctx.vfs.env[BARE_OS_EXIT_STATUS_ENV], '0')
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine export -p lists session env', async (t) => {
const dir = testCorestoreDir('shexportp')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pexp'))
await drive.ready()
await personal.ready()
const logs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (m) => logs.push(String(m)),
error: (...a) => logs.push(a.map(String).join(' '))
}
ctx.vfs.env.FOO = 'bar'
ctx.exitCode = 0
await execShellLine(ctx, 'export -p')
t.ok(logs.some((l) => l.includes("export FOO='bar'")))
t.is(ctx.exitCode, 0)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine readonly -p lists readonly vars with stable quoting', async (t) => {
const dir = testCorestoreDir('shreadonlyp')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pronly'))
await drive.ready()
await personal.ready()
const logs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (m) => logs.push(String(m)),
error: (...a) => logs.push(a.map(String).join(' '))
}
ctx.exitCode = 0
await execShellLine(ctx, "readonly FOO=bar BAR='x y'")
await execShellLine(ctx, 'readonly -p')
t.ok(logs.some((l) => l.includes("readonly BAR='x y'")))
t.ok(logs.some((l) => l.includes("readonly FOO='bar'")))
t.is(ctx.exitCode, 0)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine BARE_OS_SHELL_READ_BUILTIN reads from shellStdin', async (t) => {
const dir = testCorestoreDir('shreadin')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pshr'))
await drive.ready()
await personal.ready()
const ctx = testCtx(drive, personal)
ctx.vfs.env.BARE_OS_SHELL_READ_BUILTIN = '1'
ctx.vfs.env.IFS = ' '
ctx.shellStdin = 'one two\n'
ctx.exitCode = 0
await execShellLine(ctx, 'read x y')
t.is(ctx.vfs.env.x, 'one')
t.is(ctx.vfs.env.y, 'two')
t.is(ctx.exitCode, 0)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine read builtin supports -d delimiter', async (t) => {
const dir = testCorestoreDir('shread-delim')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pshread-delim'))
await drive.ready()
await personal.ready()
const ctx = testCtx(drive, personal)
ctx.vfs.env.BARE_OS_SHELL_READ_BUILTIN = '1'
ctx.shellStdin = 'alpha,beta,gamma'
await execShellLine(ctx, 'read -d , first rest')
t.is(ctx.vfs.env.first, 'alpha')
t.is(ctx.exitCode, 0)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine read builtin supports -r raw backslashes', async (t) => {
const dir = testCorestoreDir('shread-raw')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pshread-raw'))
await drive.ready()
await personal.ready()
const ctx = testCtx(drive, personal)
ctx.vfs.env.BARE_OS_SHELL_READ_BUILTIN = '1'
ctx.shellStdin = 'a\\ b\n'
await execShellLine(ctx, 'read cooked')
t.is(ctx.vfs.env.cooked, 'a b')
ctx.shellStdin = 'c\\ d\n'
await execShellLine(ctx, 'read -r raw')
t.is(ctx.vfs.env.raw, 'c\\ d')
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine read builtin timeout returns non-zero', async (t) => {
const dir = testCorestoreDir('shread-timeout')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pshread-timeout'))
await drive.ready()
await personal.ready()
const ctx = testCtx(drive, personal)
ctx.vfs.env.BARE_OS_SHELL_READ_BUILTIN = '1'
ctx.readLine = async () => {
await new Promise((resolve) => setTimeout(resolve, 50))
return 'late'
}
await execShellLine(ctx, 'read -t 0.01 x')
t.is(ctx.exitCode, 1)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine here-doc works in non-interactive script mode', async (t) => {
const dir = testCorestoreDir('sh-heredoc-script')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pshhd'))
await drive.ready()
await personal.ready()
await drive.put('/bin/cat', b4a.from(await readBuiltBin('cat')))
const lines = []
const ctx = testCtx(drive, personal)
ctx.console = { log: (s) => lines.push(String(s)), error() {} }
ctx.readLine = (() => {
const seq = ['one $HOME', 'two', 'EOF']
return () => Promise.resolve(seq.shift() ?? null)
})()
ctx.vfs.env.HOME = '/home/user'
ctx.vfs.env.BARE_OS_SHELL_POSIX_MODE = '1'
await execShellLine(ctx, 'cat << EOF')
t.is(ctx.exitCode, 0)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine BARE_OS_SHELL_ERREXIT skips after failed command', async (t) => {
const dir = testCorestoreDir('errexit')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pex-e'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/false',
b4a.from(`async function run(ctx) { ctx.exitCode = 1 }`)
)
await drive.put(
'/bin/true',
b4a.from(`async function run(ctx) { ctx.exitCode = 0 }`)
)
await drive.put(
'/bin/echo',
b4a.from(
`async function run(ctx, argv) { ctx.console.log(argv.slice(1).join(' ')) }`
)
)
const logs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (m) => logs.push(String(m)),
error: (...a) => logs.push(a.map(String).join(' '))
}
ctx.vfs.env.BARE_OS_SHELL_ERREXIT = '1'
await execShellLine(ctx, 'false; echo AFTER')
t.ok(!logs.some((l) => l.includes('AFTER')))
logs.length = 0
ctx.exitCode = 0
await execShellLine(ctx, 'true; echo OK')
t.ok(logs.some((l) => l.includes('OK')))
logs.length = 0
ctx.exitCode = 0
delete ctx.vfs.env.BARE_OS_SHELL_ERREXIT
await execShellLine(ctx, 'set -e; false; echo NEVER')
t.ok(!logs.some((l) => l.includes('NEVER')))
logs.length = 0
ctx.exitCode = 0
await execShellLine(ctx, 'set -e; if true; then false; echo NEVER1; fi; echo NEVER2')
t.ok(!logs.some((l) => l.includes('NEVER1')))
t.ok(!logs.some((l) => l.includes('NEVER2')))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine trap normalizes signals and prints handlers', async (t) => {
const dir = testCorestoreDir('sh-trap')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pshtrap'))
await drive.ready()
await personal.ready()
const logs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (m) => logs.push(String(m)),
error: (...a) => logs.push(a.map(String).join(' '))
}
await execShellLine(ctx, "trap 'trap -l' SIGTERM")
t.is(ctx.shellTrapHandlers.TERM, 'trap -l')
await execShellLine(ctx, 'trap - TERM')
t.is(ctx.shellTrapHandlers.TERM, undefined)
await execShellLine(ctx, "trap 'trap -l' TERM")
logs.length = 0
await execShellLine(ctx, 'trap -p')
t.ok(logs.some((l) => l.includes("trap -- 'trap -l' TERM")))
logs.length = 0
const ran = await dispatchShellTrapSignal(ctx, 'SIGTERM')
t.is(ran, true)
t.ok(logs.some((l) => l.includes('HUP INT KILL TERM')))
const missed = await dispatchShellTrapSignal(ctx, 'SIGUSR1')
t.is(missed, false)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine trap EXIT runs before exit', async (t) => {
const dir = testCorestoreDir('sh-trap-exit')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pshtrapexit'))
await drive.ready()
await personal.ready()
const logs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (m) => logs.push(String(m)),
error: (...a) => logs.push(a.map(String).join(' '))
}
await execShellLine(ctx, "trap 'trap -l' EXIT; exit 1")
t.ok(logs.some((l) => l.includes('HUP INT KILL TERM')))
t.is(ctx.exitCode, 1)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine BARE_OS_SHELL_NOUNSET rejects unbound variable', async (t) => {
const dir = testCorestoreDir('nounset')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pex-nu'))
await drive.ready()
await personal.ready()
const ctx = testCtx(drive, personal)
const errs = []
ctx.console.error = (m) => errs.push(String(m))
ctx.vfs.env.BARE_OS_SHELL_NOUNSET = '1'
await execShellLine(ctx, 'echo $UNBOUND_XYZ')
t.is(ctx.exitCode, 1)
t.ok(errs.some((e) => /unbound variable/.test(e)))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine BARE_OS_SHELL_PIPEFAIL uses first failing stage exit', async (t) => {
const dir = testCorestoreDir('pipefail')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pex-pf'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/false',
b4a.from(`async function run(ctx) { ctx.exitCode = 1 }`)
)
await drive.put(
'/bin/true',
b4a.from(`async function run(ctx) { ctx.exitCode = 0 }`)
)
const ctx = testCtx(drive, personal)
ctx.vfs.env.BARE_OS_SHELL_PIPEFAIL = '1'
await execShellLine(ctx, 'false | true')
t.is(ctx.exitCode, 1)
delete ctx.vfs.env.BARE_OS_SHELL_PIPEFAIL
await execShellLine(ctx, 'false | true')
t.is(ctx.exitCode, 0)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine set -o pipefail and BARE_OS_PIPESTATUS', async (t) => {
const dir = testCorestoreDir('pipestatus')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pex-ps'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/false',
b4a.from(`async function run(ctx) { ctx.exitCode = 1 }`)
)
await drive.put(
'/bin/true',
b4a.from(`async function run(ctx) { ctx.exitCode = 0 }`)
)
const ctx = testCtx(drive, personal)
ctx.vfs.env.BARE_OS_SHELL_PIPESTATUS = '1'
await execShellLine(ctx, 'set -o pipefail; true | false | true')
t.is(ctx.vfs.env.BARE_OS_PIPESTATUS, '0 1 0')
t.is(ctx.exitCode, 1)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand kill resolves %n and %% via shellBackgroundJobs', async (t) => {
const dir = testCorestoreDir('killjob')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pkjob'))
await drive.ready()
await personal.ready()
const killPath = path.join(
path.dirname(fileURLToPath(import.meta.url)),
'../../kernel/bin/kill'
)
const killSrc = await readFile(killPath)
await drive.put('/bin/kill', killSrc)
const ctx = testCtx(drive, personal)
/** @type {[string, string][]} */
const calls = []
ctx.bareOsSendSignal = function (target, sig) {
calls.push([String(target), String(sig)])
}
ctx.shellBackgroundJobs = {
nextId: 3,
list: [
{
id: 1,
pgid: 400,
done: false,
stopped: false,
label: 'a',
promise: Promise.resolve('ok')
},
{
id: 2,
pgid: 401,
done: false,
stopped: false,
label: 'b',
promise: Promise.resolve('ok')
}
]
}
ctx.exitCode = 0
await runBinCommand(ctx, ['kill', '-0', '%1'])
t.alike(calls[0], ['4101', '0'])
calls.length = 0
await runBinCommand(ctx, ['kill', '-TERM', '%%'])
t.alike(calls[0], ['4102', 'TERM'])
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine set -o prints shell toggles', async (t) => {
const dir = testCorestoreDir('seto-print')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pso'))
await drive.ready()
await personal.ready()
const logs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (m) => logs.push(String(m)),
error: (...a) => logs.push(a.join(' '))
}
ctx.exitCode = 0
await execShellLine(ctx, 'set -o')
t.ok(logs.some((l) => /errexit\s+off/.test(l)))
t.ok(logs.some((l) => /pipefail\s+off/.test(l)))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine jobs -p prints pgid only', async (t) => {
const dir = testCorestoreDir('jobs-p')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pjobs'))
await drive.ready()
await personal.ready()
const logs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (m) => logs.push(String(m)),
error: (...a) => logs.push(a.join(' '))
}
ctx.shellBackgroundJobs = {
nextId: 2,
list: [
{
id: 1,
pgid: 712,
sid: 1,
done: false,
stopped: false,
label: 'x',
promise: Promise.resolve('ok')
}
]
}
ctx.exitCode = 0
await execShellLine(ctx, 'jobs -p')
t.ok(logs.includes('712'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine jobs -l is stable and parseable', async (t) => {
const dir = testCorestoreDir('jobs-l-parseable')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pjobsl'))
await drive.ready()
await personal.ready()
const logs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (m) => logs.push(String(m)),
error: (...a) => logs.push(a.join(' '))
}
ctx.shellBackgroundJobs = {
nextId: 3,
list: [
{
id: 2,
pgid: 990,
sid: 1,
done: false,
stopped: false,
label: 'sleep 1',
promise: Promise.resolve('ok')
}
]
}
await execShellLine(ctx, 'jobs -l')
t.ok(logs.some((l) => /^\[2\]\+\s+Running/.test(l)))
t.ok(logs.some((l) => l.includes('pid=4102')))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine wait supports synthetic pid and all keyword', async (t) => {
const dir = testCorestoreDir('wait-pid-all')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pwaitpidall'))
await drive.ready()
await personal.ready()
const mkJob = (id, exitCode) => ({
id,
pgid: 700 + id,
sid: 1,
done: false,
stopped: false,
label: `job-${id}`,
lastExitCode: exitCode,
promise: Promise.resolve('ok')
})
const ctx = testCtx(drive, personal)
ctx.shellBackgroundJobs = { nextId: 3, list: [mkJob(1, 0), mkJob(2, 7)] }
await execShellLine(ctx, 'wait 4102')
t.is(ctx.exitCode, 7)
await execShellLine(ctx, 'wait all')
t.is(ctx.exitCode, 7)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine deny/allow policy and sandbox guard', async (t) => {
const dir = testCorestoreDir('sh-policy-sandbox')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('ppolicysandbox'))
await drive.ready()
await personal.ready()
await drive.put('/bin/echo', b4a.from(`async function run(ctx, argv){ ctx.console.log(argv.slice(1).join(' ')) }`))
const logs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (m) => logs.push(String(m)),
error: (...a) => logs.push(a.join(' '))
}
ctx.vfs.env.BARE_OS_SHELL_DENY_COMMANDS = 'echo'
await execShellLine(ctx, 'echo hi')
t.is(ctx.exitCode, 126)
t.ok(logs.some((l) => l.includes('command denied by policy')))
delete ctx.vfs.env.BARE_OS_SHELL_DENY_COMMANDS
ctx.vfs.env.BARE_OS_SHELL_SANDBOX = '1'
await execShellLine(ctx, 'echo hi')
t.is(ctx.exitCode, 126)
t.ok(logs.some((l) => l.includes('sandbox blocks external command')))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine unsafe redirect guard rejects risky paths', async (t) => {
const dir = testCorestoreDir('sh-redirect-guard')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('predirectguard'))
await drive.ready()
await personal.ready()
await drive.put('/bin/echo', b4a.from(`async function run(ctx, argv){ ctx.console.log(argv.slice(1).join(' ')) }`))
const logs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (m) => logs.push(String(m)),
error: (...a) => logs.push(a.join(' '))
}
ctx.vfs.env.BARE_OS_SHELL_REDIRECT_GUARD = '1'
await execShellLine(ctx, 'echo hi > /proc/self/environ')
t.is(ctx.exitCode, 1)
t.ok(logs.some((l) => l.includes('unsafe stdout redirect path denied')))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine redirect write to /proc/cpuinfo is visible nonzero', async (t) => {
const dir = testCorestoreDir('sh-proc-write-deny')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pprocwrite'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/echo',
b4a.from(`async function run(ctx, argv){ ctx.console.log(argv.slice(1).join(' ')) }`)
)
const logs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (m) => logs.push(String(m)),
error: (...a) => logs.push(a.join(' '))
}
await execShellLine(ctx, 'echo hi > /proc/cpuinfo')
t.ok(ctx.exitCode !== 0)
t.ok(logs.some((l) => /EACCES|EROFS|denied|read-only/i.test(l)))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine appends structured shell audit events', async (t) => {
const dir = testCorestoreDir('sh-audit-events')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pauditevents'))
await drive.ready()
await personal.ready()
await drive.put('/bin/echo', b4a.from(`async function run(ctx, argv){ ctx.console.log(argv.slice(1).join(' ')) }`))
const ctx = testCtx(drive, personal)
await execShellLine(ctx, 'echo hello')
t.ok(Array.isArray(ctx.shellAuditEvents))
t.ok(ctx.shellAuditEvents.some((e) => e.event === 'shell.command.start'))
t.ok(ctx.shellAuditEvents.some((e) => e.event === 'shell.command.finish'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine errexit stops inside if-then body after failure', async (t) => {
const dir = testCorestoreDir('errexit-if')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pex-eif'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/false',
b4a.from(`async function run(ctx) { ctx.exitCode = 1 }`)
)
await drive.put(
'/bin/echo',
b4a.from(
`async function run(ctx, argv) { ctx.console.log(argv.slice(1).join(' ')) }`
)
)
const logs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (m) => logs.push(String(m)),
error: (...a) => logs.push(a.map(String).join(' '))
}
ctx.vfs.env.BARE_OS_SHELL_ERREXIT = '1'
await execShellLine(ctx, 'if true; then false; echo BAD; fi')
t.ok(!logs.some((l) => l.includes('BAD')))
logs.length = 0
ctx.exitCode = 0
await execShellLine(ctx, 'if false; then echo no; fi; echo AFTER')
t.ok(logs.some((l) => l.includes('AFTER')))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('evaluateBareOsPeerAdmission honors dhtAddressClass with allowlist', async (t) => {
const env = {
BARE_OS_PEER_ALLOWLIST_HEX: '',
BARE_OS_PEER_ALLOW_ALL: '1',
BARE_OS_DHT_ADDRESS_CLASS_ALLOWLIST: 'ipv4,relay'
}
const deny = evaluateBareOsPeerAdmission(env, 'aa', {
dhtAddressClass: 'ipv6'
})
t.is(deny.schema, 2)
t.is(deny.verdict, 'deny')
const ok = evaluateBareOsPeerAdmission(env, 'aa', { dhtAddressClass: 'ipv4' })
t.is(ok.schema, 2)
t.is(ok.verdict, 'allow')
})
test('evaluateBareOsPeerAdmission denylist wins over allowlist', async (t) => {
const env = {
BARE_OS_PEER_ALLOWLIST_HEX: 'aa,bb',
BARE_OS_PEER_DENYLIST_HEX: 'bb'
}
t.is(evaluateBareOsPeerAdmission(env, 'bb').verdict, 'deny')
t.is(evaluateBareOsPeerAdmission(env, 'bb').reason, 'peer_denylist')
t.is(evaluateBareOsPeerAdmission(env, 'aa').verdict, 'allow')
})
test('evaluateBareOsPeerAdmission strict empty allowlist denies all peers', async (t) => {
const env = {
BARE_OS_PEER_ALLOWLIST_HEX: '',
BARE_OS_PEER_ALLOWLIST_STRICT: '1'
}
const r = evaluateBareOsPeerAdmission(env, 'abc')
t.is(r.verdict, 'deny')
t.ok(String(r.note || '').includes('allowlist is empty'))
})
test('evaluateBareOsPeerAdmission BARE_OS_PEER_REQUIRE_CAPS_JSON', async (t) => {
const env = {
BARE_OS_PEER_ALLOW_ALL: '1',
BARE_OS_PEER_REQUIRE_CAPS_JSON: '["seed","read"]'
}
t.is(
evaluateBareOsPeerAdmission(env, 'k1', { caps: ['seed'] }).verdict,
'deny'
)
t.is(
evaluateBareOsPeerAdmission(env, 'k1', { caps: ['seed', 'read'] })
.verdict,
'allow'
)
const denied = evaluateBareOsPeerAdmission(env, 'k1', {
caps: ['seed'],
dhtAddressClass: 'relay'
})
t.is(denied.reason, 'peer_missing_cap')
t.alike(denied.requiredCaps, ['seed', 'read'])
})
test('evaluateBareOsPeerAdmission denylist overrides satisfied require_caps', async (t) => {
const env = {
BARE_OS_PEER_DENYLIST_HEX: 'dead',
BARE_OS_PEER_REQUIRE_CAPS_JSON: '["seed"]'
}
const r = evaluateBareOsPeerAdmission(env, 'dead', { caps: ['seed'] })
t.is(r.verdict, 'deny')
t.is(r.reason, 'peer_denylist')
})
test('bareOsFormatPeerAdmissionAuditEvent never embeds full peer keys', (t) => {
const full = 'a'.repeat(64)
const res = evaluateBareOsPeerAdmission({}, full)
const ev = bareOsFormatPeerAdmissionAuditEvent(full, res, {
dhtAddressClass: 'ipv4'
})
t.is(ev.peerKeyHexPrefix, 'a'.repeat(16))
t.absent(ev.peerKeyHex)
t.is(ev.type, 'peer_admission')
t.is(ev.dhtAddressClass, 'ipv4')
})
test('bareOsPeerAdmissionAuditRateAllow throttles per bucket', (t) => {
const m = new Map()
t.ok(bareOsPeerAdmissionAuditRateAllow('b', 1000, 100, m))
t.absent(bareOsPeerAdmissionAuditRateAllow('b', 1050, 100, m))
t.ok(bareOsPeerAdmissionAuditRateAllow('b', 1100, 100, m))
})
test('bareOsDrainFcntlLockWaiters wakes FIFO waiters per path', (t) => {
const lockPath = '/coop/lockfifo'
const slot = { readers: new Set(), writer: 'holder1' }
const c = {
bareOsCooperativeLocks: { [lockPath]: slot },
bareOsFcntlLockWaitQueues: { [lockPath]: [] }
}
const q = c.bareOsFcntlLockWaitQueues[lockPath]
const order = []
q.push({
resolve: () => {
order.push('w2')
},
timer: null,
owner: 'sess2',
wantWrite: true
})
q.push({
resolve: () => {
order.push('w3')
},
timer: null,
owner: 'sess3',
wantWrite: true
})
slot.writer = null
bareOsDrainFcntlLockWaiters(c, lockPath)
t.alike(order, ['w2'])
t.is(slot.writer, 'sess2')
slot.writer = null
bareOsDrainFcntlLockWaiters(c, lockPath)
t.alike(order, ['w2', 'w3'])
t.is(slot.writer, 'sess3')
})
test('BARE_OS_CTX_API_VERSION is semver-shaped', async (t) => {
t.ok(/^\d+\.\d+\.\d+$/.test(BARE_OS_CTX_API_VERSION))
})
test('getBareOsPipelineLimits reads BARE_OS_PIPELINE_* from env', async (t) => {
const def = getBareOsPipelineLimits({})
t.is(def.maxStages, DEFAULT_PIPELINE_MAX_STAGES)
const custom = getBareOsPipelineLimits({
BARE_OS_PIPELINE_MAX_STAGES: '4',
BARE_OS_PIPELINE_MAX_BYTES: '100',
BARE_OS_PIPELINE_MAX_LINES: '20'
})
t.is(custom.maxStages, 4)
t.is(custom.maxBytes, 100)
t.is(custom.maxLines, 20)
})
test('buildBareOsRuntimeCaps matches ctx API version and pipeline env', async (t) => {
const caps = buildBareOsRuntimeCaps({
BARE_OS_PIPELINE_MAX_STAGES: '8',
BARE_OS_CTX_API_VERSION
})
t.is(caps.ctxApiVersion, BARE_OS_CTX_API_VERSION)
t.is(caps.pipeline.maxStages, 8)
t.ok(Array.isArray(caps.pseudoFsPaths))
t.ok(caps.pseudoFsPaths.includes('/proc/version'))
t.ok(caps.pseudoFsPaths.includes('/run/bare-os/boot_profile'))
t.ok(caps.pseudoFsPaths.includes('/run/bare-os/ipc'))
t.ok(caps.pseudoFsPaths.includes('/run/bare-os/session'))
t.ok(caps.pseudoFsPaths.includes('/proc/mounts'))
t.ok(caps.pseudoFsPaths.includes('/proc/bare_os_resources'))
t.ok(caps.pseudoFsPaths.includes('/run/bare-os/virtual'))
t.ok(caps.pseudoFsPaths.includes('/run/bare-os/unit-journal'))
t.ok(caps.pseudoFsPaths.includes('/snapshots'))
t.ok(caps.pseudoFsPaths.includes('/snapshots/system'))
t.is(caps.features.simulatedPipelines, true)
t.is(caps.features.httpDelegate, true)
t.is(caps.features.gitDelegate, true)
t.is(caps.features.systemctlDelegate, true)
t.is(caps.features.bareCtxModules, true)
t.is(caps.features.bareDriveBundles, true)
t.is(caps.features.bareHostImportsForCtx, true)
const capsOff = buildBareOsRuntimeCaps({ BARE_OS_BARE_MODULES: '0' })
t.is(capsOff.features.bareCtxModules, false)
t.is(capsOff.features.bareDriveBundles, false)
t.is(capsOff.features.bareHostImportsForCtx, false)
const capsIso = buildBareOsRuntimeCaps({ BARE_OS_BARE_HOST_IMPORTS: '0' })
t.is(capsIso.features.bareHostImportsForCtx, false)
t.is(capsIso.features.bareCtxModules, true)
})
test('bareOsBareModulesEnabled respects BARE_OS_BARE_MODULES', async (t) => {
t.ok(bareOsBareModulesEnabled({}))
t.ok(!bareOsBareModulesEnabled({ BARE_OS_BARE_MODULES: '0' }))
t.ok(!bareOsBareModulesEnabled({ BARE_OS_BARE_MODULES: 'false' }))
})
test('loadBareModuleManifest has entries', async (t) => {
const m = loadBareModuleManifest()
t.ok(m.version >= 1)
t.ok(Array.isArray(m.entries))
t.ok(m.entries.some((e) => e.ctxKey === 'b4a'))
})
function stableStringifyManifest(obj) {
if (obj === null || typeof obj !== 'object') return JSON.stringify(obj)
if (Array.isArray(obj)) {
return '[' + obj.map((x) => stableStringifyManifest(x)).join(',') + ']'
}
const keys = Object.keys(obj).sort()
return (
'{' +
keys
.map((k) => JSON.stringify(k) + ':' + stableStringifyManifest(obj[k]))
.join(',') +
'}'
)
}
test('bare-module-manifest.data.mjs matches bare-module-manifest.json', async (t) => {
const dir = path.dirname(fileURLToPath(import.meta.url))
const jsonPath = path.join(dir, 'lib', 'ctx', 'bare-module-manifest.json')
const fromJson = JSON.parse(await readFile(jsonPath, 'utf8'))
const embedded = bareOsBareModuleManifestEmbeddedRef()
t.is(stableStringifyManifest(fromJson), stableStringifyManifest(embedded))
})
test('buildBareCtxObjectFromHost loads core keys on Node', async (t) => {
const target = {}
await buildBareCtxObjectFromHost(
{
// Narrow host import set: parallel import of the full manifest is slow and
// some Bare-oriented packages disturb brittle's hrtime-based timers.
BARE_OS_BARE_HOST_ONLY_CTX_KEYS: 'b4a,compactEncoding,protomux'
},
target
)
t.ok(target.b4a)
t.ok(target.compactEncoding)
t.ok(target.protomux)
})
test('buildPearCtxObjectFromHost loads pear tier keys on Node', async (t) => {
const target = {}
await buildPearCtxObjectFromHost({}, target)
t.ok(target.pearBuild, 'pearBuild')
t.ok(target.bareBundleCompile, 'bareBundleCompile')
t.ok(target.bareBundleEvaluate, 'bareBundleEvaluate')
// pear-bundle and pear-ref are Pear-runtime-only; host stub skips them on Node.
})
test('maybeMergeBareFromDrive fills missing keys from bundle (mock vfs)', async (t) => {
const repoRoot = path.join(
fileURLToPath(new URL('.', import.meta.url)),
'..',
'..'
)
const bundleAbs = path.join(repoRoot, 'kernel/lib/bare/bundles/b4a.js')
const bundleSrc = await readFile(bundleAbs)
const manifest = {
version: 1,
bundles: [{ path: '/lib/bare/bundles/b4a.js', keys: ['b4a'] }]
}
const target = {}
const vfs = {
async readFile(p) {
if (p === '/lib/bare/manifest.json')
return b4a.from(JSON.stringify(manifest))
if (p === '/lib/bare/bundles/b4a.js') return new Uint8Array(bundleSrc)
return null
}
}
await maybeMergeBareFromDrive({}, vfs, target)
t.ok(target.b4a)
})
test('expandArgvAliases expands first word and keeps trailing argv', async (t) => {
t.alike(expandArgvAliases(['ll', 'z'], defaultShellAliases()), [
'ls',
'-la',
'z'
])
})
test('expandArgvAliases leaves sed unchanged', async (t) => {
t.alike(
expandArgvAliases(['sed', 's/a/b/', 'x.txt'], defaultShellAliases()),
['sed', 's/a/b/', 'x.txt']
)
})
test('defaultShellAliases does not remap sed', async (t) => {
t.is(defaultShellAliases().sed, undefined)
})
test('defaultShellAliases top maps to baretop', async (t) => {
t.is(defaultShellAliases().top, 'baretop')
})
test('defaultShellAliases btop maps to baretop', async (t) => {
t.is(defaultShellAliases().btop, 'baretop')
})
test('defaultShellAliases nano maps to edit', async (t) => {
t.is(defaultShellAliases().nano, 'edit')
t.alike(expandArgvAliases(['nano', 'x'], defaultShellAliases()), [
'edit',
'x'
])
})
test('expandArgvAliases throws on cyclic alias chain', async (t) => {
const cyclic = { a: 'b', b: 'a' }
t.exception(
() => expandArgvAliases(['a'], cyclic),
/alias: expansion nested too deeply/
)
})
test('loadBarerc applies export and alias from personal ~/.barerc', async (t) => {
const dir = testCorestoreDir('barerc')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('brc'))
await drive.ready()
await personal.ready()
await personal.put(
personalHomeBacking('/home/user', '.barerc'),
b4a.from('export MYRC=1\nalias dog=echo woof\n')
)
const ctx = testCtx(drive, personal)
await loadBarerc(ctx)
t.is(ctx.vfs.env.MYRC, '1')
t.is(ctx.shellAliases.dog, 'echo woof')
t.is(ctx.shellAliases.ll, 'ls -la')
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('loadBarerc createSkeletonIfMissing writes ~/.barerc when absent', async (t) => {
const dir = testCorestoreDir('barercskel')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('brcsk'))
await drive.ready()
await personal.ready()
const ctx = testCtx(drive, personal)
await loadBarerc(ctx, { createSkeletonIfMissing: true })
const back = await ctx.vfs.readFile('~/.barerc')
t.ok(back)
t.is(ctx.b4a.toString(back), BARERC_SKELETON)
t.is(ctx.shellAliases.ll, 'ls -la')
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('loadBarerc theme nord sets REPL color env and LS_COLORS', async (t) => {
const dir = testCorestoreDir('barerctheme')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('brcth'))
await drive.ready()
await personal.ready()
await personal.put(
personalHomeBacking('/home/user', '.barerc'),
b4a.from('theme nord\n')
)
const ctx = testCtx(drive, personal)
await loadBarerc(ctx)
t.is(ctx.vfs.env.BARE_OS_THEME, 'nord')
t.ok(
String(ctx.vfs.env.BARE_OS_COLOR_PROMPT || '').includes('38;2;'),
'nord preset uses truecolor prompt'
)
t.ok(
String(ctx.vfs.env.LS_COLORS || '').includes('di='),
'LS_COLORS set from preset'
)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine barerc reload reapplies barerc', async (t) => {
const dir = testCorestoreDir('barercrel')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('brcrel'))
await drive.ready()
await personal.ready()
await personal.put(
personalHomeBacking('/home/user', '.barerc'),
b4a.from('export MARK=before\n')
)
const ctx = testCtx(drive, personal)
await loadBarerc(ctx)
t.is(ctx.vfs.env.MARK, 'before')
await personal.put(
personalHomeBacking('/home/user', '.barerc'),
b4a.from('export MARK=after\n')
)
await execShellLine(ctx, 'barerc reload')
t.is(ctx.vfs.env.MARK, 'after')
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('bareParseLsColors roundtrip', async (t) => {
const s = 'di=01;34:ln=36:ex=32:*.tar=01;31'
const m = bareParseLsColors(s)
t.is(m.di, '01;34')
t.is(m.ln, '36')
t.alike(bareParseLsColors(bareSerializeLsColors(m)), m)
})
test('bareLsColorOpenSgrFromMap directory and glob', async (t) => {
const map = bareParseLsColors('di=01;34:*.md=00;32')
const d = bareLsColorOpenSgrFromMap(
{ type: 'directory', mode: 0o040755 },
'foo',
map
)
t.ok(d.startsWith('\x1b['), 'directory colored')
const md = bareLsColorOpenSgrFromMap(
{ type: 'file', mode: 0o100644 },
'README.md',
map
)
t.ok(md.includes('32'), 'markdown glob')
})
test('bareLsColorOpenSgrFromMap mh for multi-link regular file', async (t) => {
const map = bareParseLsColors('mh=01;44:fi=40;31')
const one = bareLsColorOpenSgrFromMap(
{ type: 'file', mode: 0o100644, nlink: 1 },
'a',
map
)
const two = bareLsColorOpenSgrFromMap(
{ type: 'file', mode: 0o100644, nlink: 2 },
'a',
map
)
t.ok(two.includes('44'), 'nlink>1 uses mh SGR')
t.ok(one.includes('31'), 'nlink 1 uses fi')
})
test('bareLsColorOpenSgrFromMap ca when stat has capabilities', async (t) => {
const map = bareParseLsColors('ca=30;41:fi=00')
const sgr = bareLsColorOpenSgrFromMap(
{ type: 'file', mode: 0o100644, nlink: 1, capabilities: true },
'cap',
map
)
t.ok(sgr.includes('41'), 'capabilities use ca')
})
test('applyBareOsThemeFromEnv BARE_OS_COLOR_DEPTH=256 drops truecolor', async (t) => {
const ctx = {
vfs: { env: { BARE_OS_THEME: 'nord', BARE_OS_COLOR_DEPTH: '256' } }
}
await applyBareOsThemeFromEnv(ctx)
const p = String(ctx.vfs.env.BARE_OS_COLOR_PROMPT || '')
t.ok(p.includes('38;5;'), '256-color palette index SGR')
t.ok(!p.includes('38;2;'), 'no RGB truecolor')
})
test('dircolors -p includes TERM and di', async (t) => {
const db = bareDefaultDircolorsDatabase()
t.ok(db.includes('TERM'))
t.ok(db.includes('di '))
})
test('bareParseDircolorsDatabase TERM block', async (t) => {
const text = 'TERM xterm\ndi 01;34\nTERM none\nfi 00\nTERM *\nln 01;36\n'
const m = bareParseDircolorsDatabase(text, 'xterm')
t.is(m.di, '01;34')
t.is(m.ln, '01;36')
})
test('runBinCommand theme set writes barerc and updates env', async (t) => {
const themePath = path.join(__dirname, '../../kernel/bin/theme')
const themeSrc = await readFile(themePath, 'utf8')
const dir = testCorestoreDir('themecmd')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('thcmd'))
await drive.ready()
await personal.ready()
await drive.put('/bin/theme', b4a.from(themeSrc))
const ctx = testCtx(drive, personal)
await loadBarerc(ctx)
await runBinCommand(ctx, ['theme', 'set', 'dracula'])
t.is(ctx.vfs.env.BARE_OS_THEME, 'dracula')
const barc = ctx.b4a.toString(await ctx.vfs.readFile('~/.barerc'))
t.ok(barc.includes('theme dracula'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand dircolors -p prints database', async (t) => {
const dcPath = path.join(__dirname, '../../kernel/bin/dircolors')
const dcSrc = await readFile(dcPath, 'utf8')
const dir = testCorestoreDir('dircolp')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('dcp'))
await drive.ready()
await personal.ready()
await drive.put('/bin/dircolors', b4a.from(dcSrc))
const lines = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (s) => lines.push(String(s)),
error: (...a) => lines.push(a.join(' '))
}
await runBinCommand(ctx, ['dircolors', '-p'])
const out = lines.join('\n')
t.ok(out.includes('TERM'))
t.ok(out.includes('di'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand edit --help prints usage', async (t) => {
const editPath = path.join(__dirname, '../../kernel/bin/edit')
const editSrc = await readFile(editPath, 'utf8')
const dir = testCorestoreDir('edithlp')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('edh'))
await drive.ready()
await personal.ready()
await drive.put('/bin/edit', b4a.from(editSrc))
const lines = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (s) => lines.push(String(s)),
error: (...a) => lines.push(a.join(' '))
}
await runBinCommand(ctx, ['edit', '--help'])
const out = lines.join('\n')
t.ok(out.includes('usage:'))
t.ok(out.includes('TTY'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand edit requires TTY', async (t) => {
const editPath = path.join(__dirname, '../../kernel/bin/edit')
const editSrc = await readFile(editPath, 'utf8')
const dir = testCorestoreDir('ednotty')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('ednt'))
await drive.ready()
await personal.ready()
await drive.put('/bin/edit', b4a.from(editSrc))
const lines = []
const ctx = testCtx(drive, personal)
ctx.replStdin = { isTTY: false }
ctx.console = {
log: (s) => lines.push(String(s)),
error: (...a) => lines.push(a.join(' '))
}
await runBinCommand(ctx, ['edit', 'x.txt'])
t.is(ctx.exitCode, 1)
t.ok(lines.some((l) => l.includes('TTY')))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand baretop --help prints usage', async (t) => {
const binPath = path.join(__dirname, '../../kernel/bin/baretop')
const src = await readFile(binPath, 'utf8')
const dir = testCorestoreDir('baretophlp')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('bth'))
await drive.ready()
await personal.ready()
await drive.put('/bin/baretop', b4a.from(src))
const lines = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (s) => lines.push(String(s)),
error: (...a) => lines.push(a.join(' '))
}
await runBinCommand(ctx, ['baretop', '--help'])
const out = lines.join('\n')
t.ok(out.includes('usage:'))
t.ok(out.includes('TTY'))
t.ok(out.toLowerCase().includes('dashboard'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand baretop requires TTY', async (t) => {
const binPath = path.join(__dirname, '../../kernel/bin/baretop')
const src = await readFile(binPath, 'utf8')
const dir = testCorestoreDir('baretopntty')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('btnt'))
await drive.ready()
await personal.ready()
await drive.put('/bin/baretop', b4a.from(src))
const lines = []
const ctx = testCtx(drive, personal)
ctx.replStdin = { isTTY: false }
ctx.console = {
log: (s) => lines.push(String(s)),
error: (...a) => lines.push(a.join(' '))
}
await runBinCommand(ctx, ['baretop'])
t.is(ctx.exitCode, 1)
t.ok(lines.some((l) => l.includes('TTY')))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand nano --help matches edit bundle', async (t) => {
const nanoPath = path.join(__dirname, '../../kernel/bin/nano')
const nanoSrc = await readFile(nanoPath, 'utf8')
const dir = testCorestoreDir('nanohlp')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('nanh'))
await drive.ready()
await personal.ready()
await drive.put('/bin/nano', b4a.from(nanoSrc))
const lines = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (s) => lines.push(String(s)),
error: (...a) => lines.push(a.join(' '))
}
await runBinCommand(ctx, ['nano', '-h'])
const out = lines.join('\n')
t.ok(out.includes('usage:'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('tokenize leaves echo 2 > file as stdout redirect not 2>', async (t) => {
const toks = tokenize('echo 2 > /tmp/x')
const words = toks.filter((x) => x.type === 'word').map((x) => x.value)
t.ok(words.includes('2'))
t.ok(toks.some((x) => x.type === 'op' && x.value === '>'))
t.ok(!toks.some((x) => x.type === 'op' && x.value === '2>'))
})
test('execShellLine stderr 2> and 2>&1 in pipeline', async (t) => {
const dir = testCorestoreDir('sherr')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('serr'))
await drive.ready()
await personal.ready()
const emit = `
async function run(ctx) {
ctx.console.log('OUT')
ctx.console.error('ERR')
}
`
const cat = `
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
async function run(ctx) {
ctx.console.log(bareStdin(ctx).replace(/\\n$/, ''))
}
`
await drive.put('/bin/emit', b4a.from(emit))
await drive.put('/bin/cat', b4a.from(cat))
const ctx = testCtx(drive, personal)
await execShellLine(ctx, 'emit 2>~/e.out')
const eb = await ctx.vfs.readFile('~/e.out')
t.ok(eb && ctx.b4a.toString(eb).includes('ERR'))
t.ok(!ctx.b4a.toString(eb).includes('OUT'))
await ctx.vfs.unlink('~/e.out')
const logs = []
ctx.console = {
log: (...a) => logs.push(['out', ...a]),
error: (...a) => logs.push(['err', ...a])
}
await execShellLine(ctx, 'emit 2>&1 | cat')
const piped = logs
.filter((x) => x[0] === 'out')
.map((x) => x.slice(1).join(' '))
t.ok(piped.some((l) => l.includes('OUT') && l.includes('ERR')))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine pipeline < in and > out redirection', async (t) => {
const dir = testCorestoreDir('shpipeio')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('spio'))
await drive.ready()
await personal.ready()
const cat = `
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
async function run(ctx) {
ctx.console.log(bareStdin(ctx).replace(/\\n$/, ''))
}
`
await drive.put('/bin/cat', b4a.from(cat))
const ctx = testCtx(drive, personal)
await ctx.vfs.writeFile('~/in.txt', b4a.from('pipeline-data\n'))
await execShellLine(ctx, 'cat < ~/in.txt | cat > ~/out.txt')
t.is(ctx.exitCode, 0)
t.is(
b4a.toString(await ctx.vfs.readFile('~/out.txt'), 'utf8'),
'pipeline-data\n'
)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine pipeline first stage stdin < file only', async (t) => {
const dir = testCorestoreDir('shpipein')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('spin'))
await drive.ready()
await personal.ready()
const cat = `
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
async function run(ctx) {
ctx.console.log(bareStdin(ctx).replace(/\\n$/, ''))
}
`
const wcPath = path.join(__dirname, '../../kernel/bin/wc')
const wcSrc = await readFile(wcPath, 'utf8')
await drive.put('/bin/cat', b4a.from(cat))
await drive.put('/bin/wc', b4a.from(wcSrc))
const lines = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (s) => lines.push(String(s)),
error: (...a) => lines.push(a.join(' '))
}
await ctx.vfs.writeFile('~/lines.txt', b4a.from('a\nb\nc\n'))
await execShellLine(ctx, 'wc -l < ~/lines.txt | cat')
t.is(ctx.exitCode, 0)
t.ok(lines.some((l) => /^\s*3\s/.test(l) || l.includes('3')))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine pipeline stage and byte limits', async (t) => {
const dir = testCorestoreDir('shpipe')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('spipe'))
await drive.ready()
await personal.ready()
const echo = `
async function run(ctx, argv) {
ctx.console.log(argv.slice(1).join(' '))
}
`
const cat = `
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
async function run(ctx) {
ctx.console.log(bareStdin(ctx).replace(/\\n$/, ''))
}
`
await drive.put('/bin/echo', b4a.from(echo))
await drive.put('/bin/cat', b4a.from(cat))
const spam = `
async function run(ctx) {
ctx.console.log('z'.repeat(200))
}
`
await drive.put('/bin/spam', b4a.from(spam))
const logs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (...a) => logs.push(a.join(' ')),
error: (...a) => logs.push(a.join(' '))
}
ctx.env.BARE_OS_PIPELINE_MAX_STAGES = '2'
await execShellLine(ctx, 'echo a | echo b | echo c')
t.is(ctx.exitCode, 1)
t.ok(logs.some((l) => l.includes('BARE_OS_PIPELINE_MAX_STAGES')))
logs.length = 0
ctx.env.BARE_OS_PIPELINE_MAX_STAGES = '32'
ctx.env.BARE_OS_PIPELINE_MAX_BYTES = '80'
await execShellLine(ctx, 'spam | cat')
t.is(ctx.exitCode, 1)
t.ok(logs.some((l) => l.includes('BARE_OS_PIPELINE_MAX_BYTES')))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine pipeline stage timeout aborts stalled stage safely', async (t) => {
const dir = testCorestoreDir('pipestagetimeout')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('ppipestagetimeout'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/hang',
b4a.from(
`async function run(){ await new Promise((resolve)=>setTimeout(resolve,200)); }`
)
)
await drive.put(
'/bin/echo',
b4a.from(`async function run(ctx, argv){ ctx.console.log(argv.slice(1).join(' ')) }`)
)
const logs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (...a) => logs.push(a.join(' ')),
error: (...a) => logs.push(a.join(' '))
}
ctx.vfs.env.BARE_OS_SHELL_PIPELINE_STAGE_TIMEOUT_MS = '20'
await execShellLine(ctx, 'hang | echo never')
t.ok(logs.some((l) => l.includes('pipeline stage timeout')))
t.is(ctx.exitCode, 1)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('ls bareOsStdoutCaptured lists one name per line', async (t) => {
const lsPath = path.join(__dirname, '../../kernel/bin/ls')
const lsSrc = await readFile(lsPath, 'utf8')
const dir = testCorestoreDir('lscap')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('plcap'))
await drive.ready()
await personal.ready()
await drive.put('/bin/ls', b4a.from(lsSrc))
const lines = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (s) => lines.push(String(s)),
error: (...a) => lines.push(a.join(' '))
}
await ctx.vfs.writeFile('aaa', b4a.from(''))
await ctx.vfs.writeFile('lib', b4a.from(''))
await ctx.vfs.writeFile('zzz', b4a.from(''))
await runBinCommand(Object.assign({}, ctx, { bareOsStdoutCaptured: true }), [
'ls'
])
t.is(lines.length, 3)
t.ok(lines.includes('aaa') && lines.includes('lib') && lines.includes('zzz'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine ls pipe grep prints only matching entry line', async (t) => {
const lsPath = path.join(__dirname, '../../kernel/bin/ls')
const grepPath = path.join(__dirname, '../../kernel/bin/grep')
const lsSrc = await readFile(lsPath, 'utf8')
const grepSrc = await readFile(grepPath, 'utf8')
const dir = testCorestoreDir('lspipegrep')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('plpg'))
await drive.ready()
await personal.ready()
await drive.put('/bin/ls', b4a.from(lsSrc))
await drive.put('/bin/grep', b4a.from(grepSrc))
const lines = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (s) => lines.push(String(s)),
error: (...a) => lines.push(a.join(' '))
}
await ctx.vfs.writeFile('aaa', b4a.from(''))
await ctx.vfs.writeFile('lib', b4a.from(''))
await ctx.vfs.writeFile('zzz', b4a.from(''))
await execShellLine(ctx, 'ls | grep -F lib')
t.is(ctx.exitCode, 0)
t.alike(lines, ['lib'])
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine ls pipe wc -l counts lines', async (t) => {
const lsPath = path.join(__dirname, '../../kernel/bin/ls')
const wcPath = path.join(__dirname, '../../kernel/bin/wc')
const lsSrc = await readFile(lsPath, 'utf8')
const wcSrc = await readFile(wcPath, 'utf8')
const dir = testCorestoreDir('lspipewc')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('plwc'))
await drive.ready()
await personal.ready()
await drive.put('/bin/ls', b4a.from(lsSrc))
await drive.put('/bin/wc', b4a.from(wcSrc))
const lines = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (s) => lines.push(String(s)),
error: (...a) => lines.push(a.join(' '))
}
await ctx.vfs.writeFile('a', b4a.from(''))
await ctx.vfs.writeFile('b', b4a.from(''))
await ctx.vfs.writeFile('c', b4a.from(''))
await execShellLine(ctx, 'ls | wc -l')
t.is(ctx.exitCode, 0)
const out = lines.join('\n').trim()
t.is(out, '3', 'three directory entries => three lines')
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine runs cd and external', async (t) => {
const dir = testCorestoreDir('sh')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('psh'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/xy',
b4a.from(`
async function run(ctx, argv) {
ctx.got.push(argv.join(' '))
}
`)
)
const got = []
const ctx = testCtx(drive, personal)
ctx.got = got
await execShellLine(ctx, 'cd /bin')
t.is(ctx.vfs.getcwd(), '/bin')
await execShellLine(ctx, 'xy one two')
t.is(got[0], 'xy one two')
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine expands default ll to ls -la', async (t) => {
const dir = testCorestoreDir('llalias')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('lla'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/ls',
b4a.from(`
async function run(ctx, argv) {
ctx.got.push(argv.join(' '))
}
`)
)
const got = []
const ctx = testCtx(drive, personal)
ctx.got = got
await execShellLine(ctx, 'll one')
t.is(got[0], 'ls -la one')
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('tokenize && || ; and split helpers', async (t) => {
const toks = tokenize('a&&b||c;d')
t.is(toks.map((x) => x.value).join(' '), 'a && b || c ; d')
const lists = splitTokensBySemicolon(toks)
t.is(lists.length, 2)
const { segments, ops } = splitTokensByAndOr(lists[0])
t.is(segments.length, 3)
t.is(ops.join(' '), '&& ||')
})
test('execShellLine && || ; short-circuit and exitCode', async (t) => {
const dir = testCorestoreDir('shandor')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('sand'))
await drive.ready()
await personal.ready()
const stub = `
async function run(ctx, argv) {
const c = argv[0]
ctx.ran.push(c)
ctx.exitCode = c === 'false' ? 1 : 0
}
`
await drive.put('/bin/true', b4a.from(stub))
await drive.put('/bin/false', b4a.from(stub))
await drive.put(
'/bin/rec',
b4a.from(`
async function run(ctx, argv) {
ctx.ran.push('rec')
ctx.exitCode = 0
}
`)
)
const ran = []
const ctx = testCtx(drive, personal)
ctx.ran = ran
ctx.exitCode = 0
await execShellLine(ctx, 'false && rec')
t.is(ran.join(','), 'false')
t.is(ctx.exitCode, 1)
ran.length = 0
ctx.exitCode = 0
await execShellLine(ctx, 'false || rec')
t.is(ran.join(','), 'false,rec')
t.is(ctx.exitCode, 0)
ran.length = 0
ctx.exitCode = 0
await execShellLine(ctx, 'true && rec')
t.is(ran.join(','), 'true,rec')
t.is(ctx.exitCode, 0)
ran.length = 0
ctx.exitCode = 0
await execShellLine(ctx, 'true || rec')
t.is(ran.join(','), 'true')
t.is(ctx.exitCode, 0)
ran.length = 0
ctx.exitCode = 0
await execShellLine(ctx, 'false || false || rec')
t.is(ran.join(','), 'false,false,rec')
t.is(ctx.exitCode, 0)
ran.length = 0
ctx.exitCode = 0
await execShellLine(ctx, 'true && false || rec')
t.is(ran.join(','), 'true,false,rec')
t.is(ctx.exitCode, 0)
ran.length = 0
ctx.exitCode = 0
await execShellLine(ctx, 'true; rec')
t.is(ran.join(','), 'true,rec')
await drive.put(
'/bin/last',
b4a.from(`
async function run(ctx, argv) {
ctx.ran.push('last')
ctx.exitCode = 0
}
`)
)
ran.length = 0
ctx.exitCode = 0
await execShellLine(ctx, 'false | last')
t.is(ran.join(','), 'false,last', 'pipeline runs both stages')
t.is(ctx.exitCode, 0, 'POSIX-like: pipeline exit status is last stage')
ran.length = 0
ctx.exitCode = 0
await execShellLine(ctx, 'BARE_OS_SHELL_PIPEFAIL=1 false | last')
t.is(ran.join(','), 'false,last')
t.is(
ctx.exitCode,
1,
'pipefail: failure in an earlier stage overrides last stage success'
)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('execShellLine if then else fi', async (t) => {
const dir = testCorestoreDir('shif')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('sif'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/echo',
b4a.from(`
async function run(ctx, argv) {
ctx.console.log(argv.slice(1).join(' '))
}
`)
)
await drive.put(
'/bin/true',
b4a.from(`
async function run(ctx) {
ctx.exitCode = 0
}
`)
)
await drive.put(
'/bin/false',
b4a.from(`
async function run(ctx) {
ctx.exitCode = 1
}
`)
)
const lines = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (s) => lines.push(String(s)),
error: (...a) => lines.push(a.join(' '))
}
await execShellLine(ctx, 'if false; then echo no; else echo yes; fi')
t.ok(lines.some((l) => l.includes('yes')))
lines.length = 0
await execShellLine(ctx, 'if true; then echo ok; fi')
t.ok(lines.some((l) => l.includes('ok')))
lines.length = 0
await execShellLine(ctx, 'if false; then echo x; fi')
t.ok(!lines.some((l) => l.includes('x')))
ctx.exitCode = 0
await execShellLine(ctx, 'n=0; if true; then n=$((n+1)); fi; echo $n')
t.is(ctx.exitCode, 0)
t.ok(lines.some((l) => l.includes('1')))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('filenameFromContentDisposition parses attachment names', async (t) => {
t.is(
filenameFromContentDisposition('attachment; filename="a b.txt"'),
'a b.txt'
)
t.is(
filenameFromContentDisposition("attachment; filename*=UTF-8''x%20y.bin"),
'x y.bin'
)
t.is(filenameFromContentDisposition(null), null)
})
test('cron fieldMatches and dowFieldMatches', async (t) => {
t.ok(fieldMatches('*', 0, 0, 59))
t.ok(fieldMatches('*/5', 10, 0, 59))
t.ok(!fieldMatches('*/5', 11, 0, 59))
t.ok(fieldMatches('1-3', 2, 0, 59))
t.ok(!fieldMatches('1-3', 4, 0, 59))
t.ok(fieldMatches('1,4', 1, 0, 59))
t.ok(fieldMatches('1,4', 4, 0, 59))
t.ok(fieldMatches('1-10/2', 3, 0, 59))
t.ok(!fieldMatches('1-10/2', 4, 0, 59))
t.ok(dowFieldMatches('7', 0))
t.ok(!dowFieldMatches('7', 1))
t.ok(dowFieldMatches('0', 0))
t.ok(dowFieldMatches('1-5', 3))
})
test('cron parseCronLine and jobMatchesDate', async (t) => {
t.absent(parseCronLine(''))
t.absent(parseCronLine('# comment'))
t.absent(parseCronLine('0 0 * *'))
const j = parseCronLine('30 14 15 6 * echo hello world')
t.ok(j)
t.is(j.command, 'echo hello world')
const when = new Date(2020, 5, 15, 14, 30, 0)
t.ok(jobMatchesDate(j, when))
t.ok(!jobMatchesDate(j, new Date(2020, 5, 15, 14, 31, 0)))
const orJob = parseCronLine('0 12 15 * 1 echo dom-or-mon')
t.ok(orJob)
t.ok(
jobMatchesDate(orJob, new Date(2020, 5, 15, 12, 0, 0)),
'15th matches when dow also set (OR)'
)
t.ok(
jobMatchesDate(orJob, new Date(2020, 5, 8, 12, 0, 0)),
'Monday matches when dom also set (OR)'
)
t.ok(!jobMatchesDate(orJob, new Date(2020, 5, 16, 12, 0, 0)), 'Tue 16th no match')
const reboot = parseCronLine('@reboot echo hi')
t.ok(reboot)
t.is(reboot.minute, '@reboot')
t.is(reboot.command, 'echo hi')
t.ok(!jobMatchesDate(reboot, when))
const jit = parseCronLine('@reboot JitterSec=12 /bin/true')
t.ok(jit)
t.is(jit.jitterSec, 12)
t.is(jit.command, '/bin/true')
const jl = parseCronLine('* * * * * JitterSec=3 echo x')
t.ok(jl)
t.is(jl.jitterSec, 3)
t.is(jl.command, 'echo x')
})
test('systemctl list after startBareInitd shows kernel-logger and bare-cron', async (t) => {
const dir = testCorestoreDir('initctl')
const store = new Corestore(dir)
const sys = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pinit'))
await sys.ready()
await personal.ready()
const logs = []
const ctx = testCtx(sys, personal)
ctx.execLine = async () => {}
await startBareInitd(ctx)
const dagRaw = getLastBareInitdDagSnapshotJson()
const dag = JSON.parse(String(dagRaw).trim())
t.ok(dag && dag.supervision && dag.supervision.schema === 1)
t.ok(Array.isArray(dag.supervision.restartKeys))
ctx.console = {
log: (...a) => logs.push(a.join(' ')),
error: (...a) => logs.push(a.join(' '))
}
ctx.exitCode = 0
await runBinCommand(ctx, ['systemctl', 'list'])
t.is(ctx.exitCode, 0)
const text = logs.join('\n')
t.ok(text.includes('kernel-logger'))
t.ok(text.includes('bare-cron'))
t.ok(text.includes('bare-openssh'))
stopBareInitd()
await store.close()
rmSync(dir, { recursive: true, force: true })
})