Kernel Updates
This commit is contained in:
@@ -53,8 +53,11 @@ The following are set on `ctx` before the kernel starts (unless noted as overwri
|
|||||||
| **`bareOsAwaitInitdUnits(names, timeoutMs)`** | Resolves when all listed initd units are **`active`** (polls **`getBareServiceRuntime`**); returns **`false`** on timeout. |
|
| **`bareOsAwaitInitdUnits(names, timeoutMs)`** | Resolves when all listed initd units are **`active`** (polls **`getBareServiceRuntime`**); returns **`false`** on timeout. |
|
||||||
| **`bareOsGetResourceStatus()`** | Returns a plain object snapshot (pipeline limits, exec depth, IPC **`stats()`**, session counters, swarm peer count)—mirrors **`/proc/bare_os_resources`**. |
|
| **`bareOsGetResourceStatus()`** | Returns a plain object snapshot (pipeline limits, exec depth, IPC **`stats()`**, session counters, swarm peer count)—mirrors **`/proc/bare_os_resources`**. |
|
||||||
| **`bareOsRegisterVirtualFile(name, reader)`** | Registers **`/run/bare-os/virtual/<name>`** content; **`reader`** may return string or **`Uint8Array`** (sync or async). Gated by runtime cap **`virtualRegisterFiles`**. |
|
| **`bareOsRegisterVirtualFile(name, reader)`** | Registers **`/run/bare-os/virtual/<name>`** content; **`reader`** may return string or **`Uint8Array`** (sync or async). Gated by runtime cap **`virtualRegisterFiles`**. |
|
||||||
| **`bareOsSandboxRunScript()`** | **Throws** today—reserved for future worker/isolate execution; see [Chapter 9](09-security-and-trust.md). |
|
| **`bareOsSandboxRunScript(source, argv?, opts?)`** | Runs script source with a **restricted `ctx`** (personal-drive writes only; identity/virtual registration disabled). Respects **`raceWithAbortAndTimeout`** opts. Disable with **`BARE_OS_SANDBOX_SCRIPT=0`**. See [Chapter 9](09-security-and-trust.md). |
|
||||||
| **`bareOsRequestPearReload()`** | Returns **`{ requested, hint, env }`** for Pear OTA integration (host must apply); not a host IPC call by itself. |
|
| **`bareOsBootFileSha256Hex(buf)`** | **`sha256` hex for boot manifest checks** (`BARE_OS_BOOT_MANIFEST` + `/etc/bare-os/boot.manifest.json` on the stock kernel). |
|
||||||
|
| **`bareOsRegisterBootPhaseHook(phase, fn)`** / **`bareOsInvokeBootPhaseHooks(ev)`** | Hooks around stock **`kernel/init.js`** phases; **`ev`** includes **`phase`**, **`when`** (`before` / `after`), **`label`**. **`phase`** may be `*` or `before:rc` style. |
|
||||||
|
| **`bareOsInvalidateVirtualFile(name)`** / **`bareOsUpdateVirtualFileMeta(name, patch)`** | Virtual files under **`/run/bare-os/virtual/`**; **`bareOsRegisterVirtualFile`** accepts optional **`{ etag }`** third argument or **`{ read }`** object. |
|
||||||
|
| **`bareOsRequestPearReload(opts?)`** | **`async`** — returns **`{ requested, hint, env }`**; with **`{ persistRequest: true }`** writes **`~/.bare-os/pear-reload.request`** and may **`process.emit('bare-os:pear-reload', …)`** on Node. |
|
||||||
| **`bareOsHostStats`** _(optional)_ | When the **`bare-os`** npm module loads on the host, a **frozen** snapshot: **`hostname`**, **`loadavg`**, **`cpus`**, **`networkInterfaces`**. |
|
| **`bareOsHostStats`** _(optional)_ | When the **`bare-os`** npm module loads on the host, a **frozen** snapshot: **`hostname`**, **`loadavg`**, **`cpus`**, **`networkInterfaces`**. |
|
||||||
| **`httpFetch`** _(optional)_ | When Node/global **`fetch`** exists, the booter sets a **`fetch`** compatible function with optional **HTTP allow/deny** policy (**`BARE_OS_HTTP_ALLOWLIST`**, **`BARE_OS_HTTP_DENYLIST`**) and audit hooks when **`BARE_OS_AUDIT`** is on. |
|
| **`httpFetch`** _(optional)_ | When Node/global **`fetch`** exists, the booter sets a **`fetch`** compatible function with optional **HTTP allow/deny** policy (**`BARE_OS_HTTP_ALLOWLIST`**, **`BARE_OS_HTTP_DENYLIST`**) and audit hooks when **`BARE_OS_AUDIT`** is on. |
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ The **system** Hyperdrive is the **OS image**. You normally obtain it by **repli
|
|||||||
|
|
||||||
**IPC JSON-RPC:** when **`BARE_OS_IPC_RPC_TOKEN`** is set, **`pushJson`** payloads must include matching **`bareOsIpcToken`** or the push throws. Line size is capped (**`BARE_OS_IPC_JSON_MAX_BYTES`**, default 256 KiB).
|
**IPC JSON-RPC:** when **`BARE_OS_IPC_RPC_TOKEN`** is set, **`pushJson`** payloads must include matching **`bareOsIpcToken`** or the push throws. Line size is capped (**`BARE_OS_IPC_JSON_MAX_BYTES`**, default 256 KiB).
|
||||||
|
|
||||||
**Future isolation:** **`ctx.bareOsSandboxRunScript()`** is reserved and **throws** today; real worker or Pear-isolate execution would narrow trust for untrusted scripts—see the handbook blueprints chapter for the architectural split.
|
**Sandboxed scripts:** **`ctx.bareOsSandboxRunScript(source, argv?, opts?)`** runs in-image JS with a **restricted `ctx`**: writes are limited to the personal namespace (same routing rules as `isPersonalRoute`), and identity / vault / virtual-file registration hooks are disabled. Disable entirely with **`BARE_OS_SANDBOX_SCRIPT=0`**. This is still **not** a hardware isolate—treat it as a trust reducer, not a security boundary.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ function bareOsEmitRaw(ctx, chunk) {
|
|||||||
* next, exit, break, continue, ++/--, arrays, strings, regex ~ !~, builtins (length, substr, index,
|
* next, exit, break, continue, ++/--, arrays, strings, regex ~ !~, builtins (length, substr, index,
|
||||||
* split, sprintf, sub, gsub, match, int, tolower, toupper, rand, srand), getline from stdin/files,
|
* split, sprintf, sub, gsub, match, int, tolower, toupper, rand, srand), getline from stdin/files,
|
||||||
* -F FS, NF, NR, FNR, $0..$n, OFS, ORS, RS, ARGC, ARGV, FILENAME, ENVIRON (read-only mirror).
|
* -F FS, NF, NR, FNR, $0..$n, OFS, ORS, RS, ARGC, ARGV, FILENAME, ENVIRON (read-only mirror).
|
||||||
|
* GNU-style empty fields: consecutive FS delimiters still advance $1..$NF where the grammar allows.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function bareAwkError(msg) {
|
function bareAwkError(msg) {
|
||||||
|
|||||||
@@ -98,6 +98,9 @@ async function run(ctx, argv) {
|
|||||||
git remote add origin 'git+pear://<key-or-link>'
|
git remote add origin 'git+pear://<key-or-link>'
|
||||||
git push origin main
|
git push origin main
|
||||||
|
|
||||||
|
Host env (aligns with gip-transport / git-remote-git+pear):
|
||||||
|
GIT_PEAR_DEBUG, HYPERSWARM_DHT, PEAR_CHANNEL — forwarded by Pear tooling when set.
|
||||||
|
|
||||||
Pear app channel metadata for images: set BARE_OS_PEAR_CHANNEL / BARE_OS_PEAR_RELEASE on the host.
|
Pear app channel metadata for images: set BARE_OS_PEAR_CHANNEL / BARE_OS_PEAR_RELEASE on the host.
|
||||||
`)
|
`)
|
||||||
ctx.exitCode = 0
|
ctx.exitCode = 0
|
||||||
|
|||||||
+1
-1
@@ -87,7 +87,7 @@ function bareOsEmitRaw(ctx, chunk) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var BARE_OS_HELP_BIN_SPACED = "arch awk base32 base64 basename basenc cat chgrp chmod chown cksum clear comm cp crontab curl cut date df dir dircolors dirname du echo edit env exit expand expr factor false find fmt fold getconf git git-pear grep groups hdms head help hostid hostname id install join journalctl jq ln login logname logout ls man md5sum mkdir mkfifo mktemp mv nano nl nproc numfmt od paste pathchk pr printenv printf pwd readlink realpath rev rm rmdir savevault sed seq sha1sum sha256sum sha512sum shuf sleep sort split stat sum sync systemctl tac tail tee test theme time touch tr true truncate tsort tty uname unexpand uniq unlink uptime users vdir wc wget which who whoami xargs yes"
|
var BARE_OS_HELP_BIN_SPACED = "arch awk base32 base64 basename basenc cat chgrp chmod chown cksum clear comm cp crontab curl cut date df dir dircolors dirname du echo edit env exit expand expr factor false find fmt fold getconf git git-pear grep groups hdms head help hostid hostname id install join journalctl jq ln login logname logout ls man md5sum mkdir mkfifo mktemp mv nano nl nproc numfmt od oidc-publish paste pathchk pr printenv printf pwd readlink realpath rev rm rmdir savevault sed seq sha1sum sha256sum sha512sum shuf sleep sort split stat sum sync systemctl tac tail tee test theme time touch tr true truncate tsort tty uname unexpand uniq unlink uptime users vdir wc wget which who whoami xargs yes"
|
||||||
async function run(ctx, argv) {
|
async function run(ctx, argv) {
|
||||||
ctx.console.log(
|
ctx.console.log(
|
||||||
'Bare OS — default user: guest | shell builtins: alias, barerc, cd, command, export, exit, login, logout, readonly, type, umask, unalias, unset, : | /bin: ' +
|
'Bare OS — default user: guest | shell builtins: alias, barerc, cd, command, export, exit, login, logout, readonly, type, umask, unalias, unset, : | /bin: ' +
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
|
||||||
|
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
|
||||||
|
function bareStdin(ctx) {
|
||||||
|
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
|
||||||
|
function bareFormatModeString(mode, type) {
|
||||||
|
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
|
||||||
|
const perm = mode & 0o777
|
||||||
|
const r = (bit) => (perm & bit ? 'r' : '-')
|
||||||
|
const w = (bit) => (perm & bit ? 'w' : '-')
|
||||||
|
const x = (bit) => (perm & bit ? 'x' : '-')
|
||||||
|
return (
|
||||||
|
typeChar +
|
||||||
|
r(0o400) +
|
||||||
|
w(0o200) +
|
||||||
|
x(0o100) +
|
||||||
|
r(0o040) +
|
||||||
|
w(0o020) +
|
||||||
|
x(0o010) +
|
||||||
|
r(0o004) +
|
||||||
|
w(0o002) +
|
||||||
|
x(0o001)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {number} mtimeMs @param {number} [nowMs] */
|
||||||
|
function bareFormatLsMtime(mtimeMs, nowMs) {
|
||||||
|
const now = nowMs != null ? nowMs : Date.now()
|
||||||
|
const d = new Date(mtimeMs)
|
||||||
|
const months = [
|
||||||
|
'Jan',
|
||||||
|
'Feb',
|
||||||
|
'Mar',
|
||||||
|
'Apr',
|
||||||
|
'May',
|
||||||
|
'Jun',
|
||||||
|
'Jul',
|
||||||
|
'Aug',
|
||||||
|
'Sep',
|
||||||
|
'Oct',
|
||||||
|
'Nov',
|
||||||
|
'Dec'
|
||||||
|
]
|
||||||
|
const mon = months[d.getMonth()]
|
||||||
|
const day = String(d.getDate()).padStart(2, ' ')
|
||||||
|
const sixMo = 180 * 24 * 3600 * 1000
|
||||||
|
if (Math.abs(now - mtimeMs) > sixMo) {
|
||||||
|
const yr = String(d.getFullYear()).padStart(4, ' ')
|
||||||
|
return mon + ' ' + day + ' ' + yr
|
||||||
|
}
|
||||||
|
const hh = String(d.getHours()).padStart(2, '0')
|
||||||
|
const mm = String(d.getMinutes()).padStart(2, '0')
|
||||||
|
return mon + ' ' + day + ' ' + hh + ':' + mm
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {number} size */
|
||||||
|
function barePosixBlocks(size) {
|
||||||
|
return Math.ceil(Number(size) / 512) || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
|
||||||
|
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
|
||||||
|
* @param {Record<string, unknown>} ctx
|
||||||
|
* @param {string | Uint8Array} chunk
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function bareOsEmitRaw(ctx, chunk) {
|
||||||
|
if (typeof ctx.bareOsBinWrite === 'function') {
|
||||||
|
const b4 = ctx.b4a
|
||||||
|
const u8 =
|
||||||
|
typeof chunk === 'string'
|
||||||
|
? b4 && typeof b4.from === 'function'
|
||||||
|
? b4.from(chunk)
|
||||||
|
: new TextEncoder().encode(chunk)
|
||||||
|
: chunk
|
||||||
|
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const w = globalThis.process?.stdout?.write
|
||||||
|
if (typeof w === 'function') {
|
||||||
|
w.call(globalThis.process.stdout, chunk)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thin helper for OIDC-style token exchange workflows (Holepunch oidc-publishing patterns).
|
||||||
|
* Uses ctx.httpFetch when present and host HTTP policy allows the issuer URL.
|
||||||
|
*/
|
||||||
|
async function run(ctx, argv) {
|
||||||
|
if (argv[1] === 'help' || argv[1] === '-h' || argv[1] === '--help') {
|
||||||
|
ctx.console.log(`oidc-publish — optional OIDC token helper
|
||||||
|
|
||||||
|
Reads OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET (or argv) and POSTs
|
||||||
|
to \${issuer}/oauth/token with grant_type=client_credentials when ctx.httpFetch exists.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
export OIDC_ISSUER=https://issuer.example
|
||||||
|
oidc-publish
|
||||||
|
|
||||||
|
Requires delegated httpFetch and allowlisted host (BARE_OS_HTTP_ALLOWLIST).
|
||||||
|
`)
|
||||||
|
ctx.exitCode = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const env = ctx.vfs?.env || {}
|
||||||
|
const issuer = String(env.OIDC_ISSUER || argv[2] || '').replace(/\/+$/, '')
|
||||||
|
const cid = String(env.OIDC_CLIENT_ID || argv[3] || '')
|
||||||
|
const csec = String(env.OIDC_CLIENT_SECRET || argv[4] || '')
|
||||||
|
if (!issuer || !cid || !csec) {
|
||||||
|
ctx.console.error(
|
||||||
|
'oidc-publish: set OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET'
|
||||||
|
)
|
||||||
|
ctx.exitCode = 2
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (typeof ctx.httpFetch !== 'function') {
|
||||||
|
ctx.console.error('oidc-publish: ctx.httpFetch not available')
|
||||||
|
ctx.exitCode = 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const url = `${issuer}/oauth/token`
|
||||||
|
const body =
|
||||||
|
'grant_type=client_credentials&client_id=' +
|
||||||
|
encodeURIComponent(cid) +
|
||||||
|
'&client_secret=' +
|
||||||
|
encodeURIComponent(csec)
|
||||||
|
try {
|
||||||
|
const res = await ctx.httpFetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'content-type': 'application/x-www-form-urlencoded' },
|
||||||
|
body
|
||||||
|
})
|
||||||
|
const text = await res.text()
|
||||||
|
if (!res.ok) {
|
||||||
|
ctx.console.error('oidc-publish: HTTP ' + res.status + ' ' + text.slice(0, 200))
|
||||||
|
ctx.exitCode = 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.console.log(text.slice(0, 4000))
|
||||||
|
ctx.exitCode = 0
|
||||||
|
} catch (e) {
|
||||||
|
ctx.console.error('oidc-publish: ' + ((e && e.message) || String(e)))
|
||||||
|
ctx.exitCode = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"sha256": {
|
||||||
|
"/etc/bare-os/rc": "replace-with-sha256-hex-of-file-contents"
|
||||||
|
}
|
||||||
|
}
|
||||||
+111
-2
@@ -196,6 +196,13 @@ function bootLineAllowed(line, allow) {
|
|||||||
* @param {string[]} phaseLog
|
* @param {string[]} phaseLog
|
||||||
*/
|
*/
|
||||||
async function bootTimed(ctx, label, fn, phaseLog) {
|
async function bootTimed(ctx, label, fn, phaseLog) {
|
||||||
|
if (typeof ctx.bareOsInvokeBootPhaseHooks === 'function') {
|
||||||
|
await ctx.bareOsInvokeBootPhaseHooks({
|
||||||
|
phase: label,
|
||||||
|
when: 'before',
|
||||||
|
label
|
||||||
|
})
|
||||||
|
}
|
||||||
const t0 = Date.now()
|
const t0 = Date.now()
|
||||||
await fn()
|
await fn()
|
||||||
const ms = Date.now() - t0
|
const ms = Date.now() - t0
|
||||||
@@ -226,6 +233,67 @@ async function bootTimed(ctx, label, fn, phaseLog) {
|
|||||||
sessionId: (ctx.env && ctx.env.BARE_OS_SESSION_ID) || ''
|
sessionId: (ctx.env && ctx.env.BARE_OS_SESSION_ID) || ''
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (typeof ctx.bareOsInvokeBootPhaseHooks === 'function') {
|
||||||
|
await ctx.bareOsInvokeBootPhaseHooks({
|
||||||
|
phase: label,
|
||||||
|
when: 'after',
|
||||||
|
label
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Record<string, unknown> | null | undefined} */
|
||||||
|
let bootManifestMemo
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Record<string, unknown>} ctx
|
||||||
|
* @returns {Promise<Record<string, unknown> | null>}
|
||||||
|
*/
|
||||||
|
async function loadBootManifest(ctx) {
|
||||||
|
const v = ctx.env && ctx.env.BARE_OS_BOOT_MANIFEST
|
||||||
|
if (v !== '1' && v !== 'true') return null
|
||||||
|
if (bootManifestMemo !== undefined) return bootManifestMemo
|
||||||
|
const { drive, b4a, console } = ctx
|
||||||
|
try {
|
||||||
|
const buf = await drive.get('/etc/bare-os/boot.manifest.json')
|
||||||
|
if (!buf) {
|
||||||
|
bootManifestMemo = null
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
bootManifestMemo = JSON.parse(b4a.toString(buf))
|
||||||
|
return bootManifestMemo
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
'[boot] boot.manifest.json: ' + ((e && e.message) || String(e))
|
||||||
|
)
|
||||||
|
bootManifestMemo = null
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Record<string, unknown>} ctx
|
||||||
|
* @param {string} drivePath
|
||||||
|
* @param {string | Uint8Array} content
|
||||||
|
*/
|
||||||
|
async function bootManifestDigestOk(ctx, drivePath, content) {
|
||||||
|
const m = await loadBootManifest(ctx)
|
||||||
|
const sha = m && typeof m === 'object' ? m.sha256 : null
|
||||||
|
if (!sha || typeof sha !== 'object') return true
|
||||||
|
const exp = /** @type {Record<string, string>} */ (sha)[drivePath]
|
||||||
|
if (exp == null || exp === '') return true
|
||||||
|
if (typeof ctx.bareOsBootFileSha256Hex !== 'function') {
|
||||||
|
ctx.console.error('[boot] manifest present but bareOsBootFileSha256Hex missing')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const buf =
|
||||||
|
typeof content === 'string' ? ctx.b4a.from(content, 'utf8') : content
|
||||||
|
const hex = ctx.bareOsBootFileSha256Hex(buf)
|
||||||
|
if (hex !== String(exp).trim().toLowerCase()) {
|
||||||
|
ctx.console.error('[boot] manifest sha256 mismatch: ' + drivePath)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -400,7 +468,15 @@ async function runRcFileAt(ctx, drivePath, label) {
|
|||||||
try {
|
try {
|
||||||
const buf = await drive.get(drivePath)
|
const buf = await drive.get(drivePath)
|
||||||
if (!buf) return true
|
if (!buf) return true
|
||||||
return await runRcLines(ctx, b4a.toString(buf))
|
const text = b4a.toString(buf)
|
||||||
|
if (!(await bootManifestDigestOk(ctx, drivePath, text))) {
|
||||||
|
if (bootStrict(ctx)) {
|
||||||
|
if (typeof ctx.requestBooterExit === 'function') ctx.requestBooterExit(1)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return await runRcLines(ctx, text)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`${label}: ` + ((e && e.message) || String(e)))
|
console.error(`${label}: ` + ((e && e.message) || String(e)))
|
||||||
return true
|
return true
|
||||||
@@ -578,6 +654,35 @@ async function runKernelSelftest(ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
/** @type {string[]} */
|
||||||
|
const snames = []
|
||||||
|
for await (const n of ctx.drive.readdir('/etc/bare-os/selftest.d'))
|
||||||
|
snames.push(n)
|
||||||
|
snames.sort()
|
||||||
|
for (const name of snames) {
|
||||||
|
if (!isBareOsRcSnippetFile(name)) continue
|
||||||
|
const p = `/etc/bare-os/selftest.d/${name}`
|
||||||
|
const buf = await ctx.drive.get(p)
|
||||||
|
if (!buf) continue
|
||||||
|
const label = `selftest.d/${name}`
|
||||||
|
try {
|
||||||
|
await runRcLines(ctx, ctx.b4a.toString(buf))
|
||||||
|
tapLine(true, label, '')
|
||||||
|
} catch (e) {
|
||||||
|
const msg = (e && e.message) || String(e)
|
||||||
|
console.error('selftest: ' + msg)
|
||||||
|
tapLine(false, label, msg)
|
||||||
|
if (strict) {
|
||||||
|
if (typeof ctx.requestBooterExit === 'function')
|
||||||
|
ctx.requestBooterExit(1)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
/* no selftest.d */
|
||||||
|
}
|
||||||
if (tap) {
|
if (tap) {
|
||||||
console.error(`1..${tapN}`)
|
console.error(`1..${tapN}`)
|
||||||
}
|
}
|
||||||
@@ -596,7 +701,11 @@ function publishBootReady(ctx, phaseLog) {
|
|||||||
phases: [...phaseLog],
|
phases: [...phaseLog],
|
||||||
sessionId: sid,
|
sessionId: sid,
|
||||||
completedAtMs: Date.now(),
|
completedAtMs: Date.now(),
|
||||||
minimal: bootMinimal(ctx)
|
minimal: bootMinimal(ctx),
|
||||||
|
subsystems: {
|
||||||
|
kernel: { ready: true, phaseCount: phaseLog.length },
|
||||||
|
initd: { awaited: true }
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"pins": {}
|
||||||
|
}
|
||||||
+204
-204
@@ -1,6 +1,12 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"bundles": [
|
"bundles": [
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/b4a.js",
|
||||||
|
"keys": [
|
||||||
|
"b4a"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/safetyCatch.js",
|
"path": "/lib/bare/bundles/safetyCatch.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -13,12 +19,6 @@
|
|||||||
"hypercoreIdEncoding"
|
"hypercoreIdEncoding"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/b4a.js",
|
|
||||||
"keys": [
|
|
||||||
"b4a"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/compactEncoding.js",
|
"path": "/lib/bare/bundles/compactEncoding.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -31,30 +31,30 @@
|
|||||||
"bareUrl"
|
"bareUrl"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/protomux.js",
|
|
||||||
"keys": [
|
|
||||||
"protomux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareEncoding.js",
|
"path": "/lib/bare/bundles/bareEncoding.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareEncoding"
|
"bareEncoding"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/barePath.js",
|
|
||||||
"keys": [
|
|
||||||
"barePath"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareEvents.js",
|
"path": "/lib/bare/bundles/bareEvents.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareEvents"
|
"bareEvents"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/protomux.js",
|
||||||
|
"keys": [
|
||||||
|
"protomux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/barePath.js",
|
||||||
|
"keys": [
|
||||||
|
"barePath"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareAbort.js",
|
"path": "/lib/bare/bundles/bareAbort.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -73,6 +73,12 @@
|
|||||||
"bareAnsiEscapes"
|
"bareAnsiEscapes"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareAddonResolve.js",
|
||||||
|
"keys": [
|
||||||
|
"bareAddonResolve"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareReadline.js",
|
"path": "/lib/bare/bundles/bareReadline.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -85,24 +91,6 @@
|
|||||||
"bareCrypto"
|
"bareCrypto"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareAddonResolve.js",
|
|
||||||
"keys": [
|
|
||||||
"bareAddonResolve"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareAsyncHooks.js",
|
|
||||||
"keys": [
|
|
||||||
"bareAsyncHooks"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareAtomics.js",
|
|
||||||
"keys": [
|
|
||||||
"bareAtomics"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareAppKit.js",
|
"path": "/lib/bare/bundles/bareAppKit.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -116,9 +104,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareAssert.js",
|
"path": "/lib/bare/bundles/bareAsyncHooks.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareAssert"
|
"bareAsyncHooks"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -127,6 +115,18 @@
|
|||||||
"fetch"
|
"fetch"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareAtomics.js",
|
||||||
|
"keys": [
|
||||||
|
"bareAtomics"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareAssert.js",
|
||||||
|
"keys": [
|
||||||
|
"bareAssert"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareBmp.js",
|
"path": "/lib/bare/bundles/bareBmp.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -139,12 +139,6 @@
|
|||||||
"bareBundleCompile"
|
"bareBundleCompile"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareBuffer.js",
|
|
||||||
"keys": [
|
|
||||||
"bareBuffer"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareBundle.js",
|
"path": "/lib/bare/bundles/bareBundle.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -152,9 +146,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
|
"path": "/lib/bare/bundles/bareBuffer.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareBundleEvaluate"
|
"bareBuffer"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -163,24 +157,36 @@
|
|||||||
"bareBluetoothApple"
|
"bareBluetoothApple"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
|
||||||
|
"keys": [
|
||||||
|
"bareBundleEvaluate"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareBoot.js",
|
"path": "/lib/bare/bundles/bareBoot.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareBoot"
|
"bareBoot"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareDaemon.js",
|
|
||||||
"keys": [
|
|
||||||
"bareDaemon"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareConsole.js",
|
"path": "/lib/bare/bundles/bareConsole.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareConsole"
|
"bareConsole"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareDebugLog.js",
|
||||||
|
"keys": [
|
||||||
|
"bareDebugLog"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareDaemon.js",
|
||||||
|
"keys": [
|
||||||
|
"bareDaemon"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareBundleId.js",
|
"path": "/lib/bare/bundles/bareBundleId.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -193,12 +199,6 @@
|
|||||||
"bareChannel"
|
"bareChannel"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareDebugLog.js",
|
|
||||||
"keys": [
|
|
||||||
"bareDebugLog"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareDelta.js",
|
"path": "/lib/bare/bundles/bareDelta.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -223,6 +223,12 @@
|
|||||||
"bareEnv"
|
"bareEnv"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareExif.js",
|
||||||
|
"keys": [
|
||||||
|
"bareExif"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareCov.js",
|
"path": "/lib/bare/bundles/bareCov.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -235,12 +241,6 @@
|
|||||||
"bareDgram"
|
"bareDgram"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareExif.js",
|
|
||||||
"keys": [
|
|
||||||
"bareExif"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareFfmpeg.js",
|
"path": "/lib/bare/bundles/bareFfmpeg.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -253,12 +253,6 @@
|
|||||||
"bareFfmpegEncodings"
|
"bareFfmpegEncodings"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareFormData.js",
|
|
||||||
"keys": [
|
|
||||||
"bareFormData"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareFormat.js",
|
"path": "/lib/bare/bundles/bareFormat.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -266,9 +260,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareGif.js",
|
"path": "/lib/bare/bundles/bareFormData.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareGif"
|
"bareFormData"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareHeif.js",
|
||||||
|
"keys": [
|
||||||
|
"bareHeif"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -278,15 +278,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareGtk.js",
|
"path": "/lib/bare/bundles/bareGif.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareGtk"
|
"bareGif"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareHeif.js",
|
"path": "/lib/bare/bundles/bareGtk.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareHeif"
|
"bareGtk"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -307,12 +307,6 @@
|
|||||||
"bareHttpParser"
|
"bareHttpParser"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareImageResample.js",
|
|
||||||
"keys": [
|
|
||||||
"bareImageResample"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareIco.js",
|
"path": "/lib/bare/bundles/bareIco.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -320,9 +314,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareHttp1.js",
|
"path": "/lib/bare/bundles/bareImageResample.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareHttp1"
|
"bareImageResample"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -331,6 +325,12 @@
|
|||||||
"bareInspect"
|
"bareInspect"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareHttp1.js",
|
||||||
|
"keys": [
|
||||||
|
"bareHttp1"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareHttps.js",
|
"path": "/lib/bare/bundles/bareHttps.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -361,6 +361,12 @@
|
|||||||
"bareLief"
|
"bareLief"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareLogger.js",
|
||||||
|
"keys": [
|
||||||
|
"bareLogger"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareLink.js",
|
"path": "/lib/bare/bundles/bareLink.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -373,18 +379,6 @@
|
|||||||
"bareInspector"
|
"bareInspector"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareLogger.js",
|
|
||||||
"keys": [
|
|
||||||
"bareLogger"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareModuleLexer.js",
|
|
||||||
"keys": [
|
|
||||||
"bareModuleLexer"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareMake.js",
|
"path": "/lib/bare/bundles/bareMake.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -398,15 +392,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareModule.js",
|
"path": "/lib/bare/bundles/bareModuleLexer.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareModule"
|
"bareModuleLexer"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareNdk.js",
|
|
||||||
"keys": [
|
|
||||||
"bareNdk"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -416,9 +404,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareNative.js",
|
"path": "/lib/bare/bundles/bareModule.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareNative"
|
"bareModule"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -428,39 +416,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareOpen.js",
|
"path": "/lib/bare/bundles/bareNdk.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareOpen"
|
"bareNdk"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareNet.js",
|
"path": "/lib/bare/bundles/bareNative.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareNet"
|
"bareNative"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareMedia.js",
|
|
||||||
"keys": [
|
|
||||||
"bareMedia"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareOs.js",
|
|
||||||
"keys": [
|
|
||||||
"bareOs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/barePack.js",
|
|
||||||
"keys": [
|
|
||||||
"barePack"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareNodeRuntime.js",
|
|
||||||
"keys": [
|
|
||||||
"bareNodeRuntime"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -470,9 +434,27 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePng.js",
|
"path": "/lib/bare/bundles/bareOs.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"barePng"
|
"bareOs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareOpen.js",
|
||||||
|
"keys": [
|
||||||
|
"bareOpen"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareMedia.js",
|
||||||
|
"keys": [
|
||||||
|
"bareMedia"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareNet.js",
|
||||||
|
"keys": [
|
||||||
|
"bareNet"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -481,12 +463,30 @@
|
|||||||
"barePerformance"
|
"barePerformance"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/barePng.js",
|
||||||
|
"keys": [
|
||||||
|
"barePng"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePackDrive.js",
|
"path": "/lib/bare/bundles/barePackDrive.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"barePackDrive"
|
"barePackDrive"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/barePack.js",
|
||||||
|
"keys": [
|
||||||
|
"barePack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/barePipe.js",
|
||||||
|
"keys": [
|
||||||
|
"barePipe"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePunycode.js",
|
"path": "/lib/bare/bundles/barePunycode.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -505,18 +505,18 @@
|
|||||||
"barePrebuild"
|
"barePrebuild"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/barePipe.js",
|
|
||||||
"keys": [
|
|
||||||
"barePipe"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareQueueMicrotask.js",
|
"path": "/lib/bare/bundles/bareQueueMicrotask.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareQueueMicrotask"
|
"bareQueueMicrotask"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareNodeRuntime.js",
|
||||||
|
"keys": [
|
||||||
|
"bareNodeRuntime"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareRealm.js",
|
"path": "/lib/bare/bundles/bareRealm.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -541,12 +541,6 @@
|
|||||||
"bareRuntime"
|
"bareRuntime"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareSdl.js",
|
|
||||||
"keys": [
|
|
||||||
"bareSdl"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareRepl.js",
|
"path": "/lib/bare/bundles/bareRepl.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -559,6 +553,12 @@
|
|||||||
"bareRpc"
|
"bareRpc"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareSdl.js",
|
||||||
|
"keys": [
|
||||||
|
"bareSdl"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareSemver.js",
|
"path": "/lib/bare/bundles/bareSemver.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -571,12 +571,6 @@
|
|||||||
"bareRun"
|
"bareRun"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareSidecar.js",
|
|
||||||
"keys": [
|
|
||||||
"bareSidecar"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareSignals.js",
|
"path": "/lib/bare/bundles/bareSignals.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -584,9 +578,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareStorage.js",
|
"path": "/lib/bare/bundles/bareSidecar.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareStorage"
|
"bareSidecar"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -596,9 +590,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareStringDecoder.js",
|
"path": "/lib/bare/bundles/bareStorage.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareStringDecoder"
|
"bareStorage"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -607,6 +601,12 @@
|
|||||||
"bareStdio"
|
"bareStdio"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareStringDecoder.js",
|
||||||
|
"keys": [
|
||||||
|
"bareStringDecoder"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareSvg.js",
|
"path": "/lib/bare/bundles/bareSvg.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -625,6 +625,12 @@
|
|||||||
"bareStructuredClone"
|
"bareStructuredClone"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareTiff.js",
|
||||||
|
"keys": [
|
||||||
|
"bareTiff"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareTap.js",
|
"path": "/lib/bare/bundles/bareTap.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -638,15 +644,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareTiff.js",
|
"path": "/lib/bare/bundles/bareThread.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareTiff"
|
"bareThread"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareTimers.js",
|
|
||||||
"keys": [
|
|
||||||
"bareTimers"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -655,18 +655,30 @@
|
|||||||
"bareTcp"
|
"bareTcp"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareThread.js",
|
|
||||||
"keys": [
|
|
||||||
"bareThread"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareTpl.js",
|
"path": "/lib/bare/bundles/bareTpl.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareTpl"
|
"bareTpl"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareTimers.js",
|
||||||
|
"keys": [
|
||||||
|
"bareTimers"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareTls.js",
|
||||||
|
"keys": [
|
||||||
|
"bareTls"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareTty.js",
|
||||||
|
"keys": [
|
||||||
|
"bareTty"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareType.js",
|
"path": "/lib/bare/bundles/bareType.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -679,30 +691,24 @@
|
|||||||
"bareUiKit"
|
"bareUiKit"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareTls.js",
|
|
||||||
"keys": [
|
|
||||||
"bareTls"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareUnpack.js",
|
"path": "/lib/bare/bundles/bareUnpack.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareUnpack"
|
"bareUnpack"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareTty.js",
|
|
||||||
"keys": [
|
|
||||||
"bareTty"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareV8.js",
|
"path": "/lib/bare/bundles/bareV8.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareV8"
|
"bareV8"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareWalkHandles.js",
|
||||||
|
"keys": [
|
||||||
|
"bareWalkHandles"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareVm.js",
|
"path": "/lib/bare/bundles/bareVm.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -715,12 +721,6 @@
|
|||||||
"bareUnionBundle"
|
"bareUnionBundle"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareWalkHandles.js",
|
|
||||||
"keys": [
|
|
||||||
"bareWalkHandles"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareWebKit.js",
|
"path": "/lib/bare/bundles/bareWebKit.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -739,18 +739,18 @@
|
|||||||
"bareWebp"
|
"bareWebp"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareUtils.js",
|
|
||||||
"keys": [
|
|
||||||
"bareUtils"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareWhich.js",
|
"path": "/lib/bare/bundles/bareWhich.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareWhich"
|
"bareWhich"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareUtils.js",
|
||||||
|
"keys": [
|
||||||
|
"bareUtils"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
|
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -769,12 +769,6 @@
|
|||||||
"bareXdiff"
|
"bareXdiff"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareZlib.js",
|
|
||||||
"keys": [
|
|
||||||
"bareZlib"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareWs.js",
|
"path": "/lib/bare/bundles/bareWs.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -782,9 +776,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareZmq.js",
|
"path": "/lib/bare/bundles/bareZlib.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareZmq"
|
"bareZlib"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -792,6 +786,12 @@
|
|||||||
"keys": [
|
"keys": [
|
||||||
"bareWorker"
|
"bareWorker"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareZmq.js",
|
||||||
|
"keys": [
|
||||||
|
"bareZmq"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"bundleStats": {
|
"bundleStats": {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
| Version | Booter (workspace) | Notes |
|
| Version | Booter (workspace) | Notes |
|
||||||
| ------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| ------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| 1.8.0 | 0.1.0 | **`ctx.bareOsSandboxRunScript`**, **`bareOsBootFileSha256Hex`**, boot phase hooks (**`bareOsRegisterBootPhaseHook`** / **`bareOsInvokeBootPhaseHooks`**), virtual file meta (**`bareOsInvalidateVirtualFile`**, **`bareOsUpdateVirtualFileMeta`**), **`bareOsIpc.createDuplexBridge`**, **`/proc/bare_os_swarm`**, VFS union read (**`BARE_OS_VFS_UNION_PREFIXES`**), **`BARE_OS_URANDOM_CRYPTO=0`**, async **`bareOsRequestPearReload`**, **`bareOsPublishBootReady.subsystems`**, **`verifyBareModuleLockfile`**. Kernel: **`BARE_OS_BOOT_MANIFEST`**, **`/etc/bare-os/selftest.d/`**, stock init hooks. |
|
||||||
| 1.7.0 | 0.1.0 | **`ctx.bare`**: frozen map of Holepunch-style npm modules for in-image scripts (manifest-driven host `import()` + optional trusted **`/lib/bare/bundles/*.js`** merge). Caps **`bareCtxModules`**, **`bareDriveBundles`**. Env **`BARE_OS_BARE_MODULES`**, **`BARE_OS_BARE_DRIVE_BUNDLES`**. Workspace **`bare-os-bare-libs`** builds seeded bundles. |
|
| 1.7.0 | 0.1.0 | **`ctx.bare`**: frozen map of Holepunch-style npm modules for in-image scripts (manifest-driven host `import()` + optional trusted **`/lib/bare/bundles/*.js`** merge). Caps **`bareCtxModules`**, **`bareDriveBundles`**. Env **`BARE_OS_BARE_MODULES`**, **`BARE_OS_BARE_DRIVE_BUNDLES`**. Workspace **`bare-os-bare-libs`** builds seeded bundles. |
|
||||||
| 1.6.0 | 0.1.0 | Abort/timeout on `execLine`, `readLine`, `runBinCommand`, VFS `readFile`/`writeFile`; IPC fan-out + JSON-RPC token/line limits; HTTP allow/deny + audit; `/proc/bare_os_resources`, `/proc/bare_os_features`; `/run/bare-os/virtual/*`; booter boot phases in `boot.json` (`booterPhases`); optional `ctx.bareOsHostStats`, `ctx.httpFetch` policy wrapper; initd `ReadinessPath` / `ReadinessTimeoutSec`; Pear/sandbox stubs. |
|
| 1.6.0 | 0.1.0 | Abort/timeout on `execLine`, `readLine`, `runBinCommand`, VFS `readFile`/`writeFile`; IPC fan-out + JSON-RPC token/line limits; HTTP allow/deny + audit; `/proc/bare_os_resources`, `/proc/bare_os_features`; `/run/bare-os/virtual/*`; booter boot phases in `boot.json` (`booterPhases`); optional `ctx.bareOsHostStats`, `ctx.httpFetch` policy wrapper; initd `ReadinessPath` / `ReadinessTimeoutSec`; Pear/sandbox stubs. |
|
||||||
| 1.5.0 | (prior) | Previous documented contract. |
|
| 1.5.0 | (prior) | Previous documented contract. |
|
||||||
|
|||||||
@@ -9,10 +9,16 @@ import {
|
|||||||
topicKey,
|
topicKey,
|
||||||
parseMbr,
|
parseMbr,
|
||||||
BARE_OS_KERNEL_FEATURES_STOCK_V1,
|
BARE_OS_KERNEL_FEATURES_STOCK_V1,
|
||||||
BARE_OS_KERNEL_FEATURE_BITS_DOC
|
BARE_OS_KERNEL_FEATURE_BITS_DOC,
|
||||||
|
BARE_OS_FEATURE_CRYPTO_URANDOM
|
||||||
} from 'bare-os-protocol'
|
} from 'bare-os-protocol'
|
||||||
import { SwarmDisk } from './lib/swarm-disk.js'
|
import { SwarmDisk } from './lib/swarm-disk.js'
|
||||||
import { runKernelFromSource, runBinCommand } from './lib/kernel-runner.js'
|
import {
|
||||||
|
runKernelFromSource,
|
||||||
|
runBinCommand,
|
||||||
|
runUserScriptFromSource
|
||||||
|
} from './lib/kernel-runner.js'
|
||||||
|
import { createBareOsSandboxContext } from './lib/bare-os-sandbox.js'
|
||||||
import { createVfs } from './lib/vfs.js'
|
import { createVfs } from './lib/vfs.js'
|
||||||
import { createBareOsIpc } from './lib/bare-os-ipc.js'
|
import { createBareOsIpc } from './lib/bare-os-ipc.js'
|
||||||
import {
|
import {
|
||||||
@@ -57,7 +63,8 @@ import {
|
|||||||
bareOsBareModulesEnabled,
|
bareOsBareModulesEnabled,
|
||||||
buildBareCtxObjectFromHost,
|
buildBareCtxObjectFromHost,
|
||||||
maybeMergeBareFromDrive,
|
maybeMergeBareFromDrive,
|
||||||
bareOsBareHostImportsEnabled
|
bareOsBareHostImportsEnabled,
|
||||||
|
verifyBareModuleLockfile
|
||||||
} from './lib/bare-os-ctx-bare.js'
|
} from './lib/bare-os-ctx-bare.js'
|
||||||
import { raceWithAbortAndTimeout } from './lib/bare-os-abort.js'
|
import { raceWithAbortAndTimeout } from './lib/bare-os-abort.js'
|
||||||
import {
|
import {
|
||||||
@@ -69,11 +76,25 @@ import {
|
|||||||
bareOsListThemeNames
|
bareOsListThemeNames
|
||||||
} from './lib/bare-os-theme-presets.js'
|
} from './lib/bare-os-theme-presets.js'
|
||||||
import './lib/bare-cron.js'
|
import './lib/bare-cron.js'
|
||||||
|
import { createHash } from 'node:crypto'
|
||||||
|
|
||||||
const { randomUUID, randomBytes } = bareCrypto
|
const { randomUUID, randomBytes } = bareCrypto
|
||||||
|
|
||||||
const _pkg = packageRootDir(import.meta.url)
|
const _pkg = packageRootDir(import.meta.url)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Record<string, string>} env
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
|
function parseUnionReadPrefixes(env) {
|
||||||
|
const raw = env && env.BARE_OS_VFS_UNION_PREFIXES
|
||||||
|
if (raw == null || !String(raw).trim()) return []
|
||||||
|
return String(raw)
|
||||||
|
.split(/[,:]+/)
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter((s) => s.startsWith('/'))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On Pear/Bare, `process.exit` is often missing or ineffective; use `Bare.exit`
|
* On Pear/Bare, `process.exit` is often missing or ineffective; use `Bare.exit`
|
||||||
* (see holepunch `pear-prerelease`, `pear-terminal`, `bare-process`).
|
* (see holepunch `pear-prerelease`, `pear-terminal`, `bare-process`).
|
||||||
@@ -332,7 +353,7 @@ async function executeKernel(disk, store, swarm, initSource) {
|
|||||||
const hdmsLifecycleSubs = []
|
const hdmsLifecycleSubs = []
|
||||||
/** @type {Set<(ev: Record<string, unknown>) => void | Promise<void>>} */
|
/** @type {Set<(ev: Record<string, unknown>) => void | Promise<void>>} */
|
||||||
const bootEventSubs = new Set()
|
const bootEventSubs = new Set()
|
||||||
/** @type {{ ready: boolean, phases: string[], booterPhases: string[], sessionId: string, completedAtMs: number | null, minimal: boolean, imageDigest: string, pearChannel: string, pearRelease: string }} */
|
/** @type {{ ready: boolean, phases: string[], booterPhases: string[], sessionId: string, completedAtMs: number | null, minimal: boolean, imageDigest: string, pearChannel: string, pearRelease: string, subsystems?: Record<string, unknown>, bareModuleLockWarnings?: string[] }} */
|
||||||
const bootReadyStateRef = {
|
const bootReadyStateRef = {
|
||||||
ready: false,
|
ready: false,
|
||||||
phases: [],
|
phases: [],
|
||||||
@@ -343,7 +364,9 @@ async function executeKernel(disk, store, swarm, initSource) {
|
|||||||
minimal: false,
|
minimal: false,
|
||||||
imageDigest: '',
|
imageDigest: '',
|
||||||
pearChannel: '',
|
pearChannel: '',
|
||||||
pearRelease: ''
|
pearRelease: '',
|
||||||
|
subsystems: {},
|
||||||
|
bareModuleLockWarnings: []
|
||||||
}
|
}
|
||||||
bootReadyStateRef.imageDigest = String(
|
bootReadyStateRef.imageDigest = String(
|
||||||
shellEnv.BARE_OS_IMAGE_DIGEST || ''
|
shellEnv.BARE_OS_IMAGE_DIGEST || ''
|
||||||
@@ -379,8 +402,13 @@ async function executeKernel(disk, store, swarm, initSource) {
|
|||||||
shellEnv.BARE_OS_IPC_FANOUT !== '0' &&
|
shellEnv.BARE_OS_IPC_FANOUT !== '0' &&
|
||||||
shellEnv.BARE_OS_IPC_FANOUT !== 'false'
|
shellEnv.BARE_OS_IPC_FANOUT !== 'false'
|
||||||
const ipcRpcTok = String(shellEnv.BARE_OS_IPC_RPC_TOKEN || '').trim() || null
|
const ipcRpcTok = String(shellEnv.BARE_OS_IPC_RPC_TOKEN || '').trim() || null
|
||||||
/** @type {Map<string, () => string | Uint8Array | Promise<string | Uint8Array>>} */
|
/** @type {Map<string, { read: () => string | Uint8Array | Promise<string | Uint8Array>, etag: string, version: number }>} */
|
||||||
const virtualReaders = new Map()
|
const virtualReaderEntries = new Map()
|
||||||
|
/** @type {{ phase: string, fn: (ev: Record<string, unknown>) => void | Promise<void> }[]} */
|
||||||
|
const bootPhaseHooks = []
|
||||||
|
const urandomCryptoOn =
|
||||||
|
shellEnv.BARE_OS_URANDOM_CRYPTO !== '0' &&
|
||||||
|
shellEnv.BARE_OS_URANDOM_CRYPTO !== 'false'
|
||||||
|
|
||||||
const bareOsIpc = createBareOsIpc({
|
const bareOsIpc = createBareOsIpc({
|
||||||
maxFifoBytes: ipcMaxResolved,
|
maxFifoBytes: ipcMaxResolved,
|
||||||
@@ -447,9 +475,13 @@ async function executeKernel(disk, store, swarm, initSource) {
|
|||||||
(d != null && String(d).trim() ? String(d).trim() : 'unknown') + '\n'
|
(d != null && String(d).trim() ? String(d).trim() : 'unknown') + '\n'
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
secureRandomBytes(n) {
|
secureRandomBytes:
|
||||||
return new Uint8Array(randomBytes(Math.min(65536, Math.max(1, n | 0))))
|
urandomCryptoOn
|
||||||
},
|
? (n) =>
|
||||||
|
new Uint8Array(
|
||||||
|
randomBytes(Math.min(65536, Math.max(1, n | 0)))
|
||||||
|
)
|
||||||
|
: undefined,
|
||||||
bareOsIpc,
|
bareOsIpc,
|
||||||
procNetDevText() {
|
procNetDevText() {
|
||||||
const n = disk.peers?.size ?? 0
|
const n = disk.peers?.size ?? 0
|
||||||
@@ -489,12 +521,25 @@ async function executeKernel(disk, store, swarm, initSource) {
|
|||||||
})}\n`
|
})}\n`
|
||||||
},
|
},
|
||||||
procBareOsFeaturesText() {
|
procBareOsFeaturesText() {
|
||||||
|
let bits = BARE_OS_KERNEL_FEATURES_STOCK_V1
|
||||||
|
if (!urandomCryptoOn) bits &= ~BARE_OS_FEATURE_CRYPTO_URANDOM
|
||||||
return `${JSON.stringify({
|
return `${JSON.stringify({
|
||||||
doc: BARE_OS_KERNEL_FEATURE_BITS_DOC,
|
doc: BARE_OS_KERNEL_FEATURE_BITS_DOC,
|
||||||
bits: BARE_OS_KERNEL_FEATURES_STOCK_V1
|
bits,
|
||||||
|
urandomCrypto: urandomCryptoOn
|
||||||
})}\n`
|
})}\n`
|
||||||
},
|
},
|
||||||
getVirtualReaders: () => virtualReaders,
|
procBareOsSwarmText() {
|
||||||
|
const tk = topicKey()
|
||||||
|
const peers = disk.peers?.size ?? 0
|
||||||
|
return `${JSON.stringify({
|
||||||
|
topicHex: b4a.toString(tk, 'hex'),
|
||||||
|
peerCount: peers,
|
||||||
|
protocol: 'bare-os-v1'
|
||||||
|
})}\n`
|
||||||
|
},
|
||||||
|
getVirtualReaders: () => virtualReaderEntries,
|
||||||
|
unionReadPrefixes: parseUnionReadPrefixes(shellEnv),
|
||||||
sysClassNetLoText() {
|
sysClassNetLoText() {
|
||||||
const n = disk.peers?.size ?? 0
|
const n = disk.peers?.size ?? 0
|
||||||
return `operstate ${n > 0 ? 'unknown' : 'down'}\ncarrier ${n > 0 ? 1 : 0}\n`
|
return `operstate ${n > 0 ? 'unknown' : 'down'}\ncarrier ${n > 0 ? 1 : 0}\n`
|
||||||
@@ -502,6 +547,10 @@ async function executeKernel(disk, store, swarm, initSource) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
emitBooterBootPhase('vfs')
|
emitBooterBootPhase('vfs')
|
||||||
|
Object.assign(bootReadyStateRef.subsystems, {
|
||||||
|
vfs: { ready: true, atMs: Date.now() },
|
||||||
|
swarm: { peersAtSession: disk.peers?.size ?? 0 }
|
||||||
|
})
|
||||||
|
|
||||||
/** @type {Record<string, unknown>} */
|
/** @type {Record<string, unknown>} */
|
||||||
const bareLibrary = {}
|
const bareLibrary = {}
|
||||||
@@ -510,6 +559,8 @@ async function executeKernel(disk, store, swarm, initSource) {
|
|||||||
if (bareOsBareHostImportsEnabled(shellEnv)) {
|
if (bareOsBareHostImportsEnabled(shellEnv)) {
|
||||||
await buildBareCtxObjectFromHost(shellEnv, bareLibrary)
|
await buildBareCtxObjectFromHost(shellEnv, bareLibrary)
|
||||||
}
|
}
|
||||||
|
const lockWarn = await verifyBareModuleLockfile(vfs, bareLibrary)
|
||||||
|
if (lockWarn.length) bootReadyStateRef.bareModuleLockWarnings = lockWarn
|
||||||
}
|
}
|
||||||
|
|
||||||
const hdmsController = new HdmsController()
|
const hdmsController = new HdmsController()
|
||||||
@@ -671,8 +722,14 @@ async function executeKernel(disk, store, swarm, initSource) {
|
|||||||
* @param {Partial<{ ready: boolean, phases: string[], sessionId: string, completedAtMs: number, minimal: boolean }>} patch
|
* @param {Partial<{ ready: boolean, phases: string[], sessionId: string, completedAtMs: number, minimal: boolean }>} patch
|
||||||
*/
|
*/
|
||||||
bareOsPublishBootReady(patch) {
|
bareOsPublishBootReady(patch) {
|
||||||
if (patch && typeof patch === 'object')
|
if (!patch || typeof patch !== 'object') return
|
||||||
Object.assign(bootReadyStateRef, patch)
|
if (patch.subsystems && typeof patch.subsystems === 'object') {
|
||||||
|
Object.assign(bootReadyStateRef.subsystems, patch.subsystems)
|
||||||
|
}
|
||||||
|
for (const key of Object.keys(patch)) {
|
||||||
|
if (key === 'subsystems') continue
|
||||||
|
bootReadyStateRef[key] = patch[key]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/** Mutable ref mirrored in `/proc/bare_os_session_stats` (execLineCount, pipelineBytesTotal). */
|
/** Mutable ref mirrored in `/proc/bare_os_session_stats` (execLineCount, pipelineBytesTotal). */
|
||||||
bareOsSessionStats: sessionStatsRef,
|
bareOsSessionStats: sessionStatsRef,
|
||||||
@@ -735,7 +792,12 @@ async function executeKernel(disk, store, swarm, initSource) {
|
|||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {() => string | Uint8Array | Promise<string | Uint8Array>} reader
|
* @param {() => string | Uint8Array | Promise<string | Uint8Array>} reader
|
||||||
*/
|
*/
|
||||||
bareOsRegisterVirtualFile(name, reader) {
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @param {(() => string | Uint8Array | Promise<string | Uint8Array>) | { read: typeof reader }} reader
|
||||||
|
* @param {{ etag?: string }} [opts]
|
||||||
|
*/
|
||||||
|
bareOsRegisterVirtualFile(name, reader, opts = {}) {
|
||||||
const caps = this.bareOsRuntimeCaps
|
const caps = this.bareOsRuntimeCaps
|
||||||
const ok =
|
const ok =
|
||||||
caps &&
|
caps &&
|
||||||
@@ -748,30 +810,135 @@ async function executeKernel(disk, store, swarm, initSource) {
|
|||||||
if (!name || !/^[a-zA-Z0-9._-]+$/.test(String(name))) {
|
if (!name || !/^[a-zA-Z0-9._-]+$/.test(String(name))) {
|
||||||
throw new Error('bareOsRegisterVirtualFile: invalid name')
|
throw new Error('bareOsRegisterVirtualFile: invalid name')
|
||||||
}
|
}
|
||||||
if (typeof reader !== 'function') {
|
const readFn =
|
||||||
|
typeof reader === 'function'
|
||||||
|
? reader
|
||||||
|
: reader &&
|
||||||
|
typeof reader === 'object' &&
|
||||||
|
typeof reader.read === 'function'
|
||||||
|
? reader.read
|
||||||
|
: null
|
||||||
|
if (typeof readFn !== 'function') {
|
||||||
throw new Error('bareOsRegisterVirtualFile: reader must be a function')
|
throw new Error('bareOsRegisterVirtualFile: reader must be a function')
|
||||||
}
|
}
|
||||||
virtualReaders.set(String(name), reader)
|
virtualReaderEntries.set(String(name), {
|
||||||
|
read: readFn,
|
||||||
|
etag: String(opts.etag != null ? opts.etag : '1'),
|
||||||
|
version: 1
|
||||||
|
})
|
||||||
},
|
},
|
||||||
/** Placeholder for future Bare worker / Pear isolate execution (see developer guide). */
|
bareOsInvalidateVirtualFile(name) {
|
||||||
async bareOsSandboxRunScript() {
|
virtualReaderEntries.delete(String(name))
|
||||||
throw new Error(
|
},
|
||||||
'bareOsSandboxRunScript is not implemented; see developer-guide/09-security-and-trust.md'
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @param {{ etag?: string, version?: number }} patch
|
||||||
|
*/
|
||||||
|
bareOsUpdateVirtualFileMeta(name, patch) {
|
||||||
|
const e = virtualReaderEntries.get(String(name))
|
||||||
|
if (!e) throw new Error('bareOsUpdateVirtualFileMeta: unknown virtual file')
|
||||||
|
if (patch.etag != null) e.etag = String(patch.etag)
|
||||||
|
if (patch.version != null && Number.isFinite(Number(patch.version))) {
|
||||||
|
e.version = Number(patch.version)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {string} phase e.g. `os-release`, `before:rc`, `after:rc`, or `*`
|
||||||
|
* @param {(ev: { phase: string, when?: string, label?: string }) => void | Promise<void>} fn
|
||||||
|
* @returns {() => void} unsubscribe
|
||||||
|
*/
|
||||||
|
bareOsRegisterBootPhaseHook(phase, fn) {
|
||||||
|
if (typeof fn !== 'function' || !phase) return () => {}
|
||||||
|
const entry = { phase: String(phase), fn }
|
||||||
|
bootPhaseHooks.push(entry)
|
||||||
|
return () => {
|
||||||
|
const i = bootPhaseHooks.indexOf(entry)
|
||||||
|
if (i >= 0) bootPhaseHooks.splice(i, 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** @param {{ phase: string, when?: string, label?: string }} ev */
|
||||||
|
async bareOsInvokeBootPhaseHooks(ev) {
|
||||||
|
if (!ev || typeof ev !== 'object') return
|
||||||
|
for (const { phase, fn } of bootPhaseHooks) {
|
||||||
|
const p = String(ev.phase || '')
|
||||||
|
const w = ev.when ? `${ev.when}:${p}` : p
|
||||||
|
if (phase === '*' || phase === w || phase === p) {
|
||||||
|
try {
|
||||||
|
await fn(ev)
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {string} source
|
||||||
|
* @param {string[]} [argv]
|
||||||
|
* @param {{ signal?: AbortSignal, timeoutMs?: number }} [runOpts]
|
||||||
|
*/
|
||||||
|
async bareOsSandboxRunScript(source, argv = [], runOpts) {
|
||||||
|
const caps = this.bareOsRuntimeCaps
|
||||||
|
if (
|
||||||
|
!caps?.features ||
|
||||||
|
/** @type {{ sandboxScript?: boolean }} */ (caps.features)
|
||||||
|
.sandboxScript === false
|
||||||
|
) {
|
||||||
|
throw new Error('bareOsSandboxRunScript: feature disabled')
|
||||||
|
}
|
||||||
|
if (shellEnv.BARE_OS_SANDBOX_SCRIPT === '0') {
|
||||||
|
throw new Error('bareOsSandboxRunScript: disabled by BARE_OS_SANDBOX_SCRIPT=0')
|
||||||
|
}
|
||||||
|
const sb = createBareOsSandboxContext(this)
|
||||||
|
const { raceWithAbortAndTimeout } = await import('./lib/bare-os-abort.js')
|
||||||
|
return raceWithAbortAndTimeout(
|
||||||
|
runUserScriptFromSource(sb, String(source), argv, 'sandbox'),
|
||||||
|
runOpts,
|
||||||
|
'bareOsSandboxRunScript'
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Hint object for Pear OTA / channel reload (host must implement).
|
* Hint object for Pear OTA / channel reload (host must implement).
|
||||||
* @returns {{ requested: boolean, hint: string, env: string[] }}
|
* @returns {{ requested: boolean, hint: string, env: string[] }}
|
||||||
*/
|
*/
|
||||||
bareOsRequestPearReload() {
|
async bareOsRequestPearReload(opts = {}) {
|
||||||
const env = []
|
const env = []
|
||||||
const ch = shellEnv.BARE_OS_PEAR_CHANNEL || shellEnv.PEAR_CHANNEL
|
const ch = shellEnv.BARE_OS_PEAR_CHANNEL || shellEnv.PEAR_CHANNEL
|
||||||
if (ch) env.push(`PEAR_CHANNEL=${ch}`)
|
if (ch) env.push(`PEAR_CHANNEL=${ch}`)
|
||||||
if (shellEnv.BARE_OS_PEAR_RELEASE)
|
if (shellEnv.BARE_OS_PEAR_RELEASE)
|
||||||
env.push(`BARE_OS_PEAR_RELEASE=${shellEnv.BARE_OS_PEAR_RELEASE}`)
|
env.push(`BARE_OS_PEAR_RELEASE=${shellEnv.BARE_OS_PEAR_RELEASE}`)
|
||||||
|
let requested = false
|
||||||
|
if (opts && opts.persistRequest) {
|
||||||
|
try {
|
||||||
|
const line = JSON.stringify({
|
||||||
|
ts: Date.now(),
|
||||||
|
channel: String(ch || ''),
|
||||||
|
release: String(shellEnv.BARE_OS_PEAR_RELEASE || '')
|
||||||
|
})
|
||||||
|
await vfs.mkdir('~/.bare-os', { recursive: true })
|
||||||
|
await vfs.writeFile(
|
||||||
|
'~/.bare-os/pear-reload.request',
|
||||||
|
b4a.from(line + '\n', 'utf8')
|
||||||
|
)
|
||||||
|
requested = true
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof globalThis.process?.emit === 'function') {
|
||||||
|
try {
|
||||||
|
globalThis.process.emit('bare-os:pear-reload', {
|
||||||
|
channel: ch,
|
||||||
|
release: shellEnv.BARE_OS_PEAR_RELEASE
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
requested: false,
|
requested,
|
||||||
hint: 'Host Pear runtime (pear-runtime-updater) must apply reload; kernel only exposes env hints.',
|
hint: requested
|
||||||
|
? 'Reload marker written to ~/.bare-os/pear-reload.request; host Pear / pear-runtime-updater should consume it.'
|
||||||
|
: 'Pass { persistRequest: true } to write ~/.bare-os/pear-reload.request; host may listen for process "bare-os:pear-reload".',
|
||||||
env
|
env
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -782,6 +949,14 @@ async function executeKernel(disk, store, swarm, initSource) {
|
|||||||
/** Preset names for `/bin/theme` and docs. */
|
/** Preset names for `/bin/theme` and docs. */
|
||||||
bareOsListThemes() {
|
bareOsListThemes() {
|
||||||
return bareOsListThemeNames()
|
return bareOsListThemeNames()
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* SHA-256 hex digest for boot manifest checks (stock kernel).
|
||||||
|
* @param {Uint8Array | ArrayBuffer} buf
|
||||||
|
*/
|
||||||
|
bareOsBootFileSha256Hex(buf) {
|
||||||
|
const u8 = buf instanceof Uint8Array ? buf : new Uint8Array(buf)
|
||||||
|
return createHash('sha256').update(u8).digest('hex')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,10 +11,14 @@ export const BARE_INITD_DISABLED_FILE = '~/.config/bare-os/initd/disabled.txt'
|
|||||||
/**
|
/**
|
||||||
* Optional `name.unit` files under this directory. Supports `[Unit]` keys:
|
* Optional `name.unit` files under this directory. Supports `[Unit]` keys:
|
||||||
* After=, Requires=, Wants=, TimeoutStartSec=, TimeoutStopSec=, Restart=, RestartSec=, ExecStartPost=, SocketActivationIpc=,
|
* After=, Requires=, Wants=, TimeoutStartSec=, TimeoutStopSec=, Restart=, RestartSec=, ExecStartPost=, SocketActivationIpc=,
|
||||||
* ReadinessPath= (VFS path until exists), ReadinessTimeoutSec=.
|
* ReadinessPath= (VFS path until exists), ReadinessTimeoutSec=,
|
||||||
|
* ExecHealthCmd=, HealthIntervalSec=, HealthFailureThreshold=, BareMaxExecDepth=.
|
||||||
*/
|
*/
|
||||||
export const BARE_INITD_UNITS_DIR = '~/.config/bare-os/units'
|
export const BARE_INITD_UNITS_DIR = '~/.config/bare-os/units'
|
||||||
|
|
||||||
|
/** `systemctl --user` style overrides (merged on top of {@link BARE_INITD_UNITS_DIR}). */
|
||||||
|
export const BARE_INITD_SYSTEMCTL_USER_UNITS_DIR = '~/.config/bare-init/units'
|
||||||
|
|
||||||
/** Timer drop-ins: `[Timer]` with OnCalendar= (five cron fields) and ExecLine=. */
|
/** Timer drop-ins: `[Timer]` with OnCalendar= (five cron fields) and ExecLine=. */
|
||||||
export const BARE_INITD_TIMERS_DIR = '~/.config/bare-os/timers'
|
export const BARE_INITD_TIMERS_DIR = '~/.config/bare-os/timers'
|
||||||
|
|
||||||
@@ -35,7 +39,11 @@ export const BARE_INITD_DEFAULT_AFTER = Object.freeze({
|
|||||||
* execStartPost: string | null,
|
* execStartPost: string | null,
|
||||||
* socketActivationIpc: string | null,
|
* socketActivationIpc: string | null,
|
||||||
* readinessPath: string | null,
|
* readinessPath: string | null,
|
||||||
* readinessTimeoutSec: number | null
|
* readinessTimeoutSec: number | null,
|
||||||
|
* execHealthCmd: string | null,
|
||||||
|
* healthIntervalSec: number | null,
|
||||||
|
* healthFailureThreshold: number | null,
|
||||||
|
* bareMaxExecDepth: number | null
|
||||||
* }} BareInitdUnitDropIn
|
* }} BareInitdUnitDropIn
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -52,7 +60,11 @@ export function emptyUnitDropIn() {
|
|||||||
execStartPost: null,
|
execStartPost: null,
|
||||||
socketActivationIpc: null,
|
socketActivationIpc: null,
|
||||||
readinessPath: null,
|
readinessPath: null,
|
||||||
readinessTimeoutSec: null
|
readinessTimeoutSec: null,
|
||||||
|
execHealthCmd: null,
|
||||||
|
healthIntervalSec: null,
|
||||||
|
healthFailureThreshold: null,
|
||||||
|
bareMaxExecDepth: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +122,17 @@ export function parseUnitDropInText(text) {
|
|||||||
} else if (key === 'readinesstimeoutsec') {
|
} else if (key === 'readinesstimeoutsec') {
|
||||||
const n = Number.parseInt(val, 10)
|
const n = Number.parseInt(val, 10)
|
||||||
if (Number.isFinite(n) && n > 0) out.readinessTimeoutSec = n
|
if (Number.isFinite(n) && n > 0) out.readinessTimeoutSec = n
|
||||||
|
} else if (key === 'exechealthcmd') {
|
||||||
|
if (val.trim()) out.execHealthCmd = val.trim()
|
||||||
|
} else if (key === 'healthintervalsec') {
|
||||||
|
const n = Number.parseInt(val, 10)
|
||||||
|
if (Number.isFinite(n) && n > 0) out.healthIntervalSec = n
|
||||||
|
} else if (key === 'healthfailurethreshold') {
|
||||||
|
const n = Number.parseInt(val, 10)
|
||||||
|
if (Number.isFinite(n) && n > 0) out.healthFailureThreshold = n
|
||||||
|
} else if (key === 'baremaxexecdepth') {
|
||||||
|
const n = Number.parseInt(val, 10)
|
||||||
|
if (Number.isFinite(n) && n > 0) out.bareMaxExecDepth = n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.after = [...new Set(out.after)]
|
out.after = [...new Set(out.after)]
|
||||||
@@ -118,6 +141,50 @@ export function parseUnitDropInText(text) {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {BareInitdUnitDropIn} base
|
||||||
|
* @param {BareInitdUnitDropIn} user
|
||||||
|
* @returns {BareInitdUnitDropIn}
|
||||||
|
*/
|
||||||
|
export function mergeUnitDropIns(base, user) {
|
||||||
|
const pickArr = (u, b) =>
|
||||||
|
u.length ? [...new Set(u)] : [...new Set(b)]
|
||||||
|
return {
|
||||||
|
after: pickArr(user.after, base.after),
|
||||||
|
requires: pickArr(user.requires, base.requires),
|
||||||
|
wants: pickArr(user.wants, base.wants),
|
||||||
|
timeoutStartSec: user.timeoutStartSec ?? base.timeoutStartSec,
|
||||||
|
timeoutStopSec: user.timeoutStopSec ?? base.timeoutStopSec,
|
||||||
|
restart: user.restart ?? base.restart,
|
||||||
|
restartSec: user.restartSec ?? base.restartSec,
|
||||||
|
execStartPost: user.execStartPost ?? base.execStartPost,
|
||||||
|
socketActivationIpc: user.socketActivationIpc ?? base.socketActivationIpc,
|
||||||
|
readinessPath: user.readinessPath ?? base.readinessPath,
|
||||||
|
readinessTimeoutSec:
|
||||||
|
user.readinessTimeoutSec ?? base.readinessTimeoutSec,
|
||||||
|
execHealthCmd: user.execHealthCmd ?? base.execHealthCmd,
|
||||||
|
healthIntervalSec: user.healthIntervalSec ?? base.healthIntervalSec,
|
||||||
|
healthFailureThreshold:
|
||||||
|
user.healthFailureThreshold ?? base.healthFailureThreshold,
|
||||||
|
bareMaxExecDepth: user.bareMaxExecDepth ?? base.bareMaxExecDepth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{ readFile: (p: string) => Promise<Uint8Array | null> }} vfs
|
||||||
|
* @param {string} logicalPath
|
||||||
|
* @returns {Promise<BareInitdUnitDropIn>}
|
||||||
|
*/
|
||||||
|
export async function readUnitDropInFromPath(vfs, logicalPath) {
|
||||||
|
try {
|
||||||
|
const buf = await vfs.readFile(logicalPath)
|
||||||
|
if (!buf) return emptyUnitDropIn()
|
||||||
|
return parseUnitDropInText(b4a.toString(buf, 'utf8'))
|
||||||
|
} catch {
|
||||||
|
return emptyUnitDropIn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{ readFile: (p: string) => Promise<Uint8Array | null> }} vfs
|
* @param {{ readFile: (p: string) => Promise<Uint8Array | null> }} vfs
|
||||||
* @returns {Promise<Set<string>>}
|
* @returns {Promise<Set<string>>}
|
||||||
@@ -168,14 +235,15 @@ export async function writeInitdDisabledSet(vfs, disabled) {
|
|||||||
*/
|
*/
|
||||||
export async function readUnitDropIn(vfs, unitName) {
|
export async function readUnitDropIn(vfs, unitName) {
|
||||||
if (!/^[a-zA-Z0-9._-]+$/.test(unitName)) return emptyUnitDropIn()
|
if (!/^[a-zA-Z0-9._-]+$/.test(unitName)) return emptyUnitDropIn()
|
||||||
const path = `${BARE_INITD_UNITS_DIR}/${unitName}.unit`
|
const base = await readUnitDropInFromPath(
|
||||||
try {
|
vfs,
|
||||||
const buf = await vfs.readFile(path)
|
`${BARE_INITD_UNITS_DIR}/${unitName}.unit`
|
||||||
if (!buf) return emptyUnitDropIn()
|
)
|
||||||
return parseUnitDropInText(b4a.toString(buf, 'utf8'))
|
const usr = await readUnitDropInFromPath(
|
||||||
} catch {
|
vfs,
|
||||||
return emptyUnitDropIn()
|
`${BARE_INITD_SYSTEMCTL_USER_UNITS_DIR}/${unitName}.unit`
|
||||||
}
|
)
|
||||||
|
return mergeUnitDropIns(base, usr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ const registry = []
|
|||||||
/** @type {Map<string, BareServiceRuntime>} */
|
/** @type {Map<string, BareServiceRuntime>} */
|
||||||
const runtime = new Map()
|
const runtime = new Map()
|
||||||
|
|
||||||
|
/** @type {Map<string, ReturnType<typeof setInterval>>} */
|
||||||
|
const unitHealthTimers = new Map()
|
||||||
|
|
||||||
/** @type {(() => void)[]} */
|
/** @type {(() => void)[]} */
|
||||||
const disposers = []
|
const disposers = []
|
||||||
|
|
||||||
@@ -82,6 +85,8 @@ export async function runKernelShutdownHooks() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function stopBareInitd() {
|
export function stopBareInitd() {
|
||||||
|
for (const t of unitHealthTimers.values()) clearInterval(t)
|
||||||
|
unitHealthTimers.clear()
|
||||||
for (const fn of disposers) {
|
for (const fn of disposers) {
|
||||||
try {
|
try {
|
||||||
fn()
|
fn()
|
||||||
@@ -238,6 +243,56 @@ async function withTimeoutSec(promise, sec, label) {
|
|||||||
* @param {string} logicalPath
|
* @param {string} logicalPath
|
||||||
* @param {number} timeoutSec
|
* @param {number} timeoutSec
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* @param {Record<string, unknown>} ctx
|
||||||
|
* @param {string} name
|
||||||
|
* @param {import('./bare-initd-user.js').BareInitdUnitDropIn} dropIn
|
||||||
|
*/
|
||||||
|
function scheduleUnitHealth(ctx, name, dropIn) {
|
||||||
|
if (!dropIn.execHealthCmd || !dropIn.healthIntervalSec) return
|
||||||
|
if (typeof ctx.execLine !== 'function') return
|
||||||
|
const intervalMs = Math.max(5, dropIn.healthIntervalSec) * 1000
|
||||||
|
const threshold = Math.max(1, dropIn.healthFailureThreshold ?? 3)
|
||||||
|
let fails = 0
|
||||||
|
const t = setInterval(() => {
|
||||||
|
void (async () => {
|
||||||
|
const rt = runtime.get(name)
|
||||||
|
if (!rt || rt.phase !== 'active') return
|
||||||
|
const env = ctx.vfs?.env
|
||||||
|
const saved = env ? env.BARE_OS_EXEC_MAX_DEPTH : undefined
|
||||||
|
if (dropIn.bareMaxExecDepth != null && env) {
|
||||||
|
env.BARE_OS_EXEC_MAX_DEPTH = String(dropIn.bareMaxExecDepth)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await ctx.execLine(dropIn.execHealthCmd.trim())
|
||||||
|
if ((Number(ctx.exitCode) || 0) === 0) fails = 0
|
||||||
|
else fails++
|
||||||
|
} catch {
|
||||||
|
fails++
|
||||||
|
} finally {
|
||||||
|
if (env && saved !== undefined) env.BARE_OS_EXEC_MAX_DEPTH = saved
|
||||||
|
}
|
||||||
|
if (fails >= threshold) {
|
||||||
|
clearInterval(t)
|
||||||
|
unitHealthTimers.delete(name)
|
||||||
|
const msg = 'health check failed'
|
||||||
|
runtime.set(name, {
|
||||||
|
phase: 'failed',
|
||||||
|
startedAtMs: Date.now(),
|
||||||
|
error: msg
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
ctx.console?.error?.(`[bare-initd] ${name}: ${msg}`)
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
void appendVarLog(ctx, INITD_LOG, name, msg)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
}, intervalMs)
|
||||||
|
unitHealthTimers.set(name, t)
|
||||||
|
}
|
||||||
|
|
||||||
async function waitForReadinessPath(ctx, logicalPath, timeoutSec) {
|
async function waitForReadinessPath(ctx, logicalPath, timeoutSec) {
|
||||||
const vfs = ctx.vfs
|
const vfs = ctx.vfs
|
||||||
if (!vfs || typeof vfs.exists !== 'function') return
|
if (!vfs || typeof vfs.exists !== 'function') return
|
||||||
@@ -257,6 +312,11 @@ async function waitForReadinessPath(ctx, logicalPath, timeoutSec) {
|
|||||||
export async function stopBareService(ctx, name) {
|
export async function stopBareService(ctx, name) {
|
||||||
const s = findBareServiceDefinition(name)
|
const s = findBareServiceDefinition(name)
|
||||||
if (!s) throw new Error(`Unknown unit: ${name}`)
|
if (!s) throw new Error(`Unknown unit: ${name}`)
|
||||||
|
const ht = unitHealthTimers.get(name)
|
||||||
|
if (ht) {
|
||||||
|
clearInterval(ht)
|
||||||
|
unitHealthTimers.delete(name)
|
||||||
|
}
|
||||||
if (typeof s.stop !== 'function') {
|
if (typeof s.stop !== 'function') {
|
||||||
throw new Error(`Unit ${name} does not support stop (no stop handler)`)
|
throw new Error(`Unit ${name} does not support stop (no stop handler)`)
|
||||||
}
|
}
|
||||||
@@ -387,6 +447,7 @@ export async function startBareInitd(ctx) {
|
|||||||
void appendVarLog(ctx, INITD_LOG, s.name, 'ExecStartPost: ' + msg)
|
void appendVarLog(ctx, INITD_LOG, s.name, 'ExecStartPost: ' + msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
scheduleUnitHealth(ctx, s.name, dropIn)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = e?.message || String(e)
|
const msg = e?.message || String(e)
|
||||||
runtime.set(s.name, { phase: 'failed', startedAtMs: t0, error: msg })
|
runtime.set(s.name, { phase: 'failed', startedAtMs: t0, error: msg })
|
||||||
@@ -435,6 +496,7 @@ export async function startBareInitd(ctx) {
|
|||||||
void appendVarLog(ctx, INITD_LOG, s.name, 'ExecStartPost: ' + msg)
|
void appendVarLog(ctx, INITD_LOG, s.name, 'ExecStartPost: ' + msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
scheduleUnitHealth(ctx, s.name, dropIn)
|
||||||
break
|
break
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = e?.message || String(e)
|
const msg = e?.message || String(e)
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
* Semantic version of the booter `ctx` contract for custom kernels.
|
* Semantic version of the booter `ctx` contract for custom kernels.
|
||||||
* Bump when adding/removing/renaming documented `ctx` fields or changing behavior.
|
* Bump when adding/removing/renaming documented `ctx` fields or changing behavior.
|
||||||
*/
|
*/
|
||||||
export const BARE_OS_CTX_API_VERSION = '1.7.1'
|
export const BARE_OS_CTX_API_VERSION = '1.8.0'
|
||||||
|
|||||||
@@ -212,6 +212,43 @@ export async function buildBareCtxObjectFromHost(shellEnv, target) {
|
|||||||
* @param {{ readFile: (p: string, opts?: unknown) => Promise<Uint8Array | null> }} vfs
|
* @param {{ readFile: (p: string, opts?: unknown) => Promise<Uint8Array | null> }} vfs
|
||||||
* @param {Record<string, unknown>} target
|
* @param {Record<string, unknown>} target
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* Optional `/lib/bare/bare-module-lock.json` on the system image: `{ "pins": { "ctxKey": "semver" } }`.
|
||||||
|
* Emits warnings for pins whose `ctxKey` is still missing after drive merge + host import.
|
||||||
|
* @param {{ readFile: (p: string) => Promise<Uint8Array | null> }} vfs
|
||||||
|
* @param {Record<string, unknown>} target
|
||||||
|
* @returns {Promise<string[]>}
|
||||||
|
*/
|
||||||
|
export async function verifyBareModuleLockfile(vfs, target) {
|
||||||
|
/** @type {string[]} */
|
||||||
|
const warnings = []
|
||||||
|
if (!vfs || typeof vfs.readFile !== 'function') return warnings
|
||||||
|
let buf
|
||||||
|
try {
|
||||||
|
buf = await vfs.readFile('/lib/bare/bare-module-lock.json')
|
||||||
|
} catch {
|
||||||
|
return warnings
|
||||||
|
}
|
||||||
|
if (!buf || !buf.byteLength) return warnings
|
||||||
|
let j
|
||||||
|
try {
|
||||||
|
j = JSON.parse(new TextDecoder().decode(buf))
|
||||||
|
} catch {
|
||||||
|
warnings.push('bare-module-lock.json: invalid JSON')
|
||||||
|
return warnings
|
||||||
|
}
|
||||||
|
const pins = j && typeof j === 'object' ? j.pins : null
|
||||||
|
if (!pins || typeof pins !== 'object') return warnings
|
||||||
|
for (const [k, ver] of Object.entries(pins)) {
|
||||||
|
if (target[k] === undefined) {
|
||||||
|
warnings.push(
|
||||||
|
`bare-module-lock: ctx.bare.${k} missing (pinned ${String(ver)})`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return warnings
|
||||||
|
}
|
||||||
|
|
||||||
export async function maybeMergeBareFromDrive(shellEnv, vfs, target) {
|
export async function maybeMergeBareFromDrive(shellEnv, vfs, target) {
|
||||||
if (!bareOsBareModulesEnabled(shellEnv)) return
|
if (!bareOsBareModulesEnabled(shellEnv)) return
|
||||||
if (!bareOsBareDriveBundlesEnabled(shellEnv)) return
|
if (!bareOsBareDriveBundlesEnabled(shellEnv)) return
|
||||||
|
|||||||
+34
-4
@@ -46,6 +46,16 @@ export interface BareOsIpc {
|
|||||||
fanoutTopicCount: number
|
fanoutTopicCount: number
|
||||||
fanoutSubscribersTotal: number
|
fanoutSubscribersTotal: number
|
||||||
}
|
}
|
||||||
|
createDuplexBridge(baseName: string): {
|
||||||
|
left: {
|
||||||
|
push: (buf: Uint8Array | ArrayBuffer) => void
|
||||||
|
take: () => Promise<Uint8Array>
|
||||||
|
}
|
||||||
|
right: {
|
||||||
|
push: (buf: Uint8Array | ArrayBuffer) => void
|
||||||
|
take: () => Promise<Uint8Array>
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BareOsHostStats {
|
export interface BareOsHostStats {
|
||||||
@@ -89,14 +99,34 @@ export interface BareOsKernelContext {
|
|||||||
bareOsGetResourceStatus(): BareOsResourceStatus
|
bareOsGetResourceStatus(): BareOsResourceStatus
|
||||||
bareOsRegisterVirtualFile(
|
bareOsRegisterVirtualFile(
|
||||||
name: string,
|
name: string,
|
||||||
reader: () => string | Uint8Array | Promise<string | Uint8Array>
|
reader:
|
||||||
|
| (() => string | Uint8Array | Promise<string | Uint8Array>)
|
||||||
|
| { read: () => string | Uint8Array | Promise<string | Uint8Array> },
|
||||||
|
opts?: { etag?: string }
|
||||||
): void
|
): void
|
||||||
bareOsSandboxRunScript(): Promise<never>
|
bareOsInvalidateVirtualFile(name: string): void
|
||||||
bareOsRequestPearReload(): {
|
bareOsUpdateVirtualFileMeta(
|
||||||
|
name: string,
|
||||||
|
patch: { etag?: string; version?: number }
|
||||||
|
): void
|
||||||
|
bareOsRegisterBootPhaseHook(
|
||||||
|
phase: string,
|
||||||
|
fn: (ev: Record<string, unknown>) => void | Promise<void>
|
||||||
|
): () => void
|
||||||
|
bareOsInvokeBootPhaseHooks(ev: Record<string, unknown>): Promise<void>
|
||||||
|
bareOsSandboxRunScript(
|
||||||
|
source: string,
|
||||||
|
argv?: string[],
|
||||||
|
opts?: BareOsAbortOpts
|
||||||
|
): Promise<void>
|
||||||
|
bareOsBootFileSha256Hex(buf: Uint8Array | ArrayBuffer): string
|
||||||
|
bareOsRequestPearReload(opts?: {
|
||||||
|
persistRequest?: boolean
|
||||||
|
}): Promise<{
|
||||||
requested: boolean
|
requested: boolean
|
||||||
hint: string
|
hint: string
|
||||||
env: string[]
|
env: string[]
|
||||||
}
|
}>
|
||||||
bareOsHostStats?: Readonly<BareOsHostStats>
|
bareOsHostStats?: Readonly<BareOsHostStats>
|
||||||
/** Host-resolved (and optional drive-bundled) Holepunch-style modules; absent when `BARE_OS_BARE_MODULES=0`. */
|
/** Host-resolved (and optional drive-bundled) Holepunch-style modules; absent when `BARE_OS_BARE_MODULES=0`. */
|
||||||
bare?: Readonly<Record<string, unknown>>
|
bare?: Readonly<Record<string, unknown>>
|
||||||
|
|||||||
@@ -275,6 +275,26 @@ export function createBareOsIpc(opts = {}) {
|
|||||||
fanoutTopicCount: fanouts.size,
|
fanoutTopicCount: fanouts.size,
|
||||||
fanoutSubscribersTotal
|
fanoutSubscribersTotal
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bounded duplex: side A's `push` delivers to side B's `take`, and vice versa.
|
||||||
|
* @param {string} baseName
|
||||||
|
* @returns {{ left: { push: (buf: Uint8Array | ArrayBuffer) => void, take: () => Promise<Uint8Array> }, right: { push: (buf: Uint8Array | ArrayBuffer) => void, take: () => Promise<Uint8Array> } }}
|
||||||
|
*/
|
||||||
|
createDuplexBridge(baseName) {
|
||||||
|
assertSafeIpcName(baseName)
|
||||||
|
const ab = new FifoChannel()
|
||||||
|
const ba = new FifoChannel()
|
||||||
|
const left = {
|
||||||
|
push: (buf) => ba.push(buf, maxFifoBytes),
|
||||||
|
take: () => ab.take()
|
||||||
|
}
|
||||||
|
const right = {
|
||||||
|
push: (buf) => ab.push(buf, maxFifoBytes),
|
||||||
|
take: () => ba.take()
|
||||||
|
}
|
||||||
|
return { left, right }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export const BARE_OS_PSEUDO_FS_PATHS = Object.freeze([
|
|||||||
'/proc/bare_os_features',
|
'/proc/bare_os_features',
|
||||||
'/proc/bare_os_quotas',
|
'/proc/bare_os_quotas',
|
||||||
'/proc/bare_os_resources',
|
'/proc/bare_os_resources',
|
||||||
|
'/proc/bare_os_swarm',
|
||||||
'/proc/bare_os_session_stats',
|
'/proc/bare_os_session_stats',
|
||||||
'/proc/bare_os_version',
|
'/proc/bare_os_version',
|
||||||
'/proc/cpuinfo',
|
'/proc/cpuinfo',
|
||||||
@@ -150,7 +151,21 @@ export function buildBareOsRuntimeCaps(shellEnv) {
|
|||||||
bareOsBareHostImportsEnabled(shellEnv),
|
bareOsBareHostImportsEnabled(shellEnv),
|
||||||
bareDriveBundles:
|
bareDriveBundles:
|
||||||
bareOsBareModulesEnabled(shellEnv) &&
|
bareOsBareModulesEnabled(shellEnv) &&
|
||||||
bareOsBareDriveBundlesEnabled(shellEnv)
|
bareOsBareDriveBundlesEnabled(shellEnv),
|
||||||
|
sandboxScript:
|
||||||
|
shellEnv.BARE_OS_SANDBOX_SCRIPT !== '0' &&
|
||||||
|
shellEnv.BARE_OS_SANDBOX_SCRIPT !== 'false',
|
||||||
|
bootPhaseHooks: true,
|
||||||
|
ipcDuplexBridge: true,
|
||||||
|
vfsUnionRead:
|
||||||
|
shellEnv.BARE_OS_VFS_UNION_PREFIXES != null &&
|
||||||
|
String(shellEnv.BARE_OS_VFS_UNION_PREFIXES).trim() !== '',
|
||||||
|
procSwarmSnapshot: true,
|
||||||
|
bootManifestVerify:
|
||||||
|
shellEnv.BARE_OS_BOOT_MANIFEST === '1' ||
|
||||||
|
shellEnv.BARE_OS_BOOT_MANIFEST === 'true',
|
||||||
|
bareModuleLockfile: true,
|
||||||
|
oidcPublishHook: true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* Reduced `ctx` for `bareOsSandboxRunScript`: personal-drive writes only, no identity hooks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { isPersonalRoute } from './vfs-posix-meta.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Record<string, unknown>} ctx
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function sandboxWritesOk(ctx, logicalPath) {
|
||||||
|
const vfs = ctx.vfs
|
||||||
|
if (!vfs || typeof vfs.resolveLogical !== 'function') return false
|
||||||
|
if (typeof vfs.route !== 'function') return false
|
||||||
|
const abs = vfs.resolveLogical(logicalPath)
|
||||||
|
const r = vfs.route(abs)
|
||||||
|
const pd = ctx.personalDrive
|
||||||
|
if (!pd) return false
|
||||||
|
return isPersonalRoute(pd, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Record<string, unknown>} ctx
|
||||||
|
*/
|
||||||
|
export function createBareOsSandboxContext(ctx) {
|
||||||
|
const vfs = ctx.vfs
|
||||||
|
if (!vfs) throw new Error('bareOsSandboxRunScript: missing vfs')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @param {(...args: unknown[]) => unknown} fn
|
||||||
|
* @param {number} pathArgIndex
|
||||||
|
*/
|
||||||
|
const wrapWrite = (name, fn, pathArgIndex = 0) => {
|
||||||
|
if (typeof fn !== 'function') return fn
|
||||||
|
return async function (...args) {
|
||||||
|
const path = args[pathArgIndex]
|
||||||
|
if (typeof path === 'string' && !sandboxWritesOk(ctx, path)) {
|
||||||
|
throw new Error(`sandbox: ${name}: write denied outside personal namespace`)
|
||||||
|
}
|
||||||
|
return fn.apply(vfs, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sandboxVfs = Object.assign(Object.create(Object.getPrototypeOf(vfs)), vfs, {
|
||||||
|
writeFile: wrapWrite('writeFile', vfs.writeFile, 0),
|
||||||
|
mkdir: wrapWrite('mkdir', vfs.mkdir, 0),
|
||||||
|
unlink: wrapWrite('unlink', vfs.unlink, 0),
|
||||||
|
rmdir: wrapWrite('rmdir', vfs.rmdir, 0),
|
||||||
|
chmod: wrapWrite('chmod', vfs.chmod, 0),
|
||||||
|
chown: wrapWrite('chown', vfs.chown, 0),
|
||||||
|
symlink: wrapWrite('symlink', vfs.symlink, 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
/** @type {Record<string, unknown>} */
|
||||||
|
const o = Object.assign({}, ctx, {
|
||||||
|
vfs: sandboxVfs,
|
||||||
|
bareOsSandboxed: true,
|
||||||
|
async applyUnlock() {
|
||||||
|
throw new Error('sandbox: identity unlock disabled')
|
||||||
|
},
|
||||||
|
async applyRegister() {
|
||||||
|
throw new Error('sandbox: identity register disabled')
|
||||||
|
},
|
||||||
|
async applyLogin() {
|
||||||
|
throw new Error('sandbox: identity login disabled')
|
||||||
|
},
|
||||||
|
async applyLogout() {
|
||||||
|
throw new Error('sandbox: identity logout disabled')
|
||||||
|
},
|
||||||
|
async saveVault() {
|
||||||
|
throw new Error('sandbox: saveVault disabled')
|
||||||
|
},
|
||||||
|
registerKernelShutdownHook() {},
|
||||||
|
bareOsRegisterVirtualFile() {
|
||||||
|
throw new Error('sandbox: bareOsRegisterVirtualFile disabled')
|
||||||
|
},
|
||||||
|
bareOsInvalidateVirtualFile() {
|
||||||
|
throw new Error('sandbox: bareOsInvalidateVirtualFile disabled')
|
||||||
|
},
|
||||||
|
bareOsUpdateVirtualFileMeta() {
|
||||||
|
throw new Error('sandbox: bareOsUpdateVirtualFileMeta disabled')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
||||||
@@ -96,7 +96,14 @@ export async function runKernelFromSource(source, ctx) {
|
|||||||
* @param {string[]} argv
|
* @param {string[]} argv
|
||||||
* @param {string} [_label] reserved for diagnostics
|
* @param {string} [_label] reserved for diagnostics
|
||||||
*/
|
*/
|
||||||
async function runScriptFromSource(ctx, src, argv, _label = argv[0]) {
|
/**
|
||||||
|
* Evaluate user script source (same contract as `/bin` utilities).
|
||||||
|
* @param {Record<string, unknown>} ctx
|
||||||
|
* @param {string} src
|
||||||
|
* @param {string[]} argv
|
||||||
|
* @param {string} [_label]
|
||||||
|
*/
|
||||||
|
export async function runUserScriptFromSource(ctx, src, argv, _label = argv[0]) {
|
||||||
try {
|
try {
|
||||||
const body = stripShebang(src)
|
const body = stripShebang(src)
|
||||||
const fn = new AsyncFunction(
|
const fn = new AsyncFunction(
|
||||||
@@ -181,7 +188,7 @@ async function runBinCommandInner(ctx, argv) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
const source = b4a.toString(buf)
|
const source = b4a.toString(buf)
|
||||||
return runScriptFromSource(ctx, source, argv, cmd)
|
return runUserScriptFromSource(ctx, source, argv, cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// `script.js` in $PWD before PATH (same VFS routing as `./script.js`).
|
// `script.js` in $PWD before PATH (same VFS routing as `./script.js`).
|
||||||
@@ -191,7 +198,7 @@ async function runBinCommandInner(ctx, argv) {
|
|||||||
const buf = await drive.get(path, { follow: true })
|
const buf = await drive.get(path, { follow: true })
|
||||||
if (buf) {
|
if (buf) {
|
||||||
const source = b4a.toString(buf)
|
const source = b4a.toString(buf)
|
||||||
return runScriptFromSource(ctx, source, argv, cmd)
|
return runUserScriptFromSource(ctx, source, argv, cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +208,7 @@ async function runBinCommandInner(ctx, argv) {
|
|||||||
const buf = await systemDrive.get(p, { follow: true })
|
const buf = await systemDrive.get(p, { follow: true })
|
||||||
if (buf) {
|
if (buf) {
|
||||||
const source = b4a.toString(buf)
|
const source = b4a.toString(buf)
|
||||||
return runScriptFromSource(ctx, source, argv, p)
|
return runUserScriptFromSource(ctx, source, argv, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1173,6 +1173,8 @@ function splitTopLevelStatements(tokens) {
|
|||||||
if (t.type === 'word') {
|
if (t.type === 'word') {
|
||||||
if (t.value === 'if') depth++
|
if (t.value === 'if') depth++
|
||||||
else if (t.value === 'fi') depth = Math.max(0, depth - 1)
|
else if (t.value === 'fi') depth = Math.max(0, depth - 1)
|
||||||
|
else if (t.value === 'while' || t.value === 'for') depth++
|
||||||
|
else if (t.value === 'done') depth = Math.max(0, depth - 1)
|
||||||
}
|
}
|
||||||
if (t.type === 'op' && t.value === ';' && depth === 0) {
|
if (t.type === 'op' && t.value === ';' && depth === 0) {
|
||||||
if (cur.length) out.push(cur)
|
if (cur.length) out.push(cur)
|
||||||
@@ -1200,6 +1202,8 @@ function splitTopLevelByAmpersand(tokens) {
|
|||||||
if (t.type === 'word') {
|
if (t.type === 'word') {
|
||||||
if (t.value === 'if') depth++
|
if (t.value === 'if') depth++
|
||||||
else if (t.value === 'fi') depth = Math.max(0, depth - 1)
|
else if (t.value === 'fi') depth = Math.max(0, depth - 1)
|
||||||
|
else if (t.value === 'while' || t.value === 'for') depth++
|
||||||
|
else if (t.value === 'done') depth = Math.max(0, depth - 1)
|
||||||
}
|
}
|
||||||
if (t.type === 'op' && t.value === '&' && depth === 0) {
|
if (t.type === 'op' && t.value === '&' && depth === 0) {
|
||||||
out.push(cur)
|
out.push(cur)
|
||||||
@@ -1231,10 +1235,7 @@ function scheduleBackgroundShell(ctx, toks) {
|
|||||||
const statements = splitTopLevelStatements(toks)
|
const statements = splitTopLevelStatements(toks)
|
||||||
for (const stmt of statements) {
|
for (const stmt of statements) {
|
||||||
if (!stmt.length) continue
|
if (!stmt.length) continue
|
||||||
const r =
|
const r = await dispatchShellStatement(childCtx, stmt)
|
||||||
stmt[0]?.type === 'word' && stmt[0].value === 'if'
|
|
||||||
? await execIfConstruct(childCtx, stmt)
|
|
||||||
: await execAndOrList(childCtx, stmt)
|
|
||||||
if (r === 'exit') return r
|
if (r === 'exit') return r
|
||||||
}
|
}
|
||||||
syncBareOsExitStatusEnv(childCtx)
|
syncBareOsExitStatusEnv(childCtx)
|
||||||
@@ -1325,6 +1326,143 @@ async function execSemicolonLists(ctx, toks) {
|
|||||||
return 'ok'
|
return 'ok'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Token[]} tokens
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
function findWhileDoSplit(tokens) {
|
||||||
|
for (let j = 1; j < tokens.length - 2; j++) {
|
||||||
|
const t = tokens[j]
|
||||||
|
if (t.type === 'op' && t.value === ';') {
|
||||||
|
const n = tokens[j + 1]
|
||||||
|
if (n && n.type === 'word' && n.value === 'do') return j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Record<string, unknown>} ctx
|
||||||
|
* @param {Token[]} tokens
|
||||||
|
* @returns {Promise<'exit' | 'ok'>}
|
||||||
|
*/
|
||||||
|
async function execWhileConstruct(ctx, tokens) {
|
||||||
|
const split = findWhileDoSplit(tokens)
|
||||||
|
if (split < 0) {
|
||||||
|
ctx.console.error('shell: while: expected "; do …; done"')
|
||||||
|
ctx.exitCode = 2
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
const last = tokens[tokens.length - 1]
|
||||||
|
if (last.type !== 'word' || last.value !== 'done') {
|
||||||
|
ctx.console.error('shell: while: missing done')
|
||||||
|
ctx.exitCode = 2
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
const condToks = tokens.slice(1, split)
|
||||||
|
const bodyToks = tokens.slice(split + 2, tokens.length - 1)
|
||||||
|
const maxIter = Number.parseInt(
|
||||||
|
ctx.vfs?.env?.BARE_OS_SHELL_LOOP_MAX || '10000',
|
||||||
|
10
|
||||||
|
)
|
||||||
|
const cap = Number.isFinite(maxIter) && maxIter > 0 ? maxIter : 10000
|
||||||
|
for (let i = 0; i < cap; i++) {
|
||||||
|
const r0 = await execSemicolonLists(ctx, condToks)
|
||||||
|
if (r0 === 'exit') return 'exit'
|
||||||
|
if ((Number(ctx.exitCode) || 0) !== 0) break
|
||||||
|
const r1 = await execSemicolonLists(ctx, bodyToks)
|
||||||
|
if (r1 === 'exit') return 'exit'
|
||||||
|
}
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Record<string, unknown>} ctx
|
||||||
|
* @param {Token[]} tokens
|
||||||
|
* @returns {Promise<'exit' | 'ok'>}
|
||||||
|
*/
|
||||||
|
async function execForConstruct(ctx, tokens) {
|
||||||
|
const last = tokens[tokens.length - 1]
|
||||||
|
if (last.type !== 'word' || last.value !== 'done') {
|
||||||
|
ctx.console.error('shell: for: missing done')
|
||||||
|
ctx.exitCode = 2
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
if (tokens.length < 7 || tokens[1].type !== 'word') {
|
||||||
|
ctx.console.error('shell: for: invalid syntax')
|
||||||
|
ctx.exitCode = 2
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
if (tokens[2].type !== 'word' || tokens[2].value !== 'in') {
|
||||||
|
ctx.console.error('shell: for: expected `in`')
|
||||||
|
ctx.exitCode = 2
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
/** @type {Token[]} */
|
||||||
|
const inToks = []
|
||||||
|
let semi = -1
|
||||||
|
for (let j = 3; j < tokens.length; j++) {
|
||||||
|
const t = tokens[j]
|
||||||
|
if (t.type === 'op' && t.value === ';') {
|
||||||
|
semi = j
|
||||||
|
break
|
||||||
|
}
|
||||||
|
inToks.push(t)
|
||||||
|
}
|
||||||
|
if (semi < 0) {
|
||||||
|
ctx.console.error('shell: for: expected `;` before do')
|
||||||
|
ctx.exitCode = 2
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
tokens[semi + 1]?.type !== 'word' ||
|
||||||
|
tokens[semi + 1].value !== 'do'
|
||||||
|
) {
|
||||||
|
ctx.console.error('shell: for: expected `do` after `;`')
|
||||||
|
ctx.exitCode = 2
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
const varName = tokens[1].value
|
||||||
|
const bodyToks = tokens.slice(semi + 2, tokens.length - 1)
|
||||||
|
const env = ctx.vfs.env
|
||||||
|
const words = inToks
|
||||||
|
.filter((t) => t.type === 'word')
|
||||||
|
.map((t) => expandWord(/** @type {{ type: 'word', value: string }} */ (t).value, env))
|
||||||
|
const maxIter = Number.parseInt(
|
||||||
|
ctx.vfs?.env?.BARE_OS_SHELL_LOOP_MAX || '10000',
|
||||||
|
10
|
||||||
|
)
|
||||||
|
const cap = Number.isFinite(maxIter) && maxIter > 0 ? maxIter : 10000
|
||||||
|
let total = 0
|
||||||
|
for (const w of words) {
|
||||||
|
env[varName] = w
|
||||||
|
if (++total > cap) {
|
||||||
|
ctx.console.error('shell: for: exceeded BARE_OS_SHELL_LOOP_MAX')
|
||||||
|
ctx.exitCode = 1
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
const r = await execSemicolonLists(ctx, bodyToks)
|
||||||
|
if (r === 'exit') return 'exit'
|
||||||
|
}
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Record<string, unknown>} ctx
|
||||||
|
* @param {Token[]} stmt
|
||||||
|
* @returns {Promise<'exit' | 'ok'>}
|
||||||
|
*/
|
||||||
|
async function dispatchShellStatement(ctx, stmt) {
|
||||||
|
const head = stmt[0]
|
||||||
|
if (head?.type === 'word' && head.value === 'if')
|
||||||
|
return execIfConstruct(ctx, stmt)
|
||||||
|
if (head?.type === 'word' && head.value === 'while')
|
||||||
|
return execWhileConstruct(ctx, stmt)
|
||||||
|
if (head?.type === 'word' && head.value === 'for')
|
||||||
|
return execForConstruct(ctx, stmt)
|
||||||
|
return execAndOrList(ctx, stmt)
|
||||||
|
}
|
||||||
|
|
||||||
async function execIfConstruct(ctx, tokens) {
|
async function execIfConstruct(ctx, tokens) {
|
||||||
const thenIdx = findThenIndex(tokens, 1)
|
const thenIdx = findThenIndex(tokens, 1)
|
||||||
if (thenIdx < 0) {
|
if (thenIdx < 0) {
|
||||||
@@ -1483,10 +1621,7 @@ export async function execShellLine(ctx, line) {
|
|||||||
scheduleBackgroundShell(ctx, part)
|
scheduleBackgroundShell(ctx, part)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const r =
|
const r = await dispatchShellStatement(ctx, part)
|
||||||
part[0]?.type === 'word' && part[0].value === 'if'
|
|
||||||
? await execIfConstruct(ctx, part)
|
|
||||||
: await execAndOrList(ctx, part)
|
|
||||||
if (r === 'exit') {
|
if (r === 'exit') {
|
||||||
syncBareOsExitStatusEnv(ctx)
|
syncBareOsExitStatusEnv(ctx)
|
||||||
return 'exit'
|
return 'exit'
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ function printHelp(ctx, prog) {
|
|||||||
|
|
||||||
Persistent preset: ~/.config/bare-os/initd/disabled.txt (personal drive).
|
Persistent preset: ~/.config/bare-os/initd/disabled.txt (personal drive).
|
||||||
Optional ~/.config/bare-os/units/<name>.unit with [Unit] After=other-unit.
|
Optional ~/.config/bare-os/units/<name>.unit with [Unit] After=other-unit.
|
||||||
|
User overrides: ~/.config/bare-init/units/<name>.unit (merged; systemctl --user style).
|
||||||
journalctl: journalctl -u UNIT [--lines N] (alias for logs)`
|
journalctl: journalctl -u UNIT [--lines N] (alias for logs)`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,9 @@ const DIR_MARKER = '.bareos_empty'
|
|||||||
* procBareOsQuotasText?: () => string,
|
* procBareOsQuotasText?: () => string,
|
||||||
* procBareOsResourcesText?: () => string,
|
* procBareOsResourcesText?: () => string,
|
||||||
* procBareOsFeaturesText?: () => string,
|
* procBareOsFeaturesText?: () => string,
|
||||||
* getVirtualReaders?: () => Map<string, () => string | Uint8Array | Promise<string | Uint8Array>>,
|
* procBareOsSwarmText?: () => string,
|
||||||
|
* getVirtualReaders?: () => Map<string, unknown>,
|
||||||
|
* unionReadPrefixes?: readonly string[],
|
||||||
* sysClassNetLoText?: () => string
|
* sysClassNetLoText?: () => string
|
||||||
* }} [vfsOptions]
|
* }} [vfsOptions]
|
||||||
*/
|
*/
|
||||||
@@ -112,10 +114,19 @@ export function createVfs(
|
|||||||
typeof vfsOptions.procBareOsFeaturesText === 'function'
|
typeof vfsOptions.procBareOsFeaturesText === 'function'
|
||||||
? vfsOptions.procBareOsFeaturesText
|
? vfsOptions.procBareOsFeaturesText
|
||||||
: null
|
: null
|
||||||
|
const procBareOsSwarmText =
|
||||||
|
typeof vfsOptions.procBareOsSwarmText === 'function'
|
||||||
|
? vfsOptions.procBareOsSwarmText
|
||||||
|
: null
|
||||||
const getVirtualReaders =
|
const getVirtualReaders =
|
||||||
typeof vfsOptions.getVirtualReaders === 'function'
|
typeof vfsOptions.getVirtualReaders === 'function'
|
||||||
? vfsOptions.getVirtualReaders
|
? vfsOptions.getVirtualReaders
|
||||||
: null
|
: null
|
||||||
|
const unionReadPrefixes = Array.isArray(vfsOptions.unionReadPrefixes)
|
||||||
|
? vfsOptions.unionReadPrefixes.filter(
|
||||||
|
(s) => typeof s === 'string' && s.startsWith('/')
|
||||||
|
)
|
||||||
|
: []
|
||||||
const sysClassNetLoText =
|
const sysClassNetLoText =
|
||||||
typeof vfsOptions.sysClassNetLoText === 'function'
|
typeof vfsOptions.sysClassNetLoText === 'function'
|
||||||
? vfsOptions.sysClassNetLoText
|
? vfsOptions.sysClassNetLoText
|
||||||
@@ -306,6 +317,10 @@ export function createVfs(
|
|||||||
const t = procBareOsFeaturesText ? procBareOsFeaturesText() : '{}\n'
|
const t = procBareOsFeaturesText ? procBareOsFeaturesText() : '{}\n'
|
||||||
return utf8Encode(t)
|
return utf8Encode(t)
|
||||||
}
|
}
|
||||||
|
if (f === 'bare_os_swarm') {
|
||||||
|
const t = procBareOsSwarmText ? procBareOsSwarmText() : '{}\n'
|
||||||
|
return utf8Encode(t)
|
||||||
|
}
|
||||||
if (f === 'net_dev') {
|
if (f === 'net_dev') {
|
||||||
const t = procNetDevText
|
const t = procNetDevText
|
||||||
? procNetDevText()
|
? procNetDevText()
|
||||||
@@ -466,6 +481,14 @@ export function createVfs(
|
|||||||
file: 'bare_os_features'
|
file: 'bare_os_features'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (sub === 'bare_os_swarm') {
|
||||||
|
return {
|
||||||
|
virtualPseudo: true,
|
||||||
|
kind: 'proc',
|
||||||
|
node: 'file',
|
||||||
|
file: 'bare_os_swarm'
|
||||||
|
}
|
||||||
|
}
|
||||||
if (sub === 'net' || sub === 'net/') {
|
if (sub === 'net' || sub === 'net/') {
|
||||||
return { virtualPseudo: true, kind: 'proc', node: 'dir', dir: 'net' }
|
return { virtualPseudo: true, kind: 'proc', node: 'dir', dir: 'net' }
|
||||||
}
|
}
|
||||||
@@ -798,6 +821,17 @@ export function createVfs(
|
|||||||
const p = unixPathResolve(homeRoot, rel)
|
const p = unixPathResolve(homeRoot, rel)
|
||||||
return { drive: personalDrive, path: p }
|
return { drive: personalDrive, path: p }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const unionNorm = absPath.replace(/\/+$/, '') || '/'
|
||||||
|
if (
|
||||||
|
unionNorm === '/.bare-os/union' ||
|
||||||
|
absPath.startsWith('/.bare-os/union/')
|
||||||
|
) {
|
||||||
|
const p =
|
||||||
|
unionNorm === '/.bare-os/union' ? '/.bare-os/union' : unionNorm
|
||||||
|
return { drive: personalDrive, path: p }
|
||||||
|
}
|
||||||
|
|
||||||
return { drive: systemDrive, path: absPath }
|
return { drive: systemDrive, path: absPath }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1167,6 +1201,7 @@ export function createVfs(
|
|||||||
'bare_os_quotas',
|
'bare_os_quotas',
|
||||||
'bare_os_resources',
|
'bare_os_resources',
|
||||||
'bare_os_session_stats',
|
'bare_os_session_stats',
|
||||||
|
'bare_os_swarm',
|
||||||
'bare_os_version',
|
'bare_os_version',
|
||||||
'cpuinfo',
|
'cpuinfo',
|
||||||
'diskstats',
|
'diskstats',
|
||||||
@@ -1455,10 +1490,20 @@ export function createVfs(
|
|||||||
) {
|
) {
|
||||||
await assertTraverseTo(abs, 'read')
|
await assertTraverseTo(abs, 'read')
|
||||||
const m = getVirtualReaders()
|
const m = getVirtualReaders()
|
||||||
const fn =
|
const ent =
|
||||||
r.virtualName && m && typeof m.get === 'function'
|
r.virtualName && m && typeof m.get === 'function'
|
||||||
? m.get(r.virtualName)
|
? m.get(r.virtualName)
|
||||||
: null
|
: null
|
||||||
|
const fn =
|
||||||
|
typeof ent === 'function'
|
||||||
|
? ent
|
||||||
|
: ent &&
|
||||||
|
typeof ent === 'object' &&
|
||||||
|
ent !== null &&
|
||||||
|
typeof /** @type {{ read?: unknown }} */ (ent).read ===
|
||||||
|
'function'
|
||||||
|
? /** @type {{ read: () => unknown }} */ (ent).read
|
||||||
|
: null
|
||||||
if (typeof fn !== 'function') return null
|
if (typeof fn !== 'function') return null
|
||||||
const out = await Promise.resolve(fn())
|
const out = await Promise.resolve(fn())
|
||||||
if (typeof out === 'string') return utf8Encode(out)
|
if (typeof out === 'string') return utf8Encode(out)
|
||||||
@@ -1479,6 +1524,28 @@ export function createVfs(
|
|||||||
const { drive, path: p } = r
|
const { drive, path: p } = r
|
||||||
if (isHyperdriveRootPath(p)) return null
|
if (isHyperdriveRootPath(p)) return null
|
||||||
await assertTraverseTo(abs, 'read')
|
await assertTraverseTo(abs, 'read')
|
||||||
|
if (unionReadPrefixes.length && drive === systemDrive) {
|
||||||
|
for (const pre of unionReadPrefixes) {
|
||||||
|
if (abs === pre || abs.startsWith(pre + '/')) {
|
||||||
|
const ol =
|
||||||
|
'/.bare-os/union' + (abs === '/' ? '' : abs)
|
||||||
|
const ur = route(ol)
|
||||||
|
if (
|
||||||
|
!ur.virtualPseudo &&
|
||||||
|
ur.drive === personalDrive &&
|
||||||
|
!isHyperdriveRootPath(ur.path)
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const ubuf = await ur.drive.get(ur.path, { follow: true })
|
||||||
|
if (ubuf) return ubuf
|
||||||
|
} catch {
|
||||||
|
/* fall through to system */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return drive.get(p, { follow: true })
|
return drive.get(p, { follow: true })
|
||||||
})(),
|
})(),
|
||||||
abortOpts,
|
abortOpts,
|
||||||
|
|||||||
@@ -844,6 +844,7 @@ test('vfs /proc /sys read-only pseudo files; write rejected', async (t) => {
|
|||||||
bootProfileText: () => 'mini\n',
|
bootProfileText: () => 'mini\n',
|
||||||
sessionText: () => 'test-session-id\n',
|
sessionText: () => 'test-session-id\n',
|
||||||
initdRunText: () => 'demo-unit\tactive\t1\tdemo\n',
|
initdRunText: () => 'demo-unit\tactive\t1\tdemo\n',
|
||||||
|
procBareOsSwarmText: () => '{}\n',
|
||||||
bareOsIpc
|
bareOsIpc
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -858,6 +859,7 @@ test('vfs /proc /sys read-only pseudo files; write rejected', async (t) => {
|
|||||||
'bare_os_quotas',
|
'bare_os_quotas',
|
||||||
'bare_os_resources',
|
'bare_os_resources',
|
||||||
'bare_os_session_stats',
|
'bare_os_session_stats',
|
||||||
|
'bare_os_swarm',
|
||||||
'bare_os_version',
|
'bare_os_version',
|
||||||
'cpuinfo',
|
'cpuinfo',
|
||||||
'diskstats',
|
'diskstats',
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
* next, exit, break, continue, ++/--, arrays, strings, regex ~ !~, builtins (length, substr, index,
|
* next, exit, break, continue, ++/--, arrays, strings, regex ~ !~, builtins (length, substr, index,
|
||||||
* split, sprintf, sub, gsub, match, int, tolower, toupper, rand, srand), getline from stdin/files,
|
* split, sprintf, sub, gsub, match, int, tolower, toupper, rand, srand), getline from stdin/files,
|
||||||
* -F FS, NF, NR, FNR, $0..$n, OFS, ORS, RS, ARGC, ARGV, FILENAME, ENVIRON (read-only mirror).
|
* -F FS, NF, NR, FNR, $0..$n, OFS, ORS, RS, ARGC, ARGV, FILENAME, ENVIRON (read-only mirror).
|
||||||
|
* GNU-style empty fields: consecutive FS delimiters still advance $1..$NF where the grammar allows.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function bareAwkError(msg) {
|
function bareAwkError(msg) {
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ export const COREUTILS_COMMANDS = [
|
|||||||
'nproc',
|
'nproc',
|
||||||
'numfmt',
|
'numfmt',
|
||||||
'od',
|
'od',
|
||||||
|
'oidc-publish',
|
||||||
'paste',
|
'paste',
|
||||||
'pathchk',
|
'pathchk',
|
||||||
'pr',
|
'pr',
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "oidc-publish",
|
||||||
|
"section": 1,
|
||||||
|
"title": "OIDC client-credentials helper",
|
||||||
|
"synopsis": ["oidc-publish help", "oidc-publish"],
|
||||||
|
"description": "POSTs client_credentials to ${OIDC_ISSUER}/oauth/token when ctx.httpFetch exists and HTTP policy allows. See packages/bare-os-coreutils/src/oidc-publish.js.",
|
||||||
|
"options": [],
|
||||||
|
"keywords": ["oidc", "pear", "http", "bare-os"],
|
||||||
|
"examples": [
|
||||||
|
{ "caption": "help", "code": "oidc-publish help" }
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -9,6 +9,9 @@ async function run(ctx, argv) {
|
|||||||
git remote add origin 'git+pear://<key-or-link>'
|
git remote add origin 'git+pear://<key-or-link>'
|
||||||
git push origin main
|
git push origin main
|
||||||
|
|
||||||
|
Host env (aligns with gip-transport / git-remote-git+pear):
|
||||||
|
GIT_PEAR_DEBUG, HYPERSWARM_DHT, PEAR_CHANNEL — forwarded by Pear tooling when set.
|
||||||
|
|
||||||
Pear app channel metadata for images: set BARE_OS_PEAR_CHANNEL / BARE_OS_PEAR_RELEASE on the host.
|
Pear app channel metadata for images: set BARE_OS_PEAR_CHANNEL / BARE_OS_PEAR_RELEASE on the host.
|
||||||
`)
|
`)
|
||||||
ctx.exitCode = 0
|
ctx.exitCode = 0
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* Thin helper for OIDC-style token exchange workflows (Holepunch oidc-publishing patterns).
|
||||||
|
* Uses ctx.httpFetch when present and host HTTP policy allows the issuer URL.
|
||||||
|
*/
|
||||||
|
async function run(ctx, argv) {
|
||||||
|
if (argv[1] === 'help' || argv[1] === '-h' || argv[1] === '--help') {
|
||||||
|
ctx.console.log(`oidc-publish — optional OIDC token helper
|
||||||
|
|
||||||
|
Reads OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET (or argv) and POSTs
|
||||||
|
to \${issuer}/oauth/token with grant_type=client_credentials when ctx.httpFetch exists.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
export OIDC_ISSUER=https://issuer.example
|
||||||
|
oidc-publish
|
||||||
|
|
||||||
|
Requires delegated httpFetch and allowlisted host (BARE_OS_HTTP_ALLOWLIST).
|
||||||
|
`)
|
||||||
|
ctx.exitCode = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const env = ctx.vfs?.env || {}
|
||||||
|
const issuer = String(env.OIDC_ISSUER || argv[2] || '').replace(/\/+$/, '')
|
||||||
|
const cid = String(env.OIDC_CLIENT_ID || argv[3] || '')
|
||||||
|
const csec = String(env.OIDC_CLIENT_SECRET || argv[4] || '')
|
||||||
|
if (!issuer || !cid || !csec) {
|
||||||
|
ctx.console.error(
|
||||||
|
'oidc-publish: set OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET'
|
||||||
|
)
|
||||||
|
ctx.exitCode = 2
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (typeof ctx.httpFetch !== 'function') {
|
||||||
|
ctx.console.error('oidc-publish: ctx.httpFetch not available')
|
||||||
|
ctx.exitCode = 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const url = `${issuer}/oauth/token`
|
||||||
|
const body =
|
||||||
|
'grant_type=client_credentials&client_id=' +
|
||||||
|
encodeURIComponent(cid) +
|
||||||
|
'&client_secret=' +
|
||||||
|
encodeURIComponent(csec)
|
||||||
|
try {
|
||||||
|
const res = await ctx.httpFetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'content-type': 'application/x-www-form-urlencoded' },
|
||||||
|
body
|
||||||
|
})
|
||||||
|
const text = await res.text()
|
||||||
|
if (!res.ok) {
|
||||||
|
ctx.console.error('oidc-publish: HTTP ' + res.status + ' ' + text.slice(0, 200))
|
||||||
|
ctx.exitCode = 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.console.log(text.slice(0, 4000))
|
||||||
|
ctx.exitCode = 0
|
||||||
|
} catch (e) {
|
||||||
|
ctx.console.error('oidc-publish: ' + ((e && e.message) || String(e)))
|
||||||
|
ctx.exitCode = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,5 +16,11 @@ export {
|
|||||||
BARE_OS_FEATURE_HTTP_POLICY,
|
BARE_OS_FEATURE_HTTP_POLICY,
|
||||||
BARE_OS_FEATURE_VIRTUAL_FILES,
|
BARE_OS_FEATURE_VIRTUAL_FILES,
|
||||||
BARE_OS_FEATURE_HOST_STATS,
|
BARE_OS_FEATURE_HOST_STATS,
|
||||||
|
BARE_OS_FEATURE_CRYPTO_URANDOM,
|
||||||
|
BARE_OS_FEATURE_SANDBOX_SCRIPT,
|
||||||
|
BARE_OS_FEATURE_BOOT_PHASE_HOOKS,
|
||||||
|
BARE_OS_FEATURE_IPC_DUPLEX,
|
||||||
|
BARE_OS_FEATURE_VFS_UNION,
|
||||||
|
BARE_OS_FEATURE_PROC_SWARM,
|
||||||
BARE_OS_KERNEL_FEATURES_STOCK_V1
|
BARE_OS_KERNEL_FEATURES_STOCK_V1
|
||||||
} from './lib/kernel-feature-bits.js'
|
} from './lib/kernel-feature-bits.js'
|
||||||
|
|||||||
@@ -21,10 +21,34 @@ export const BARE_OS_FEATURE_VIRTUAL_FILES = 1 << 3
|
|||||||
/** Bit 4: Host introspection bridge (`ctx.bareOsHostStats`) when available. */
|
/** Bit 4: Host introspection bridge (`ctx.bareOsHostStats`) when available. */
|
||||||
export const BARE_OS_FEATURE_HOST_STATS = 1 << 4
|
export const BARE_OS_FEATURE_HOST_STATS = 1 << 4
|
||||||
|
|
||||||
/** Default bitmask implied by current stock booter (1.6.x). */
|
/** Bit 5: `/dev/urandom` backed by `bare-crypto` (disable with `BARE_OS_URANDOM_CRYPTO=0`). */
|
||||||
|
export const BARE_OS_FEATURE_CRYPTO_URANDOM = 1 << 5
|
||||||
|
|
||||||
|
/** Bit 6: `ctx.bareOsSandboxRunScript` (restricted `ctx`). */
|
||||||
|
export const BARE_OS_FEATURE_SANDBOX_SCRIPT = 1 << 6
|
||||||
|
|
||||||
|
/** Bit 7: Boot phase hooks (`ctx.bareOsRegisterBootPhaseHook`). */
|
||||||
|
export const BARE_OS_FEATURE_BOOT_PHASE_HOOKS = 1 << 7
|
||||||
|
|
||||||
|
/** Bit 8: Duplex IPC bridge (`bareOsIpc.createDuplexBridge`). */
|
||||||
|
export const BARE_OS_FEATURE_IPC_DUPLEX = 1 << 8
|
||||||
|
|
||||||
|
/** Bit 9: VFS union read overlay (`BARE_OS_VFS_UNION_PREFIXES`). */
|
||||||
|
export const BARE_OS_FEATURE_VFS_UNION = 1 << 9
|
||||||
|
|
||||||
|
/** Bit 10: Swarm snapshot proc (`/proc/bare_os_swarm`). */
|
||||||
|
export const BARE_OS_FEATURE_PROC_SWARM = 1 << 10
|
||||||
|
|
||||||
|
/** Default bitmask implied by current stock booter. */
|
||||||
export const BARE_OS_KERNEL_FEATURES_STOCK_V1 =
|
export const BARE_OS_KERNEL_FEATURES_STOCK_V1 =
|
||||||
BARE_OS_FEATURE_IPC_FANOUT |
|
BARE_OS_FEATURE_IPC_FANOUT |
|
||||||
BARE_OS_FEATURE_ABORT_TIMEOUT |
|
BARE_OS_FEATURE_ABORT_TIMEOUT |
|
||||||
BARE_OS_FEATURE_HTTP_POLICY |
|
BARE_OS_FEATURE_HTTP_POLICY |
|
||||||
BARE_OS_FEATURE_VIRTUAL_FILES |
|
BARE_OS_FEATURE_VIRTUAL_FILES |
|
||||||
BARE_OS_FEATURE_HOST_STATS
|
BARE_OS_FEATURE_HOST_STATS |
|
||||||
|
BARE_OS_FEATURE_CRYPTO_URANDOM |
|
||||||
|
BARE_OS_FEATURE_SANDBOX_SCRIPT |
|
||||||
|
BARE_OS_FEATURE_BOOT_PHASE_HOOKS |
|
||||||
|
BARE_OS_FEATURE_IPC_DUPLEX |
|
||||||
|
BARE_OS_FEATURE_VFS_UNION |
|
||||||
|
BARE_OS_FEATURE_PROC_SWARM
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
defaultKernelRoot,
|
defaultKernelRoot,
|
||||||
defaultSeedCorestorePath
|
defaultSeedCorestorePath
|
||||||
} from './lib/paths.js'
|
} from './lib/paths.js'
|
||||||
|
import { logPearMultisigKernelHint } from './lib/pear-multisig-hint.js'
|
||||||
|
|
||||||
const _pkg = packageRootDir(import.meta.url)
|
const _pkg = packageRootDir(import.meta.url)
|
||||||
|
|
||||||
@@ -110,6 +111,7 @@ async function main() {
|
|||||||
console.log('Drive ready:', drive.id)
|
console.log('Drive ready:', drive.id)
|
||||||
|
|
||||||
await stageKernelTree(drive, kernelRoot)
|
await stageKernelTree(drive, kernelRoot)
|
||||||
|
await logPearMultisigKernelHint(kernelRoot)
|
||||||
|
|
||||||
const localRAM = new Map()
|
const localRAM = new Map()
|
||||||
const mbr = buildMbr(drive.key)
|
const mbr = buildMbr(drive.key)
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ function bareOsEmitRaw(ctx, chunk) {
|
|||||||
* next, exit, break, continue, ++/--, arrays, strings, regex ~ !~, builtins (length, substr, index,
|
* next, exit, break, continue, ++/--, arrays, strings, regex ~ !~, builtins (length, substr, index,
|
||||||
* split, sprintf, sub, gsub, match, int, tolower, toupper, rand, srand), getline from stdin/files,
|
* split, sprintf, sub, gsub, match, int, tolower, toupper, rand, srand), getline from stdin/files,
|
||||||
* -F FS, NF, NR, FNR, $0..$n, OFS, ORS, RS, ARGC, ARGV, FILENAME, ENVIRON (read-only mirror).
|
* -F FS, NF, NR, FNR, $0..$n, OFS, ORS, RS, ARGC, ARGV, FILENAME, ENVIRON (read-only mirror).
|
||||||
|
* GNU-style empty fields: consecutive FS delimiters still advance $1..$NF where the grammar allows.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function bareAwkError(msg) {
|
function bareAwkError(msg) {
|
||||||
|
|||||||
@@ -98,6 +98,9 @@ async function run(ctx, argv) {
|
|||||||
git remote add origin 'git+pear://<key-or-link>'
|
git remote add origin 'git+pear://<key-or-link>'
|
||||||
git push origin main
|
git push origin main
|
||||||
|
|
||||||
|
Host env (aligns with gip-transport / git-remote-git+pear):
|
||||||
|
GIT_PEAR_DEBUG, HYPERSWARM_DHT, PEAR_CHANNEL — forwarded by Pear tooling when set.
|
||||||
|
|
||||||
Pear app channel metadata for images: set BARE_OS_PEAR_CHANNEL / BARE_OS_PEAR_RELEASE on the host.
|
Pear app channel metadata for images: set BARE_OS_PEAR_CHANNEL / BARE_OS_PEAR_RELEASE on the host.
|
||||||
`)
|
`)
|
||||||
ctx.exitCode = 0
|
ctx.exitCode = 0
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ function bareOsEmitRaw(ctx, chunk) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var BARE_OS_HELP_BIN_SPACED = "arch awk base32 base64 basename basenc cat chgrp chmod chown cksum clear comm cp crontab curl cut date df dir dircolors dirname du echo edit env exit expand expr factor false find fmt fold getconf git git-pear grep groups hdms head help hostid hostname id install join journalctl jq ln login logname logout ls man md5sum mkdir mkfifo mktemp mv nano nl nproc numfmt od paste pathchk pr printenv printf pwd readlink realpath rev rm rmdir savevault sed seq sha1sum sha256sum sha512sum shuf sleep sort split stat sum sync systemctl tac tail tee test theme time touch tr true truncate tsort tty uname unexpand uniq unlink uptime users vdir wc wget which who whoami xargs yes"
|
var BARE_OS_HELP_BIN_SPACED = "arch awk base32 base64 basename basenc cat chgrp chmod chown cksum clear comm cp crontab curl cut date df dir dircolors dirname du echo edit env exit expand expr factor false find fmt fold getconf git git-pear grep groups hdms head help hostid hostname id install join journalctl jq ln login logname logout ls man md5sum mkdir mkfifo mktemp mv nano nl nproc numfmt od oidc-publish paste pathchk pr printenv printf pwd readlink realpath rev rm rmdir savevault sed seq sha1sum sha256sum sha512sum shuf sleep sort split stat sum sync systemctl tac tail tee test theme time touch tr true truncate tsort tty uname unexpand uniq unlink uptime users vdir wc wget which who whoami xargs yes"
|
||||||
async function run(ctx, argv) {
|
async function run(ctx, argv) {
|
||||||
ctx.console.log(
|
ctx.console.log(
|
||||||
'Bare OS — default user: guest | shell builtins: alias, barerc, cd, command, export, exit, login, logout, readonly, type, umask, unalias, unset, : | /bin: ' +
|
'Bare OS — default user: guest | shell builtins: alias, barerc, cd, command, export, exit, login, logout, readonly, type, umask, unalias, unset, : | /bin: ' +
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
|
||||||
|
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
|
||||||
|
function bareStdin(ctx) {
|
||||||
|
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
|
||||||
|
function bareFormatModeString(mode, type) {
|
||||||
|
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
|
||||||
|
const perm = mode & 0o777
|
||||||
|
const r = (bit) => (perm & bit ? 'r' : '-')
|
||||||
|
const w = (bit) => (perm & bit ? 'w' : '-')
|
||||||
|
const x = (bit) => (perm & bit ? 'x' : '-')
|
||||||
|
return (
|
||||||
|
typeChar +
|
||||||
|
r(0o400) +
|
||||||
|
w(0o200) +
|
||||||
|
x(0o100) +
|
||||||
|
r(0o040) +
|
||||||
|
w(0o020) +
|
||||||
|
x(0o010) +
|
||||||
|
r(0o004) +
|
||||||
|
w(0o002) +
|
||||||
|
x(0o001)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {number} mtimeMs @param {number} [nowMs] */
|
||||||
|
function bareFormatLsMtime(mtimeMs, nowMs) {
|
||||||
|
const now = nowMs != null ? nowMs : Date.now()
|
||||||
|
const d = new Date(mtimeMs)
|
||||||
|
const months = [
|
||||||
|
'Jan',
|
||||||
|
'Feb',
|
||||||
|
'Mar',
|
||||||
|
'Apr',
|
||||||
|
'May',
|
||||||
|
'Jun',
|
||||||
|
'Jul',
|
||||||
|
'Aug',
|
||||||
|
'Sep',
|
||||||
|
'Oct',
|
||||||
|
'Nov',
|
||||||
|
'Dec'
|
||||||
|
]
|
||||||
|
const mon = months[d.getMonth()]
|
||||||
|
const day = String(d.getDate()).padStart(2, ' ')
|
||||||
|
const sixMo = 180 * 24 * 3600 * 1000
|
||||||
|
if (Math.abs(now - mtimeMs) > sixMo) {
|
||||||
|
const yr = String(d.getFullYear()).padStart(4, ' ')
|
||||||
|
return mon + ' ' + day + ' ' + yr
|
||||||
|
}
|
||||||
|
const hh = String(d.getHours()).padStart(2, '0')
|
||||||
|
const mm = String(d.getMinutes()).padStart(2, '0')
|
||||||
|
return mon + ' ' + day + ' ' + hh + ':' + mm
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {number} size */
|
||||||
|
function barePosixBlocks(size) {
|
||||||
|
return Math.ceil(Number(size) / 512) || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
|
||||||
|
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
|
||||||
|
* @param {Record<string, unknown>} ctx
|
||||||
|
* @param {string | Uint8Array} chunk
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function bareOsEmitRaw(ctx, chunk) {
|
||||||
|
if (typeof ctx.bareOsBinWrite === 'function') {
|
||||||
|
const b4 = ctx.b4a
|
||||||
|
const u8 =
|
||||||
|
typeof chunk === 'string'
|
||||||
|
? b4 && typeof b4.from === 'function'
|
||||||
|
? b4.from(chunk)
|
||||||
|
: new TextEncoder().encode(chunk)
|
||||||
|
: chunk
|
||||||
|
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const w = globalThis.process?.stdout?.write
|
||||||
|
if (typeof w === 'function') {
|
||||||
|
w.call(globalThis.process.stdout, chunk)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thin helper for OIDC-style token exchange workflows (Holepunch oidc-publishing patterns).
|
||||||
|
* Uses ctx.httpFetch when present and host HTTP policy allows the issuer URL.
|
||||||
|
*/
|
||||||
|
async function run(ctx, argv) {
|
||||||
|
if (argv[1] === 'help' || argv[1] === '-h' || argv[1] === '--help') {
|
||||||
|
ctx.console.log(`oidc-publish — optional OIDC token helper
|
||||||
|
|
||||||
|
Reads OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET (or argv) and POSTs
|
||||||
|
to \${issuer}/oauth/token with grant_type=client_credentials when ctx.httpFetch exists.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
export OIDC_ISSUER=https://issuer.example
|
||||||
|
oidc-publish
|
||||||
|
|
||||||
|
Requires delegated httpFetch and allowlisted host (BARE_OS_HTTP_ALLOWLIST).
|
||||||
|
`)
|
||||||
|
ctx.exitCode = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const env = ctx.vfs?.env || {}
|
||||||
|
const issuer = String(env.OIDC_ISSUER || argv[2] || '').replace(/\/+$/, '')
|
||||||
|
const cid = String(env.OIDC_CLIENT_ID || argv[3] || '')
|
||||||
|
const csec = String(env.OIDC_CLIENT_SECRET || argv[4] || '')
|
||||||
|
if (!issuer || !cid || !csec) {
|
||||||
|
ctx.console.error(
|
||||||
|
'oidc-publish: set OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET'
|
||||||
|
)
|
||||||
|
ctx.exitCode = 2
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (typeof ctx.httpFetch !== 'function') {
|
||||||
|
ctx.console.error('oidc-publish: ctx.httpFetch not available')
|
||||||
|
ctx.exitCode = 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const url = `${issuer}/oauth/token`
|
||||||
|
const body =
|
||||||
|
'grant_type=client_credentials&client_id=' +
|
||||||
|
encodeURIComponent(cid) +
|
||||||
|
'&client_secret=' +
|
||||||
|
encodeURIComponent(csec)
|
||||||
|
try {
|
||||||
|
const res = await ctx.httpFetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'content-type': 'application/x-www-form-urlencoded' },
|
||||||
|
body
|
||||||
|
})
|
||||||
|
const text = await res.text()
|
||||||
|
if (!res.ok) {
|
||||||
|
ctx.console.error('oidc-publish: HTTP ' + res.status + ' ' + text.slice(0, 200))
|
||||||
|
ctx.exitCode = 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.console.log(text.slice(0, 4000))
|
||||||
|
ctx.exitCode = 0
|
||||||
|
} catch (e) {
|
||||||
|
ctx.console.error('oidc-publish: ' + ((e && e.message) || String(e)))
|
||||||
|
ctx.exitCode = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"sha256": {
|
||||||
|
"/etc/bare-os/rc": "replace-with-sha256-hex-of-file-contents"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -196,6 +196,13 @@ function bootLineAllowed(line, allow) {
|
|||||||
* @param {string[]} phaseLog
|
* @param {string[]} phaseLog
|
||||||
*/
|
*/
|
||||||
async function bootTimed(ctx, label, fn, phaseLog) {
|
async function bootTimed(ctx, label, fn, phaseLog) {
|
||||||
|
if (typeof ctx.bareOsInvokeBootPhaseHooks === 'function') {
|
||||||
|
await ctx.bareOsInvokeBootPhaseHooks({
|
||||||
|
phase: label,
|
||||||
|
when: 'before',
|
||||||
|
label
|
||||||
|
})
|
||||||
|
}
|
||||||
const t0 = Date.now()
|
const t0 = Date.now()
|
||||||
await fn()
|
await fn()
|
||||||
const ms = Date.now() - t0
|
const ms = Date.now() - t0
|
||||||
@@ -226,6 +233,67 @@ async function bootTimed(ctx, label, fn, phaseLog) {
|
|||||||
sessionId: (ctx.env && ctx.env.BARE_OS_SESSION_ID) || ''
|
sessionId: (ctx.env && ctx.env.BARE_OS_SESSION_ID) || ''
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (typeof ctx.bareOsInvokeBootPhaseHooks === 'function') {
|
||||||
|
await ctx.bareOsInvokeBootPhaseHooks({
|
||||||
|
phase: label,
|
||||||
|
when: 'after',
|
||||||
|
label
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Record<string, unknown> | null | undefined} */
|
||||||
|
let bootManifestMemo
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Record<string, unknown>} ctx
|
||||||
|
* @returns {Promise<Record<string, unknown> | null>}
|
||||||
|
*/
|
||||||
|
async function loadBootManifest(ctx) {
|
||||||
|
const v = ctx.env && ctx.env.BARE_OS_BOOT_MANIFEST
|
||||||
|
if (v !== '1' && v !== 'true') return null
|
||||||
|
if (bootManifestMemo !== undefined) return bootManifestMemo
|
||||||
|
const { drive, b4a, console } = ctx
|
||||||
|
try {
|
||||||
|
const buf = await drive.get('/etc/bare-os/boot.manifest.json')
|
||||||
|
if (!buf) {
|
||||||
|
bootManifestMemo = null
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
bootManifestMemo = JSON.parse(b4a.toString(buf))
|
||||||
|
return bootManifestMemo
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
'[boot] boot.manifest.json: ' + ((e && e.message) || String(e))
|
||||||
|
)
|
||||||
|
bootManifestMemo = null
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Record<string, unknown>} ctx
|
||||||
|
* @param {string} drivePath
|
||||||
|
* @param {string | Uint8Array} content
|
||||||
|
*/
|
||||||
|
async function bootManifestDigestOk(ctx, drivePath, content) {
|
||||||
|
const m = await loadBootManifest(ctx)
|
||||||
|
const sha = m && typeof m === 'object' ? m.sha256 : null
|
||||||
|
if (!sha || typeof sha !== 'object') return true
|
||||||
|
const exp = /** @type {Record<string, string>} */ (sha)[drivePath]
|
||||||
|
if (exp == null || exp === '') return true
|
||||||
|
if (typeof ctx.bareOsBootFileSha256Hex !== 'function') {
|
||||||
|
ctx.console.error('[boot] manifest present but bareOsBootFileSha256Hex missing')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const buf =
|
||||||
|
typeof content === 'string' ? ctx.b4a.from(content, 'utf8') : content
|
||||||
|
const hex = ctx.bareOsBootFileSha256Hex(buf)
|
||||||
|
if (hex !== String(exp).trim().toLowerCase()) {
|
||||||
|
ctx.console.error('[boot] manifest sha256 mismatch: ' + drivePath)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -400,7 +468,15 @@ async function runRcFileAt(ctx, drivePath, label) {
|
|||||||
try {
|
try {
|
||||||
const buf = await drive.get(drivePath)
|
const buf = await drive.get(drivePath)
|
||||||
if (!buf) return true
|
if (!buf) return true
|
||||||
return await runRcLines(ctx, b4a.toString(buf))
|
const text = b4a.toString(buf)
|
||||||
|
if (!(await bootManifestDigestOk(ctx, drivePath, text))) {
|
||||||
|
if (bootStrict(ctx)) {
|
||||||
|
if (typeof ctx.requestBooterExit === 'function') ctx.requestBooterExit(1)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return await runRcLines(ctx, text)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`${label}: ` + ((e && e.message) || String(e)))
|
console.error(`${label}: ` + ((e && e.message) || String(e)))
|
||||||
return true
|
return true
|
||||||
@@ -578,6 +654,35 @@ async function runKernelSelftest(ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
/** @type {string[]} */
|
||||||
|
const snames = []
|
||||||
|
for await (const n of ctx.drive.readdir('/etc/bare-os/selftest.d'))
|
||||||
|
snames.push(n)
|
||||||
|
snames.sort()
|
||||||
|
for (const name of snames) {
|
||||||
|
if (!isBareOsRcSnippetFile(name)) continue
|
||||||
|
const p = `/etc/bare-os/selftest.d/${name}`
|
||||||
|
const buf = await ctx.drive.get(p)
|
||||||
|
if (!buf) continue
|
||||||
|
const label = `selftest.d/${name}`
|
||||||
|
try {
|
||||||
|
await runRcLines(ctx, ctx.b4a.toString(buf))
|
||||||
|
tapLine(true, label, '')
|
||||||
|
} catch (e) {
|
||||||
|
const msg = (e && e.message) || String(e)
|
||||||
|
console.error('selftest: ' + msg)
|
||||||
|
tapLine(false, label, msg)
|
||||||
|
if (strict) {
|
||||||
|
if (typeof ctx.requestBooterExit === 'function')
|
||||||
|
ctx.requestBooterExit(1)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
/* no selftest.d */
|
||||||
|
}
|
||||||
if (tap) {
|
if (tap) {
|
||||||
console.error(`1..${tapN}`)
|
console.error(`1..${tapN}`)
|
||||||
}
|
}
|
||||||
@@ -596,7 +701,11 @@ function publishBootReady(ctx, phaseLog) {
|
|||||||
phases: [...phaseLog],
|
phases: [...phaseLog],
|
||||||
sessionId: sid,
|
sessionId: sid,
|
||||||
completedAtMs: Date.now(),
|
completedAtMs: Date.now(),
|
||||||
minimal: bootMinimal(ctx)
|
minimal: bootMinimal(ctx),
|
||||||
|
subsystems: {
|
||||||
|
kernel: { ready: true, phaseCount: phaseLog.length },
|
||||||
|
initd: { awaited: true }
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"pins": {}
|
||||||
|
}
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"bundles": [
|
"bundles": [
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/b4a.js",
|
||||||
|
"keys": [
|
||||||
|
"b4a"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/safetyCatch.js",
|
"path": "/lib/bare/bundles/safetyCatch.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -13,12 +19,6 @@
|
|||||||
"hypercoreIdEncoding"
|
"hypercoreIdEncoding"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/b4a.js",
|
|
||||||
"keys": [
|
|
||||||
"b4a"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/compactEncoding.js",
|
"path": "/lib/bare/bundles/compactEncoding.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -31,30 +31,30 @@
|
|||||||
"bareUrl"
|
"bareUrl"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/protomux.js",
|
|
||||||
"keys": [
|
|
||||||
"protomux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareEncoding.js",
|
"path": "/lib/bare/bundles/bareEncoding.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareEncoding"
|
"bareEncoding"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/barePath.js",
|
|
||||||
"keys": [
|
|
||||||
"barePath"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareEvents.js",
|
"path": "/lib/bare/bundles/bareEvents.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareEvents"
|
"bareEvents"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/protomux.js",
|
||||||
|
"keys": [
|
||||||
|
"protomux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/barePath.js",
|
||||||
|
"keys": [
|
||||||
|
"barePath"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareAbort.js",
|
"path": "/lib/bare/bundles/bareAbort.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -73,6 +73,12 @@
|
|||||||
"bareAnsiEscapes"
|
"bareAnsiEscapes"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareAddonResolve.js",
|
||||||
|
"keys": [
|
||||||
|
"bareAddonResolve"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareReadline.js",
|
"path": "/lib/bare/bundles/bareReadline.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -85,24 +91,6 @@
|
|||||||
"bareCrypto"
|
"bareCrypto"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareAddonResolve.js",
|
|
||||||
"keys": [
|
|
||||||
"bareAddonResolve"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareAsyncHooks.js",
|
|
||||||
"keys": [
|
|
||||||
"bareAsyncHooks"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareAtomics.js",
|
|
||||||
"keys": [
|
|
||||||
"bareAtomics"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareAppKit.js",
|
"path": "/lib/bare/bundles/bareAppKit.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -116,9 +104,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareAssert.js",
|
"path": "/lib/bare/bundles/bareAsyncHooks.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareAssert"
|
"bareAsyncHooks"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -127,6 +115,18 @@
|
|||||||
"fetch"
|
"fetch"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareAtomics.js",
|
||||||
|
"keys": [
|
||||||
|
"bareAtomics"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareAssert.js",
|
||||||
|
"keys": [
|
||||||
|
"bareAssert"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareBmp.js",
|
"path": "/lib/bare/bundles/bareBmp.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -139,12 +139,6 @@
|
|||||||
"bareBundleCompile"
|
"bareBundleCompile"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareBuffer.js",
|
|
||||||
"keys": [
|
|
||||||
"bareBuffer"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareBundle.js",
|
"path": "/lib/bare/bundles/bareBundle.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -152,9 +146,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
|
"path": "/lib/bare/bundles/bareBuffer.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareBundleEvaluate"
|
"bareBuffer"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -163,24 +157,36 @@
|
|||||||
"bareBluetoothApple"
|
"bareBluetoothApple"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
|
||||||
|
"keys": [
|
||||||
|
"bareBundleEvaluate"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareBoot.js",
|
"path": "/lib/bare/bundles/bareBoot.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareBoot"
|
"bareBoot"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareDaemon.js",
|
|
||||||
"keys": [
|
|
||||||
"bareDaemon"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareConsole.js",
|
"path": "/lib/bare/bundles/bareConsole.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareConsole"
|
"bareConsole"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareDebugLog.js",
|
||||||
|
"keys": [
|
||||||
|
"bareDebugLog"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareDaemon.js",
|
||||||
|
"keys": [
|
||||||
|
"bareDaemon"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareBundleId.js",
|
"path": "/lib/bare/bundles/bareBundleId.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -193,12 +199,6 @@
|
|||||||
"bareChannel"
|
"bareChannel"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareDebugLog.js",
|
|
||||||
"keys": [
|
|
||||||
"bareDebugLog"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareDelta.js",
|
"path": "/lib/bare/bundles/bareDelta.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -223,6 +223,12 @@
|
|||||||
"bareEnv"
|
"bareEnv"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareExif.js",
|
||||||
|
"keys": [
|
||||||
|
"bareExif"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareCov.js",
|
"path": "/lib/bare/bundles/bareCov.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -235,12 +241,6 @@
|
|||||||
"bareDgram"
|
"bareDgram"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareExif.js",
|
|
||||||
"keys": [
|
|
||||||
"bareExif"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareFfmpeg.js",
|
"path": "/lib/bare/bundles/bareFfmpeg.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -253,12 +253,6 @@
|
|||||||
"bareFfmpegEncodings"
|
"bareFfmpegEncodings"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareFormData.js",
|
|
||||||
"keys": [
|
|
||||||
"bareFormData"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareFormat.js",
|
"path": "/lib/bare/bundles/bareFormat.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -266,9 +260,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareGif.js",
|
"path": "/lib/bare/bundles/bareFormData.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareGif"
|
"bareFormData"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareHeif.js",
|
||||||
|
"keys": [
|
||||||
|
"bareHeif"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -278,15 +278,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareGtk.js",
|
"path": "/lib/bare/bundles/bareGif.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareGtk"
|
"bareGif"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareHeif.js",
|
"path": "/lib/bare/bundles/bareGtk.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareHeif"
|
"bareGtk"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -307,12 +307,6 @@
|
|||||||
"bareHttpParser"
|
"bareHttpParser"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareImageResample.js",
|
|
||||||
"keys": [
|
|
||||||
"bareImageResample"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareIco.js",
|
"path": "/lib/bare/bundles/bareIco.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -320,9 +314,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareHttp1.js",
|
"path": "/lib/bare/bundles/bareImageResample.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareHttp1"
|
"bareImageResample"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -331,6 +325,12 @@
|
|||||||
"bareInspect"
|
"bareInspect"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareHttp1.js",
|
||||||
|
"keys": [
|
||||||
|
"bareHttp1"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareHttps.js",
|
"path": "/lib/bare/bundles/bareHttps.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -361,6 +361,12 @@
|
|||||||
"bareLief"
|
"bareLief"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareLogger.js",
|
||||||
|
"keys": [
|
||||||
|
"bareLogger"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareLink.js",
|
"path": "/lib/bare/bundles/bareLink.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -373,18 +379,6 @@
|
|||||||
"bareInspector"
|
"bareInspector"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareLogger.js",
|
|
||||||
"keys": [
|
|
||||||
"bareLogger"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareModuleLexer.js",
|
|
||||||
"keys": [
|
|
||||||
"bareModuleLexer"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareMake.js",
|
"path": "/lib/bare/bundles/bareMake.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -398,15 +392,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareModule.js",
|
"path": "/lib/bare/bundles/bareModuleLexer.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareModule"
|
"bareModuleLexer"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareNdk.js",
|
|
||||||
"keys": [
|
|
||||||
"bareNdk"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -416,9 +404,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareNative.js",
|
"path": "/lib/bare/bundles/bareModule.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareNative"
|
"bareModule"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -428,39 +416,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareOpen.js",
|
"path": "/lib/bare/bundles/bareNdk.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareOpen"
|
"bareNdk"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareNet.js",
|
"path": "/lib/bare/bundles/bareNative.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareNet"
|
"bareNative"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareMedia.js",
|
|
||||||
"keys": [
|
|
||||||
"bareMedia"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareOs.js",
|
|
||||||
"keys": [
|
|
||||||
"bareOs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/barePack.js",
|
|
||||||
"keys": [
|
|
||||||
"barePack"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareNodeRuntime.js",
|
|
||||||
"keys": [
|
|
||||||
"bareNodeRuntime"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -470,9 +434,27 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePng.js",
|
"path": "/lib/bare/bundles/bareOs.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"barePng"
|
"bareOs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareOpen.js",
|
||||||
|
"keys": [
|
||||||
|
"bareOpen"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareMedia.js",
|
||||||
|
"keys": [
|
||||||
|
"bareMedia"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareNet.js",
|
||||||
|
"keys": [
|
||||||
|
"bareNet"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -481,12 +463,30 @@
|
|||||||
"barePerformance"
|
"barePerformance"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/barePng.js",
|
||||||
|
"keys": [
|
||||||
|
"barePng"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePackDrive.js",
|
"path": "/lib/bare/bundles/barePackDrive.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"barePackDrive"
|
"barePackDrive"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/barePack.js",
|
||||||
|
"keys": [
|
||||||
|
"barePack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/barePipe.js",
|
||||||
|
"keys": [
|
||||||
|
"barePipe"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePunycode.js",
|
"path": "/lib/bare/bundles/barePunycode.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -505,18 +505,18 @@
|
|||||||
"barePrebuild"
|
"barePrebuild"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/barePipe.js",
|
|
||||||
"keys": [
|
|
||||||
"barePipe"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareQueueMicrotask.js",
|
"path": "/lib/bare/bundles/bareQueueMicrotask.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareQueueMicrotask"
|
"bareQueueMicrotask"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareNodeRuntime.js",
|
||||||
|
"keys": [
|
||||||
|
"bareNodeRuntime"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareRealm.js",
|
"path": "/lib/bare/bundles/bareRealm.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -541,12 +541,6 @@
|
|||||||
"bareRuntime"
|
"bareRuntime"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareSdl.js",
|
|
||||||
"keys": [
|
|
||||||
"bareSdl"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareRepl.js",
|
"path": "/lib/bare/bundles/bareRepl.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -559,6 +553,12 @@
|
|||||||
"bareRpc"
|
"bareRpc"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareSdl.js",
|
||||||
|
"keys": [
|
||||||
|
"bareSdl"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareSemver.js",
|
"path": "/lib/bare/bundles/bareSemver.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -571,12 +571,6 @@
|
|||||||
"bareRun"
|
"bareRun"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareSidecar.js",
|
|
||||||
"keys": [
|
|
||||||
"bareSidecar"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareSignals.js",
|
"path": "/lib/bare/bundles/bareSignals.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -584,9 +578,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareStorage.js",
|
"path": "/lib/bare/bundles/bareSidecar.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareStorage"
|
"bareSidecar"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -596,9 +590,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareStringDecoder.js",
|
"path": "/lib/bare/bundles/bareStorage.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareStringDecoder"
|
"bareStorage"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -607,6 +601,12 @@
|
|||||||
"bareStdio"
|
"bareStdio"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareStringDecoder.js",
|
||||||
|
"keys": [
|
||||||
|
"bareStringDecoder"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareSvg.js",
|
"path": "/lib/bare/bundles/bareSvg.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -625,6 +625,12 @@
|
|||||||
"bareStructuredClone"
|
"bareStructuredClone"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareTiff.js",
|
||||||
|
"keys": [
|
||||||
|
"bareTiff"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareTap.js",
|
"path": "/lib/bare/bundles/bareTap.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -638,15 +644,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareTiff.js",
|
"path": "/lib/bare/bundles/bareThread.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareTiff"
|
"bareThread"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareTimers.js",
|
|
||||||
"keys": [
|
|
||||||
"bareTimers"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -655,18 +655,30 @@
|
|||||||
"bareTcp"
|
"bareTcp"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareThread.js",
|
|
||||||
"keys": [
|
|
||||||
"bareThread"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareTpl.js",
|
"path": "/lib/bare/bundles/bareTpl.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareTpl"
|
"bareTpl"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareTimers.js",
|
||||||
|
"keys": [
|
||||||
|
"bareTimers"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareTls.js",
|
||||||
|
"keys": [
|
||||||
|
"bareTls"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareTty.js",
|
||||||
|
"keys": [
|
||||||
|
"bareTty"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareType.js",
|
"path": "/lib/bare/bundles/bareType.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -679,30 +691,24 @@
|
|||||||
"bareUiKit"
|
"bareUiKit"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareTls.js",
|
|
||||||
"keys": [
|
|
||||||
"bareTls"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareUnpack.js",
|
"path": "/lib/bare/bundles/bareUnpack.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareUnpack"
|
"bareUnpack"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareTty.js",
|
|
||||||
"keys": [
|
|
||||||
"bareTty"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareV8.js",
|
"path": "/lib/bare/bundles/bareV8.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareV8"
|
"bareV8"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareWalkHandles.js",
|
||||||
|
"keys": [
|
||||||
|
"bareWalkHandles"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareVm.js",
|
"path": "/lib/bare/bundles/bareVm.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -715,12 +721,6 @@
|
|||||||
"bareUnionBundle"
|
"bareUnionBundle"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareWalkHandles.js",
|
|
||||||
"keys": [
|
|
||||||
"bareWalkHandles"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareWebKit.js",
|
"path": "/lib/bare/bundles/bareWebKit.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -739,18 +739,18 @@
|
|||||||
"bareWebp"
|
"bareWebp"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareUtils.js",
|
|
||||||
"keys": [
|
|
||||||
"bareUtils"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareWhich.js",
|
"path": "/lib/bare/bundles/bareWhich.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareWhich"
|
"bareWhich"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareUtils.js",
|
||||||
|
"keys": [
|
||||||
|
"bareUtils"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
|
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -769,12 +769,6 @@
|
|||||||
"bareXdiff"
|
"bareXdiff"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareZlib.js",
|
|
||||||
"keys": [
|
|
||||||
"bareZlib"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareWs.js",
|
"path": "/lib/bare/bundles/bareWs.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -782,9 +776,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareZmq.js",
|
"path": "/lib/bare/bundles/bareZlib.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareZmq"
|
"bareZlib"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -792,6 +786,12 @@
|
|||||||
"keys": [
|
"keys": [
|
||||||
"bareWorker"
|
"bareWorker"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareZmq.js",
|
||||||
|
"keys": [
|
||||||
|
"bareZmq"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"bundleStats": {
|
"bundleStats": {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,31 @@
|
|||||||
|
import { readFile } from 'fs/promises'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When `pear.multisig.json` exists next to the kernel tree, validate shape and log status.
|
||||||
|
* Mirrors Holepunch `pear-multisig-link` style metadata (operational hint, not cryptography).
|
||||||
|
* @param {string} kernelRoot
|
||||||
|
*/
|
||||||
|
export async function logPearMultisigKernelHint(kernelRoot) {
|
||||||
|
const f = path.join(kernelRoot, 'pear.multisig.json')
|
||||||
|
try {
|
||||||
|
const raw = await readFile(f, 'utf8')
|
||||||
|
const j = JSON.parse(raw)
|
||||||
|
const signers = j.signers
|
||||||
|
const quorum = j.quorum
|
||||||
|
if (!Array.isArray(signers) || typeof quorum !== 'number') {
|
||||||
|
console.warn('[seeder] pear.multisig.json: expected { signers: [], quorum: number }')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (quorum < 1 || quorum > signers.length) {
|
||||||
|
console.warn('[seeder] pear.multisig.json: quorum out of range')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log(
|
||||||
|
`[seeder] pear.multisig.json OK (${signers.length} signers, quorum ${quorum})`
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
|
if (/** @type {NodeJS.ErrnoException} */ (e).code === 'ENOENT') return
|
||||||
|
console.warn('[seeder] pear.multisig.json:', e?.message || e)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user