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)) <= 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 }) })