145 lines
4.2 KiB
JavaScript
145 lines
4.2 KiB
JavaScript
/**
|
|
* Optional `/bin` manifest on the personal drive for large images (`BARE_OS_VFS_BIN_INDEX_BUILD=1`).
|
|
*/
|
|
|
|
/**
|
|
* Deterministic SHA-256 over UTF-8 names with `\0` terminators (Web Crypto — Node 20 + Bare).
|
|
* @param {string[]} sortedNames
|
|
*/
|
|
async function bareOsBinIndexNamesDigestAsync(sortedNames) {
|
|
const enc = new TextEncoder()
|
|
let totalLen = 0
|
|
for (const n of sortedNames) {
|
|
totalLen += enc.encode(String(n)).byteLength + 1
|
|
}
|
|
const u8 = new Uint8Array(totalLen)
|
|
let o = 0
|
|
for (const n of sortedNames) {
|
|
const s = enc.encode(String(n))
|
|
u8.set(s, o)
|
|
o += s.byteLength
|
|
u8[o++] = 0
|
|
}
|
|
const subtle = globalThis.crypto && globalThis.crypto.subtle
|
|
if (!subtle || typeof subtle.digest !== 'function') {
|
|
throw new Error('bare-os bin index: Web Crypto subtle.digest unavailable')
|
|
}
|
|
const digest = await subtle.digest('SHA-256', u8)
|
|
return Array.from(new Uint8Array(digest), (b) =>
|
|
b.toString(16).padStart(2, '0')
|
|
).join('')
|
|
}
|
|
|
|
/**
|
|
* When **`BARE_OS_BIN_HYPERBEE_INDEX=1`**, writes **`/.bare-os/index/bin-hyperbee-hint.json`**
|
|
* (module resolution probe + name list) for operators integrating Hyperbee/Corestore offline.
|
|
*/
|
|
export async function maybeBareOsBuildBinHyperbeeHint(ctx) {
|
|
const e = ctx.env
|
|
if (
|
|
e.BARE_OS_BIN_HYPERBEE_INDEX !== '1' &&
|
|
e.BARE_OS_BIN_HYPERBEE_INDEX !== 'true'
|
|
) {
|
|
return
|
|
}
|
|
const vfs = ctx.vfs
|
|
const drive = ctx.drive
|
|
const b4 = ctx.b4a
|
|
if (!vfs || !drive || typeof drive.readdir !== 'function' || !b4) return
|
|
let moduleResolves = false
|
|
try {
|
|
await import('hyperbee')
|
|
moduleResolves = true
|
|
} catch {
|
|
moduleResolves = false
|
|
}
|
|
const names = []
|
|
try {
|
|
const stream = drive.readdir('/bin')
|
|
for await (const n of stream) names.push(String(n))
|
|
} catch {
|
|
return
|
|
}
|
|
names.sort()
|
|
const digest = await bareOsBinIndexNamesDigestAsync(names)
|
|
const body =
|
|
JSON.stringify({
|
|
schema: 2,
|
|
atMs: Date.now(),
|
|
namesDigest: digest,
|
|
namesDigestAlgo: 'sha256-null-suffixed-utf8',
|
|
moduleResolves,
|
|
count: names.length,
|
|
names: names.slice(0, 8000),
|
|
invalidation: {
|
|
schema: 1,
|
|
triggers: ['bareOsVfsBatchWrite bin/*', 'BARE_OS_VFS_BIN_INDEX_BUILD'],
|
|
note: 'Rebuild when batch writes touch /bin or on boot when index env flags are set.'
|
|
},
|
|
note:
|
|
'Hyperbee-backed KV index is host-wired; this file lists /bin names and whether hyperbee imports on the booter runtime.'
|
|
}) + '\n'
|
|
try {
|
|
await vfs.writeFile('/.bare-os/index/bin-hyperbee-hint.json', b4.from(body))
|
|
} catch (err) {
|
|
ctx.console?.error?.(
|
|
'[bare-os] bin hyperbee hint: ' + ((err && err.message) || String(err))
|
|
)
|
|
}
|
|
try {
|
|
const idx =
|
|
JSON.stringify({
|
|
schema: 2,
|
|
atMs: Date.now(),
|
|
namesDigest: digest,
|
|
primaryHint: '/.bare-os/index/bin-hyperbee-hint.json',
|
|
note: 'Secondary metadata path for `/.bare-os/indexes/` namespace (Hyperbee operator integration).'
|
|
}) + '\n'
|
|
await vfs.writeFile('/.bare-os/indexes/hyperbee_status.json', b4.from(idx))
|
|
} catch {
|
|
/* personal tree may deny path on some images */
|
|
}
|
|
}
|
|
|
|
export async function maybeBareOsBuildBinManifest(ctx) {
|
|
const e = ctx.env
|
|
if (
|
|
e.BARE_OS_VFS_BIN_INDEX_BUILD !== '1' &&
|
|
e.BARE_OS_VFS_BIN_INDEX_BUILD !== 'true'
|
|
) {
|
|
return
|
|
}
|
|
const vfs = ctx.vfs
|
|
const drive = ctx.drive
|
|
const b4 = ctx.b4a
|
|
if (!vfs || !drive || typeof drive.readdir !== 'function' || !b4) return
|
|
/** @type {string[]} */
|
|
const names = []
|
|
try {
|
|
const stream = drive.readdir('/bin')
|
|
for await (const n of stream) names.push(String(n))
|
|
} catch {
|
|
return
|
|
}
|
|
names.sort()
|
|
const digest = await bareOsBinIndexNamesDigestAsync(names)
|
|
const body =
|
|
JSON.stringify({
|
|
schema: 2,
|
|
atMs: Date.now(),
|
|
path: '/bin',
|
|
namesDigest: digest,
|
|
namesDigestAlgo: 'sha256-null-suffixed-utf8',
|
|
count: names.length,
|
|
names: names.slice(0, 8000),
|
|
note: 'Sorted lexicographic /bin listing; digest covers full sorted set before truncation.'
|
|
}) + '\n'
|
|
try {
|
|
await vfs.writeFile('/.bare-os/index/bin-manifest.json', b4.from(body))
|
|
} catch (err) {
|
|
ctx.console?.error?.(
|
|
'[bare-os] bin index: ' + ((err && err.message) || String(err))
|
|
)
|
|
}
|
|
}
|