Added missing locale command support:
packages/bare-os-coreutils/src/locale.js registered in packages/bare-os-coreutils/lib/commands.mjs Fixed trustctl generated runtime mismatch: added preamble wiring in packages/bare-os-coreutils/build.mjs (trustctl: ['p2p-suite.js']) Improved help behavior: packages/bare-os-coreutils/src/ssh-keygen.js (help, -h, --help, -?) packages/bare-os-coreutils/src/oidc-publish.js (help detection across args) Improved procstat empty-state UX while preserving JSON output: packages/bare-os-coreutils/src/procstat.js adds sourcePath and additive hint when process table is empty Enforced stricter network behavior: packages/bare-os-coreutils/src/whois.js prefers ctx.httpFetch, adds bounded timeout via AbortController clearer timeout/policy/offline error mapping packages/bare-os-coreutils/src/telnet.js strict-close behavior by default --strict-close / --no-strict-close BARE_OS_TELNET_STRICT_CLOSE env override Implemented expected-item UX/compat updates: packages/bare-os-coreutils/src/iconv.js: added -l/--list, improved encoding-pair error messaging packages/bare-os-coreutils/src/crontab.js: clearer -l empty-file message packages/bare-os-coreutils/src/getconf.js: added PAGE_SIZE / PAGESIZE aliases packages/bare-os-coreutils/src/nproc.js: clarified help text packages/bare-os-coreutils/src/time.js: clarified output comments/behavior
This commit is contained in:
@@ -17,7 +17,7 @@ async function run(ctx, argv) {
|
||||
try {
|
||||
const b = await vfs.readFile(tabPath)
|
||||
if (!b || !b.length) {
|
||||
ctx.console.log('no crontab for user')
|
||||
ctx.console.log('no crontab for user (empty ~/.crontab)')
|
||||
return
|
||||
}
|
||||
const s = ctx.b4a.toString(b)
|
||||
|
||||
@@ -44,6 +44,8 @@ const CONF = {
|
||||
_POSIX_THREAD_ATTR_STACKSIZE: '65536',
|
||||
_SC_PAGESIZE: '4096',
|
||||
_SC_PAGE_SIZE: '4096',
|
||||
PAGE_SIZE: '4096',
|
||||
PAGESIZE: '4096',
|
||||
_SC_OPEN_MAX: '256',
|
||||
_SC_STREAM_MAX: '256',
|
||||
_SC_CHILD_MAX: '0',
|
||||
|
||||
@@ -63,12 +63,17 @@ function encodeFromString(s, enc, b4a) {
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const encList = ['utf-8', 'iso-8859-1', 'utf-16le', 'utf-16be']
|
||||
let fromEnc = 'utf-8'
|
||||
let toEnc = 'utf-8'
|
||||
let file = null
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = argv[i]
|
||||
if (a === '--') continue
|
||||
if (a === '-l' || a === '--list') {
|
||||
ctx.console.log(encList.join('\n'))
|
||||
return
|
||||
}
|
||||
if (a === '-f' || a === '--from-code') {
|
||||
i++
|
||||
if (i >= argv.length) {
|
||||
@@ -98,15 +103,16 @@ async function run(ctx, argv) {
|
||||
break
|
||||
}
|
||||
|
||||
const supported = new Set([
|
||||
'utf-8',
|
||||
'iso-8859-1',
|
||||
'utf-16le',
|
||||
'utf-16be'
|
||||
])
|
||||
const supported = new Set(encList)
|
||||
if (!supported.has(fromEnc) || !supported.has(toEnc)) {
|
||||
ctx.console.error(
|
||||
'iconv: unsupported encoding pair (supported: utf-8, iso-8859-1, utf-16le, utf-16be)'
|
||||
'iconv: unsupported encoding pair ' +
|
||||
fromEnc +
|
||||
' -> ' +
|
||||
toEnc +
|
||||
' (supported: ' +
|
||||
encList.join(', ') +
|
||||
'; use iconv -l)'
|
||||
)
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
async function run(ctx, argv) {
|
||||
const args = argv.slice(1)
|
||||
if (!args.length || args.includes('-a')) {
|
||||
ctx.console.log('C\nPOSIX\nC.UTF-8')
|
||||
return
|
||||
}
|
||||
if (args.includes('-h') || args.includes('--help')) {
|
||||
ctx.console.log(
|
||||
'usage: locale [-a]\nList available locales in this runtime.'
|
||||
)
|
||||
return
|
||||
}
|
||||
ctx.console.error('locale: unsupported option ' + args[0])
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
@@ -3,7 +3,7 @@ async function run(ctx, argv) {
|
||||
const a = argv[i]
|
||||
if (a === '-h' || a === '--help') {
|
||||
ctx.console.log(
|
||||
'usage: nproc [--all]\nPrint number of processing units (from /proc/cpuinfo or 1). --all is accepted for compatibility.'
|
||||
'usage: nproc [--all]\nPrint number of processing units (from /proc/cpuinfo, BARE_OS_NPROC, or fallback 1). --all is accepted for compatibility.'
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
* 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') {
|
||||
const args = argv.slice(1)
|
||||
if (args.includes('help') || args.includes('-h') || args.includes('--help')) {
|
||||
ctx.console.log(`oidc-publish — optional OIDC token helper
|
||||
|
||||
Reads OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET (or argv) and POSTs
|
||||
|
||||
@@ -20,12 +20,18 @@ async function run(ctx, argv) {
|
||||
return
|
||||
}
|
||||
const rows = Array.isArray(table.processes) ? table.processes : []
|
||||
const emptyHint =
|
||||
rows.length === 0
|
||||
? 'process table is empty (provider may be unavailable or no tracked processes)'
|
||||
: null
|
||||
ctx.console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
schema: 1,
|
||||
atMs: Date.now(),
|
||||
processCount: rows.length,
|
||||
sourcePath: '/proc/bare_os/process_table.json',
|
||||
hint: emptyHint,
|
||||
processes: rows
|
||||
},
|
||||
null,
|
||||
|
||||
@@ -2,16 +2,22 @@
|
||||
* Ed25519 key generation via booter `ssh-keygen-cli.js` (bare-crypto + VFS).
|
||||
*/
|
||||
async function run(ctx, argv) {
|
||||
if (typeof ctx.bareOsRunSshKeygenCli === 'function') {
|
||||
await ctx.bareOsRunSshKeygenCli(argv)
|
||||
return
|
||||
}
|
||||
const args = argv.slice(1)
|
||||
if (args.includes('-h') || args.includes('--help') || args.includes('-?')) {
|
||||
if (
|
||||
args.includes('help') ||
|
||||
args.includes('-h') ||
|
||||
args.includes('--help') ||
|
||||
args.includes('-?')
|
||||
) {
|
||||
ctx.console.log(
|
||||
'Usage: ssh-keygen -t ed25519 -f KEYFILE [-N ""] [-C comment]\n' +
|
||||
'Requires ctx.bareOsRunSshKeygenCli from the stock booter.'
|
||||
)
|
||||
ctx.exitCode = 0
|
||||
return
|
||||
}
|
||||
if (typeof ctx.bareOsRunSshKeygenCli === 'function') {
|
||||
await ctx.bareOsRunSshKeygenCli(argv)
|
||||
return
|
||||
}
|
||||
ctx.console.error(
|
||||
|
||||
@@ -19,7 +19,7 @@ function telnetUsage() {
|
||||
return (
|
||||
'usage: telnet [--ipv4|--ipv6] [--connect-timeout-ms N] [--timeout-ms N] [--keepalive-ms N]\n' +
|
||||
' [--reconnect N] [--reconnect-delay-ms N] [--naws-cols N] [--naws-rows N]\n' +
|
||||
' [--ttype NAME] host [port]\n' +
|
||||
' [--ttype NAME] [--strict-close|--no-strict-close] host [port]\n' +
|
||||
'\n' +
|
||||
'escape command mode: Ctrl-] then one of: quit, close, reconnect, status, send <text>\n'
|
||||
)
|
||||
@@ -275,7 +275,8 @@ function bareTelnetParseArgs(argv) {
|
||||
reconnectDelayMs: 1000,
|
||||
nawsCols: 80,
|
||||
nawsRows: 24,
|
||||
ttype: 'bare-os'
|
||||
ttype: 'bare-os',
|
||||
strictClose: true
|
||||
}
|
||||
var pos = []
|
||||
for (var i = 0; i < rest.length; i++) {
|
||||
@@ -321,6 +322,14 @@ function bareTelnetParseArgs(argv) {
|
||||
opts.ttype = String(rest[++i] || 'bare-os')
|
||||
continue
|
||||
}
|
||||
if (a === '--strict-close') {
|
||||
opts.strictClose = true
|
||||
continue
|
||||
}
|
||||
if (a === '--no-strict-close') {
|
||||
opts.strictClose = false
|
||||
continue
|
||||
}
|
||||
if (a.startsWith('-')) return { error: 'unknown option: ' + a }
|
||||
pos.push(a)
|
||||
}
|
||||
@@ -550,6 +559,8 @@ async function bareTelnetRunSession(ctx, host, port, opts) {
|
||||
var parseState = bareTelnetNewParseState()
|
||||
var nego = bareTelnetMakeNego(opts)
|
||||
var ended = false
|
||||
var sawData = false
|
||||
var userClosed = false
|
||||
var localRaw = false
|
||||
var idleTimer = null
|
||||
var keepTimer = null
|
||||
@@ -595,6 +606,7 @@ async function bareTelnetRunSession(ctx, host, port, opts) {
|
||||
var parsed = bareTelnetParseChunk(parseState, chunk)
|
||||
parseState = parsed.state
|
||||
if (parsed.data.length) bareTelnetWriteOut(stdout, bareTelnetDecodeUtf8(parsed.data))
|
||||
if (parsed.data.length) sawData = true
|
||||
for (var i = 0; i < parsed.commands.length; i++) {
|
||||
var c = parsed.commands[i]
|
||||
if (c.kind === 'neg') {
|
||||
@@ -622,6 +634,7 @@ async function bareTelnetRunSession(ctx, host, port, opts) {
|
||||
}
|
||||
if (cmd === 'quit' || cmd === 'close') {
|
||||
ended = true
|
||||
userClosed = true
|
||||
try {
|
||||
socket.end()
|
||||
} catch {
|
||||
@@ -652,6 +665,10 @@ async function bareTelnetRunSession(ctx, host, port, opts) {
|
||||
}
|
||||
var onSocketEnd = function () {
|
||||
ended = true
|
||||
if (opts.strictClose && !userClosed && !sawData) {
|
||||
reject(new Error('remote closed connection before interactive session'))
|
||||
return
|
||||
}
|
||||
resolve()
|
||||
}
|
||||
var onSocketError = function (e) {
|
||||
@@ -717,5 +734,8 @@ async function run(ctx, argv) {
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const envStrict = String(ctx.vfs?.env?.BARE_OS_TELNET_STRICT_CLOSE || '').trim()
|
||||
if (envStrict === '0' || envStrict === 'false') parsed.opts.strictClose = false
|
||||
if (envStrict === '1' || envStrict === 'true') parsed.opts.strictClose = true
|
||||
await bareTelnetRunSession(ctx, parsed.host, parsed.port, parsed.opts)
|
||||
}
|
||||
|
||||
@@ -21,12 +21,14 @@ async function run(ctx, argv) {
|
||||
} finally {
|
||||
const ms = Date.now() - t0
|
||||
if (portable) {
|
||||
// POSIX portable format; user/sys are placeholders in this runtime.
|
||||
ctx.console.error(
|
||||
'real ' +
|
||||
(ms / 1000).toFixed(2) +
|
||||
'\nuser 0.00\nsys 0.00'
|
||||
)
|
||||
} else {
|
||||
// Non-portable output goes to stderr, matching common time(1) behavior.
|
||||
ctx.console.error('real\t' + (ms / 1000).toFixed(3) + 's')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,8 +75,15 @@ function isLikelyDomain(s) {
|
||||
return true
|
||||
}
|
||||
|
||||
async function fetchJson(fetchFn, url) {
|
||||
const res = await fetchFn(url, { method: 'GET' })
|
||||
async function fetchJson(fetchFn, url, timeoutMs) {
|
||||
const ac = new AbortController()
|
||||
const tid = setTimeout(() => ac.abort(), timeoutMs)
|
||||
let res
|
||||
try {
|
||||
res = await fetchFn(url, { method: 'GET', signal: ac.signal })
|
||||
} finally {
|
||||
clearTimeout(tid)
|
||||
}
|
||||
if (!res || !res.ok) {
|
||||
const status = res ? `${res.status} ${res.statusText || ''}`.trim() : 'unknown'
|
||||
throw new Error('HTTP error from ' + url + ': ' + status)
|
||||
@@ -291,11 +298,23 @@ async function run(ctx, argv) {
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
if (typeof fetch !== 'function') {
|
||||
const fetchFn =
|
||||
typeof ctx.httpFetch === 'function'
|
||||
? ctx.httpFetch
|
||||
: typeof fetch === 'function'
|
||||
? fetch
|
||||
: null
|
||||
if (!fetchFn) {
|
||||
ctx.console.error('whois: fetch is unavailable in this runtime')
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const timeoutRaw = Number.parseInt(
|
||||
String(ctx.vfs?.env?.BARE_OS_WHOIS_TIMEOUT_MS || '10000'),
|
||||
10
|
||||
)
|
||||
const timeoutMs =
|
||||
Number.isFinite(timeoutRaw) && timeoutRaw > 0 ? Math.min(timeoutRaw, 60000) : 10000
|
||||
|
||||
let bootstrapUrl = ''
|
||||
let rdapPath = ''
|
||||
@@ -311,10 +330,14 @@ async function run(ctx, argv) {
|
||||
}
|
||||
|
||||
try {
|
||||
const bootstrap = await fetchJson(fetch, bootstrapUrl)
|
||||
const bootstrap = await fetchJson(fetchFn, bootstrapUrl, timeoutMs)
|
||||
let base = pickBaseUrlFromBootstrap(target.kind, target.query, bootstrap)
|
||||
if (!base && target.kind === 'ip' && isIpv6(target.query)) {
|
||||
const ipv6Bootstrap = await fetchJson(fetch, 'https://data.iana.org/rdap/ipv6.json')
|
||||
const ipv6Bootstrap = await fetchJson(
|
||||
fetchFn,
|
||||
'https://data.iana.org/rdap/ipv6.json',
|
||||
timeoutMs
|
||||
)
|
||||
base = pickBaseUrlFromBootstrap(target.kind, target.query, ipv6Bootstrap)
|
||||
}
|
||||
if (!base) {
|
||||
@@ -322,11 +345,20 @@ async function run(ctx, argv) {
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
const rdap = await fetchJson(fetch, joinUrl(base, rdapPath))
|
||||
const rdap = await fetchJson(fetchFn, joinUrl(base, rdapPath), timeoutMs)
|
||||
if (parsed.json) ctx.console.log(JSON.stringify(rdap, null, 2))
|
||||
else printSummary(ctx, target.kind, target.query, rdap)
|
||||
} catch (err) {
|
||||
ctx.console.error('whois: ' + (err && err.message ? err.message : String(err)))
|
||||
const msg = String((err && err.message) || err || '')
|
||||
if ((err && err.name === 'AbortError') || /abort|timeout/i.test(msg)) {
|
||||
ctx.console.error('whois: network timeout after ' + timeoutMs + 'ms')
|
||||
} else if (/allowlist|denylist|HTTP blocked/i.test(msg)) {
|
||||
ctx.console.error('whois: blocked by HTTP policy: ' + msg)
|
||||
} else if (/ENOTFOUND|EAI_AGAIN|ENETUNREACH|EHOSTUNREACH/i.test(msg)) {
|
||||
ctx.console.error('whois: network unavailable or DNS failure')
|
||||
} else {
|
||||
ctx.console.error('whois: ' + msg)
|
||||
}
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user