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:
@@ -92,6 +92,7 @@ const preamble = {
|
||||
p2ptrace: ['p2p-suite.js'],
|
||||
peerctl: ['p2p-suite.js'],
|
||||
appctl: ['p2p-suite.js'],
|
||||
trustctl: ['p2p-suite.js'],
|
||||
meshdrop: ['p2p-suite.js'],
|
||||
taskmesh: ['p2p-suite.js'],
|
||||
peernote: ['p2p-suite.js'],
|
||||
|
||||
@@ -84,6 +84,7 @@ export const COREUTILS_COMMANDS = [
|
||||
'login',
|
||||
'logout',
|
||||
'logname',
|
||||
'locale',
|
||||
'ls',
|
||||
'man',
|
||||
'md5sum',
|
||||
|
||||
@@ -1,65 +1,15 @@
|
||||
{
|
||||
"name": "agent",
|
||||
"section": 1,
|
||||
"title": "OpenAI-compatible autonomous coding agent",
|
||||
"title": "agent",
|
||||
"synopsis": [
|
||||
"agent [--setup | --config | --reset]",
|
||||
"agent reset",
|
||||
"agent [--setup | --config] REQUEST"
|
||||
"agent [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Runs a persistent ReAct-style loop against any OpenAI-compatible HTTPS API (Groq, OpenAI, xAI, etc.). **No environment variables** are used for credentials: everything lives in **`~/.agent/config.json`** on your **personal Hyperdrive**, created on first run with Groq-oriented defaults. Config also supports **`allow_delete`** (default false) and optional **`require_confirm_token`** for the **`delete_path`** tool when delete is enabled.\n\nOn a **TTY**, the interactive shell line editor is suspended during the session (**`suspendReplForSubprocess`**). Assistant output streams with ANSI highlighting; tool invocations are labeled.\n\n**Interrupt:** **Ctrl+C** aborts in-flight HTTPS requests (**`SIGINT`** / **`AbortSignal`**) and restores the REPL.\n\n**HTTPS policy:** Delegated **`fetch`** follows the booter (**`ctx.httpFetch`**). Where **`BARE_OS_HTTP_ALLOWLIST`** is set, operators must include your provider hostname (for example **`api.groq.com`**, **`api.openai.com`**, **`api.x.ai`**) **and** every host the **`web_fetch`** tool may retrieve (**`http(s)`** pages or APIs for the model).\n\n**Tools (VFS & shell):** **`read_file`**, **`write_file`**, **`edit_file`**, **`create_directory`**, **`list_directory`**, **`file_stat`**, **`move_path`**, **`delete_path`**, **`search_files`** (**`grep`**), **`run_command`** (optional **`capture_exit`** for a final **`EXIT:`** line in the capture file), **`run_js_script`**, **`run_js_script_at_path`**, **`get_system_info`**, **`get_resource_limits`**, **`read_proc_file`**, **`get_swarm_peers`**, **`read_man_page`**, **`apropos_man`**, **`read_skill`** (load **`SKILL.md`** from **`~/.agent/workspace/skills/`** or **`~/.agent/skills/`**), **`edit_agent_config`**, **`list_bin`**, **`verification_hints`** (suggested repo-root checks for a host checkout), **`runtime_diagnostic_bundle`** (one-shot allowlisted **`/proc`** + resource snapshot), **`task_complete`**. **Node is not installed** — use **`run_js_script`** (or a saved **`.mjs`** with **`run_js_script_at_path`**) for JavaScript; not via **`node` / `npx`**. The merged manual is at **`/share/man/man.json`**; prefer **`read_man_page`** / **`apropos_man`** over shelling **`man`** when you only need text.",
|
||||
"options": [
|
||||
{
|
||||
"flag": "--setup",
|
||||
"meaning": "interactive TTY wizard (**`--config`** alias): owner name, agent label, REST base URL, API key, model, provider; writes **`~/.agent/config.json`** (plain stdin/stdout; suspends shell line editor first)"
|
||||
},
|
||||
{
|
||||
"flag": "--config",
|
||||
"meaning": "same as **`--setup`**"
|
||||
},
|
||||
{
|
||||
"flag": "--help",
|
||||
"meaning": "usage summary (also **`-h`**)"
|
||||
},
|
||||
{
|
||||
"flag": "--reset",
|
||||
"meaning": "clear **`~/.agent/history.json`** and truncate **`~/.agent/progress.txt`** (fresh chat session; **`config.json`** unchanged)"
|
||||
}
|
||||
],
|
||||
"files": [
|
||||
"~/.agent/config.json — REST endpoint, bearer token, model, sampling, **max_iterations**, **allow_delete**, **require_confirm_token**",
|
||||
"~/.agent/history.json — truncated chat messages",
|
||||
"~/.agent/progress.txt — human-readable tool log",
|
||||
"~/.agent/instructions.md — optional user goals (**read** once per session)",
|
||||
"~/.agent/context.md — optional long-running context (**read** once per session)",
|
||||
"~/.agent/last_command_out.txt — stdout/stderr capture for **`run_command`** when possible",
|
||||
"~/.agent/workspace/*.md — agent soul Markdown (**SOUL.md**, **AGENTS.md**, …); seeded from **`/share/agent-workspace/`** when **`SOUL.md`** is missing",
|
||||
"~/.agent/workspace/memory/YYYY-MM-DD.md — optional daily memory append",
|
||||
"~/.agent/workspace/skills/<id>/SKILL.md — modular skills (compact index in system prompt; **`read_skill`** for full text)",
|
||||
"~/.agent/skills/ — optional global skills (lower precedence than workspace/skills/ for same name)",
|
||||
"~/.agent/README-agent.md, loader.js, index.js, skill-loader.js — seeded stubs/docs (see /share/agent-workspace/README-agent.md)"
|
||||
],
|
||||
"keywords": ["agent", "openai", "groq", "rest", "llm", "react", "tools"],
|
||||
"examples": [
|
||||
{
|
||||
"caption": "configure credentials",
|
||||
"code": "agent --setup"
|
||||
},
|
||||
{
|
||||
"caption": "clear chat session history",
|
||||
"code": "agent --reset"
|
||||
},
|
||||
{
|
||||
"caption": "natural-language task",
|
||||
"code": "agent \"list ten random names from /bin\""
|
||||
}
|
||||
],
|
||||
"seeAlso": [
|
||||
{ "name": "man", "section": 1 },
|
||||
{ "name": "bare-os-shell", "section": 7 },
|
||||
{ "name": "curl", "section": 1 },
|
||||
{ "name": "wget", "section": 1 },
|
||||
{ "name": "chat", "section": 1 }
|
||||
],
|
||||
"bareOsNotes": "Session blocks shell input via REPL suspend. **`--setup`** / **`--config`** use plain stdin/stdout prompts (not **`ctx.readLine`**). API key may echo in the terminal. Prefer **`chmod 600`** on **`config.json`** (best-effort)."
|
||||
"description": "Bare OS implementation of agent. Full behavior is defined in packages/bare-os-coreutils/src/agent.js.",
|
||||
"options": [],
|
||||
"keywords": [
|
||||
"agent",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "appctl",
|
||||
"section": 1,
|
||||
"title": "Pear app registry and launch utility",
|
||||
"title": "appctl",
|
||||
"synopsis": [
|
||||
"appctl list",
|
||||
"appctl show NAME",
|
||||
"appctl install NAME PEAR_LINK [CHANNEL]",
|
||||
"appctl launch NAME [--checkout MODE]",
|
||||
"appctl channels"
|
||||
"appctl [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Maintains a local pear:// app registry and emits launch requests through the P2P control plane.",
|
||||
"description": "Bare OS implementation of appctl. Full behavior is defined in packages/bare-os-coreutils/src/appctl.js.",
|
||||
"options": [],
|
||||
"keywords": ["pear", "apps", "registry", "launch"]
|
||||
"keywords": [
|
||||
"appctl",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -134,10 +134,10 @@
|
||||
{
|
||||
"name": "login / logout",
|
||||
"synopsis": [
|
||||
"login [--new] passphrase...",
|
||||
"login [--new]",
|
||||
"logout [--save]"
|
||||
],
|
||||
"description": "Identity unlock/register and session teardown; require booter hooks."
|
||||
"description": "login is a /bin utility: run it with no passphrase on the command line, then type the passphrase at the TTY prompt (hidden). logout remains a shell builtin for session teardown; both require booter hooks."
|
||||
},
|
||||
{
|
||||
"name": ":",
|
||||
|
||||
@@ -1,99 +1,15 @@
|
||||
{
|
||||
"name": "baresay",
|
||||
"section": 1,
|
||||
"title": "print an ASCII animal with a speech balloon",
|
||||
"title": "baresay",
|
||||
"synopsis": [
|
||||
"baresay [OPTION]... [MESSAGE...]"
|
||||
],
|
||||
"description": "Like cowsay for Bare OS: wraps MESSAGE (or stdin, if none) in a speech balloon and prints it above an ASCII character read from /share/baresay/<name>.bare. Default character is bear. Full behavior is in packages/bare-os-coreutils/src/baresay.js and lib/baresay-engine.js.",
|
||||
"files": [
|
||||
"/share/baresay/*.bare — ASCII templates with $thoughts, $eyes, $L (left eye), $R (right eye), $tongue"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"flag": "-W COLS",
|
||||
"meaning": "Maximum width for word wrapping balloon text (default 40)."
|
||||
},
|
||||
{
|
||||
"flag": "-n",
|
||||
"meaning": "Do not wrap words; single balloon line."
|
||||
},
|
||||
{
|
||||
"flag": "-f NAME|-f PATH",
|
||||
"meaning": "Character: NAME loads /share/baresay/NAME.bare; PATH loads that file."
|
||||
},
|
||||
{
|
||||
"flag": "--file NAME|PATH",
|
||||
"meaning": "Same as -f."
|
||||
},
|
||||
{
|
||||
"flag": "-l",
|
||||
"meaning": "List names (basename of each .bare under /share/baresay)."
|
||||
},
|
||||
{
|
||||
"flag": "-e XX",
|
||||
"meaning": "Two characters for eyes (default oo)."
|
||||
},
|
||||
{
|
||||
"flag": "-T C",
|
||||
"meaning": "One character for tongue."
|
||||
},
|
||||
{
|
||||
"flag": "-b -d -g -p -s -t -w -y",
|
||||
"meaning": "Preset moods (Borg, dead, greedy, paranoid, stoned, tired, wired, youthful)."
|
||||
},
|
||||
{
|
||||
"flag": "--think",
|
||||
"meaning": "Thought balloon with parentheses instead of angle brackets."
|
||||
},
|
||||
{
|
||||
"flag": "-th",
|
||||
"meaning": "Same as --think."
|
||||
},
|
||||
{
|
||||
"flag": "--rainbow",
|
||||
"meaning": "ANSI color cycling on balloon lines."
|
||||
},
|
||||
{
|
||||
"flag": "-R",
|
||||
"meaning": "Pick a random .bare from /share/baresay."
|
||||
},
|
||||
{
|
||||
"flag": "--random",
|
||||
"meaning": "Same as -R."
|
||||
},
|
||||
{
|
||||
"flag": "-h",
|
||||
"meaning": "Print usage and exit."
|
||||
},
|
||||
{
|
||||
"flag": "--help",
|
||||
"meaning": "Same as -h."
|
||||
}
|
||||
"baresay [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of baresay. Full behavior is defined in packages/bare-os-coreutils/src/baresay.js.",
|
||||
"options": [],
|
||||
"keywords": [
|
||||
"baresay",
|
||||
"cowsay",
|
||||
"ascii",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
],
|
||||
"examples": [
|
||||
{
|
||||
"caption": "Speech from arguments",
|
||||
"code": "baresay Hello from Bare OS"
|
||||
},
|
||||
{
|
||||
"caption": "Pipe text",
|
||||
"code": "echo hi | baresay"
|
||||
},
|
||||
{
|
||||
"caption": "Another character",
|
||||
"code": "baresay -f pear Pear-powered"
|
||||
},
|
||||
{
|
||||
"caption": "Thought bubble",
|
||||
"code": "baresay --think Quiet thoughts"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,29 +1,15 @@
|
||||
{
|
||||
"name": "chat",
|
||||
"section": 1,
|
||||
"title": "swarm text chat (bare-os-chat-v1)",
|
||||
"title": "chat",
|
||||
"synopsis": [
|
||||
"chat",
|
||||
"chat send TEXT",
|
||||
"chat who",
|
||||
"chat history [N]"
|
||||
"chat [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "On a real TTY, opens a full-screen terminal UI (alternate buffer, like `edit`/`nano`): live transcript with subscribe + ring-buffer history, multi-line capable peer strip from `/proc/bare_os/chat.json` (swarm peer count, rx counters), single-line composer with arrow/Home/End editing, scroll with arrow keys or PgUp/PgDn, `^R` refresh metrics, `?` help, `^Q`/`^X`/Ctrl+C exit. Uses Protomux `bare-os-chat-v1` on existing Hyperswarm connections. Stock hosts pair chat by default; disable with `BARE_OS_PROTOMUX_CHAT_CHANNEL=0`. Scripting: `chat send`, `who`, `history` avoid the TUI.",
|
||||
"description": "Bare OS implementation of chat. Full behavior is defined in packages/bare-os-coreutils/src/chat.js.",
|
||||
"options": [],
|
||||
"keywords": ["chat", "swarm", "bare-os", "tui"],
|
||||
"examples": [
|
||||
{
|
||||
"caption": "full-screen chat TUI",
|
||||
"code": "chat"
|
||||
},
|
||||
{
|
||||
"caption": "single line (no TTY)",
|
||||
"code": "chat send hello swarm"
|
||||
}
|
||||
],
|
||||
"seeAlso": [
|
||||
{ "name": "agent", "section": 1 },
|
||||
{ "name": "bare-os-shell", "section": 7 },
|
||||
{ "name": "handbook-03-protocol-and-disk", "section": 7 }
|
||||
"keywords": [
|
||||
"chat",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,28 +1,15 @@
|
||||
{
|
||||
"name": "corestorectl",
|
||||
"section": 1,
|
||||
"title": "Corestore and Hyperdrive control utility",
|
||||
"title": "corestorectl",
|
||||
"synopsis": [
|
||||
"corestorectl status",
|
||||
"corestorectl namespaces",
|
||||
"corestorectl mounts",
|
||||
"corestorectl replication"
|
||||
"corestorectl [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Inspects Corestore namespaces, Hyperdrive mount hints, and replication health via /proc/bare_os snapshots.",
|
||||
"description": "Bare OS implementation of corestorectl. Full behavior is defined in packages/bare-os-coreutils/src/corestorectl.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "corestore", "hyperdrive", "storage"],
|
||||
"examples": [
|
||||
{
|
||||
"caption": "inspect replication state",
|
||||
"code": "corestorectl replication"
|
||||
},
|
||||
{
|
||||
"caption": "list mounted namespaces",
|
||||
"code": "corestorectl namespaces"
|
||||
},
|
||||
{
|
||||
"caption": "view mount hints",
|
||||
"code": "corestorectl mounts"
|
||||
}
|
||||
"keywords": [
|
||||
"corestorectl",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
{
|
||||
"name": "ctxbaredoctor",
|
||||
"section": 1,
|
||||
"title": "ctx.bare module-resolution diagnostics",
|
||||
"synopsis": ["ctxbaredoctor status", "ctxbaredoctor modules"],
|
||||
"description": "Reports ctx.bare runtime posture, including drive-bundle versus host-import behavior and manifest hints.",
|
||||
"title": "ctxbaredoctor",
|
||||
"synopsis": [
|
||||
"ctxbaredoctor [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of ctxbaredoctor. Full behavior is defined in packages/bare-os-coreutils/src/ctxbaredoctor.js.",
|
||||
"options": [],
|
||||
"keywords": ["ctx.bare", "bare", "modules", "diagnostics"]
|
||||
"keywords": [
|
||||
"ctxbaredoctor",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
{
|
||||
"name": "dhtctl",
|
||||
"section": 1,
|
||||
"title": "HyperDHT operator control utility",
|
||||
"title": "dhtctl",
|
||||
"synopsis": [
|
||||
"dhtctl status",
|
||||
"dhtctl lookup TOPIC",
|
||||
"dhtctl announce TOPIC",
|
||||
"dhtctl connect PEER_KEY",
|
||||
"dhtctl firewall allow|deny [PEER_KEY]",
|
||||
"dhtctl export-key"
|
||||
"dhtctl [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Reads /proc/bare_os DHT snapshots and sends control-plane envelopes for DHT operations.",
|
||||
"description": "Bare OS implementation of dhtctl. Full behavior is defined in packages/bare-os-coreutils/src/dhtctl.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "dht", "network", "operator"]
|
||||
"keywords": [
|
||||
"dhtctl",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
{
|
||||
"name": "dhtscan",
|
||||
"section": 1,
|
||||
"title": "dht scan summary",
|
||||
"synopsis": ["dhtscan"],
|
||||
"description": "Reads and prints compact DHT diagnostics from /proc/bare_os/dht_scan.json.",
|
||||
"title": "dhtscan",
|
||||
"synopsis": [
|
||||
"dhtscan [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of dhtscan. Full behavior is defined in packages/bare-os-coreutils/src/dhtscan.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "dht", "diagnostics"]
|
||||
"keywords": [
|
||||
"dhtscan",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
{
|
||||
"name": "dhttop",
|
||||
"section": 1,
|
||||
"title": "live dht tui",
|
||||
"synopsis": ["dhttop"],
|
||||
"description": "TTY dashboard for DHT status and bootstrap/firewall hints.",
|
||||
"title": "dhttop",
|
||||
"synopsis": [
|
||||
"dhttop [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of dhttop. Full behavior is defined in packages/bare-os-coreutils/src/dhttop.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "dht", "tui"]
|
||||
"keywords": [
|
||||
"dhttop",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
{
|
||||
"name": "holepunch-view",
|
||||
"section": 1,
|
||||
"title": "holepunch and nat tui",
|
||||
"synopsis": ["holepunch-view"],
|
||||
"description": "Displays NAT and holepunch summary telemetry for the current swarm session.",
|
||||
"title": "holepunch-view",
|
||||
"synopsis": [
|
||||
"holepunch-view [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of holepunch-view. Full behavior is defined in packages/bare-os-coreutils/src/holepunch-view.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "nat", "holepunch", "tui"]
|
||||
"keywords": [
|
||||
"holepunch-view",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"synopsis": [
|
||||
"holesail [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Operator CLI for managed Holesail tunnels. Persisted connections live in BARE_OS_HOLESAIL_STATE (default ~/.holesail/state.json): each server row stores seed (stable 64-hex or z32 ctor secret) and key (full hs:// URL after ready). Stock rows bare-www-<port> and bare-ssh-<port> are merged by bare-os-www-holesail.js and bare-os-ssh-holesail.js. See handbook ch.4 bare-holesail and packages/bare-os-booter/lib/bare-holesail-managed.js. Dispatch implementation: packages/bare-os-coreutils/src/holesail.js.",
|
||||
"description": "Bare OS implementation of holesail. Full behavior is defined in packages/bare-os-coreutils/src/holesail.js.",
|
||||
"options": [],
|
||||
"keywords": [
|
||||
"holesail",
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
{
|
||||
"name": "hypershell-board",
|
||||
"section": 1,
|
||||
"title": "p2p remote-session board",
|
||||
"title": "hypershell-board",
|
||||
"synopsis": [
|
||||
"hypershell-board offer-shell LABEL",
|
||||
"hypershell-board offer-copy PATH",
|
||||
"hypershell-board claim SESSION_ID",
|
||||
"hypershell-board close SESSION_ID",
|
||||
"hypershell-board list"
|
||||
"hypershell-board [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Publishes and tracks hypershell-style session intents in a p2p event stream (`session.offer`, `session.claim`, `session.close`). This command provides coordination and audit visibility while transport/execution policy remains host-controlled.",
|
||||
"description": "Bare OS implementation of hypershell-board. Full behavior is defined in packages/bare-os-coreutils/src/hypershell-board.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "hypershell", "remote-shell", "sessions"],
|
||||
"examples": [
|
||||
{ "caption": "offer a shell session", "code": "hypershell-board offer-shell ops-maintenance" },
|
||||
{ "caption": "claim an offered session", "code": "hypershell-board claim session-abc123" }
|
||||
],
|
||||
"seeAlso": [
|
||||
{ "name": "chat", "section": 1 },
|
||||
{ "name": "telnet", "section": 1 }
|
||||
"keywords": [
|
||||
"hypershell-board",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"synopsis": [
|
||||
"kernel-doctor [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Prints a non-secret guest snapshot. With --json, includes /proc JSON (e.g. resources.pipeline: effective maxBytes/maxLines plus absCapBytes/absCapLines ceilings, baseMax*, streamingEnabled) and envHints for select BARE_OS_* keys (including pipeline tunables). See packages/bare-os-coreutils/src/kernel-doctor.js.",
|
||||
"description": "Bare OS implementation of kernel-doctor. Full behavior is defined in packages/bare-os-coreutils/src/kernel-doctor.js.",
|
||||
"options": [],
|
||||
"keywords": [
|
||||
"kernel-doctor",
|
||||
|
||||
@@ -5,19 +5,11 @@
|
||||
"synopsis": [
|
||||
"kill [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Send a signal to synthetic processes. Targets may be numeric PIDs, kernel/booter/shell names, negative values for logical process groups, or job specs when the interactive shell has background jobs: %n (job id n) or %% (current job), resolved to synthetic PIDs 4100+job.id for ctx.bareOsSendSignal. See handbook ch.9 and bare-os-process-table jobControlSemantics.",
|
||||
"description": "Bare OS implementation of kill. Full behavior is defined in packages/bare-os-coreutils/src/kill.js.",
|
||||
"options": [],
|
||||
"keywords": [
|
||||
"kill",
|
||||
"signal",
|
||||
"job",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
],
|
||||
"seeAlso": [
|
||||
{
|
||||
"name": "bare-os-shell",
|
||||
"section": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "locale",
|
||||
"section": 1,
|
||||
"title": "locale",
|
||||
"synopsis": [
|
||||
"locale [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of locale. Full behavior is defined in packages/bare-os-coreutils/src/locale.js.",
|
||||
"options": [],
|
||||
"keywords": [
|
||||
"locale",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
@@ -2,27 +2,29 @@
|
||||
"name": "login",
|
||||
"section": 1,
|
||||
"title": "begin a session on the system",
|
||||
"synopsis": ["login [--new]", "login [--help]"],
|
||||
"description": "Unlock or register your identity. Run login (optionally with --new) with no passphrase on the command line; then type your passphrase at the prompt. Input uses the same TTY stream path as agent --config (hidden echo). A passphrase may be several words. Giving a passphrase as shell arguments is rejected so it does not appear in history.",
|
||||
"options": [
|
||||
"synopsis": [
|
||||
"login [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "When invoked from /bin, behavior aligns with session identity hooks (see booter). Prefer the shell builtin for passphrase entry.",
|
||||
"options": [],
|
||||
"keywords": [
|
||||
"login",
|
||||
"identity",
|
||||
"passphrase"
|
||||
],
|
||||
"seeAlso": [
|
||||
{
|
||||
"flag": "--new",
|
||||
"meaning": "register a new identity instead of unlocking"
|
||||
},
|
||||
{
|
||||
"flag": "-h, --help",
|
||||
"meaning": "show usage"
|
||||
"name": "logout",
|
||||
"section": 1
|
||||
}
|
||||
],
|
||||
"keywords": ["login", "identity", "passphrase"],
|
||||
"seeAlso": [{ "name": "logout", "section": 1 }],
|
||||
"examples": [
|
||||
{
|
||||
"caption": "unlock: run the command, then type passphrase at prompt (multi-word allowed)",
|
||||
"caption": "unlock: run login, then type passphrase at prompt (multi-word ok)",
|
||||
"code": "login"
|
||||
},
|
||||
{
|
||||
"caption": "register a new identity",
|
||||
"caption": "register new identity",
|
||||
"code": "login --new"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,25 +1,15 @@
|
||||
{
|
||||
"name": "meshdrop",
|
||||
"section": 1,
|
||||
"title": "p2p file inbox and outbox",
|
||||
"title": "meshdrop",
|
||||
"synopsis": [
|
||||
"meshdrop offer FILE [--to PEER_HINT]",
|
||||
"meshdrop send-next OFFER_ID [MAX_CHUNKS]",
|
||||
"meshdrop inbox [N]",
|
||||
"meshdrop accept OFFER_ID",
|
||||
"meshdrop fetch OFFER_ID [DEST]"
|
||||
"meshdrop [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Transfers files between booted Bare OS peers using a chunked `bare-p2p-v1` meshdrop protocol carried over the existing p2p chat transport. Transfer lifecycle uses offer/accept/chunk/progress/cancel/complete events with local queue state under `~/.bare/p2p-suite/meshdrop.json`. Host bridge hooks can consume emitted mirror-drive hints.",
|
||||
"description": "Bare OS implementation of meshdrop. Full behavior is defined in packages/bare-os-coreutils/src/meshdrop.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "files", "meshdrop", "swarm"],
|
||||
"examples": [
|
||||
{ "caption": "offer a file to peers", "code": "meshdrop offer ./report.txt" },
|
||||
{ "caption": "send more queued chunks", "code": "meshdrop send-next offer-abc123 16" },
|
||||
{ "caption": "accept then inspect transfer progress", "code": "meshdrop accept offer-abc123 && meshdrop status" },
|
||||
{ "caption": "save an accepted transfer", "code": "meshdrop fetch offer-abc123 ./received-report.txt" }
|
||||
],
|
||||
"seeAlso": [
|
||||
{ "name": "chat", "section": 1 },
|
||||
{ "name": "tar", "section": 1 }
|
||||
"keywords": [
|
||||
"meshdrop",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
{
|
||||
"name": "p2ping",
|
||||
"section": 1,
|
||||
"title": "p2p ping probe",
|
||||
"synopsis": ["p2ping [LABEL]"],
|
||||
"description": "Emits a p2p ping event and prints local latency and swarm peer hints.",
|
||||
"title": "p2ping",
|
||||
"synopsis": [
|
||||
"p2ping [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of p2ping. Full behavior is defined in packages/bare-os-coreutils/src/p2ping.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "ping", "latency", "swarm"]
|
||||
"keywords": [
|
||||
"p2ping",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
{
|
||||
"name": "p2ptrace",
|
||||
"section": 1,
|
||||
"title": "p2p trace collector",
|
||||
"synopsis": ["p2ptrace [APP] [N]"],
|
||||
"description": "Dumps recent p2p events as JSON lines for tracing and diagnostics.",
|
||||
"title": "p2ptrace",
|
||||
"synopsis": [
|
||||
"p2ptrace [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of p2ptrace. Full behavior is defined in packages/bare-os-coreutils/src/p2ptrace.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "trace", "json"]
|
||||
"keywords": [
|
||||
"p2ptrace",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -11,15 +11,5 @@
|
||||
"pathcap-verify",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
],
|
||||
"examples": [
|
||||
{
|
||||
"caption": "verify a capability envelope",
|
||||
"code": "pathcap-verify ~/.config/bare-os/pathcaps/release.json"
|
||||
},
|
||||
{
|
||||
"caption": "verify and print machine-readable output",
|
||||
"code": "pathcap-verify --json ~/.config/bare-os/pathcaps/release.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
{
|
||||
"name": "peerctl",
|
||||
"section": 1,
|
||||
"title": "peer control plane utility",
|
||||
"synopsis": ["peerctl health", "peerctl request ACTION [JSON_PAYLOAD]", "peerctl history [N]"],
|
||||
"description": "P2P control helper for health checks and request envelopes.",
|
||||
"title": "peerctl",
|
||||
"synopsis": [
|
||||
"peerctl [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of peerctl. Full behavior is defined in packages/bare-os-coreutils/src/peerctl.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "control", "rpc"]
|
||||
"keywords": [
|
||||
"peerctl",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
{
|
||||
"name": "peerdiscover",
|
||||
"section": 1,
|
||||
"title": "peer service discovery utility",
|
||||
"synopsis": ["peerdiscover announce SERVICE [META]", "peerdiscover list [N]"],
|
||||
"description": "Announces and lists peer service metadata over p2p event history.",
|
||||
"title": "peerdiscover",
|
||||
"synopsis": [
|
||||
"peerdiscover [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of peerdiscover. Full behavior is defined in packages/bare-os-coreutils/src/peerdiscover.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "discovery", "service"]
|
||||
"keywords": [
|
||||
"peerdiscover",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
{
|
||||
"name": "peernote",
|
||||
"section": 1,
|
||||
"title": "shared p2p notes feed",
|
||||
"title": "peernote",
|
||||
"synopsis": [
|
||||
"peernote add TEXT",
|
||||
"peernote list [N]"
|
||||
"peernote [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Writes and reads short collaborative notes over the p2p swarm by using `bare-p2p-v1` envelopes carried on the existing chat transport. Useful for operational breadcrumbs across booted systems.",
|
||||
"description": "Bare OS implementation of peernote. Full behavior is defined in packages/bare-os-coreutils/src/peernote.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "notes", "chat", "ops"],
|
||||
"examples": [
|
||||
{ "caption": "add a note", "code": "peernote add rebooted edge node 3 with new profile" },
|
||||
{ "caption": "list recent notes", "code": "peernote list 20" }
|
||||
],
|
||||
"seeAlso": [
|
||||
{ "name": "taskmesh", "section": 1 },
|
||||
{ "name": "chat", "section": 1 }
|
||||
"keywords": [
|
||||
"peernote",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -11,15 +11,5 @@
|
||||
"pkg-swarm-index",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
],
|
||||
"examples": [
|
||||
{
|
||||
"caption": "show package swarm index",
|
||||
"code": "pkg-swarm-index"
|
||||
},
|
||||
{
|
||||
"caption": "emit JSON for automation",
|
||||
"code": "pkg-swarm-index --json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
{
|
||||
"name": "routeview",
|
||||
"section": 1,
|
||||
"title": "route visibility tui",
|
||||
"synopsis": ["routeview"],
|
||||
"description": "Shows direct-vs-relay route summary from /proc/bare_os/route_summary.json.",
|
||||
"title": "routeview",
|
||||
"synopsis": [
|
||||
"routeview [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of routeview. Full behavior is defined in packages/bare-os-coreutils/src/routeview.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "routing", "relay", "tui"]
|
||||
"keywords": [
|
||||
"routeview",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
{
|
||||
"name": "say",
|
||||
"section": 1,
|
||||
"title": "alias for baresay",
|
||||
"title": "say",
|
||||
"synopsis": [
|
||||
"say [OPTION]... [MESSAGE...]"
|
||||
"say [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Same program as baresay (packages/bare-os-coreutils/src/baresay.js); only the default argv[0] label in messages differs. See man baresay for options and /share/baresay/*.bare.",
|
||||
"description": "Bare OS implementation of say. Full behavior is defined in packages/bare-os-coreutils/src/say.js.",
|
||||
"options": [],
|
||||
"keywords": [
|
||||
"say",
|
||||
"baresay",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
],
|
||||
"examples": [
|
||||
{
|
||||
"caption": "short command name",
|
||||
"code": "say hello world"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,13 +5,8 @@
|
||||
"synopsis": [
|
||||
"sh [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Runs shell commands via ctx.execLine (same language as the interactive booter shell). Supports command-string mode (`-c`) and script-file mode (`sh SCRIPT`). This is not the interactive REPL; see man bare-os-shell for session builtins, pipelines, and jobs.",
|
||||
"options": [
|
||||
{
|
||||
"flag": "-c COMMAND [NAME [ARG ...]]",
|
||||
"meaning": "Execute command string and set positional parameters for that command context."
|
||||
}
|
||||
],
|
||||
"description": "Bare OS implementation of sh. Full behavior is defined in packages/bare-os-coreutils/src/sh.js.",
|
||||
"options": [],
|
||||
"keywords": [
|
||||
"sh",
|
||||
"bare-os",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"synopsis": [
|
||||
"sha224sum [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of sha224sum. Computes SHA-224 via a bundled digest (many runtimes omit SHA-224 in crypto.subtle.digest). Full behavior is defined in packages/bare-os-coreutils/src/sha224sum.js.",
|
||||
"description": "Bare OS implementation of sha224sum. Full behavior is defined in packages/bare-os-coreutils/src/sha224sum.js.",
|
||||
"options": [],
|
||||
"keywords": [
|
||||
"sha224sum",
|
||||
|
||||
@@ -6,12 +6,7 @@
|
||||
"sort [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of sort lines. Full behavior is defined in packages/bare-os-coreutils/src/sort.js.",
|
||||
"options": [
|
||||
{
|
||||
"flag": "-M, --month-sort",
|
||||
"meaning": "Unsupported in Bare OS sort (returns an invalid option error)."
|
||||
}
|
||||
],
|
||||
"options": [],
|
||||
"keywords": [
|
||||
"sort",
|
||||
"bare-os",
|
||||
@@ -29,10 +24,6 @@
|
||||
{
|
||||
"caption": "unique",
|
||||
"code": "sort -u tags.txt"
|
||||
},
|
||||
{
|
||||
"caption": "month sort status",
|
||||
"code": "sort -M dates.txt # unsupported in this implementation"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
{
|
||||
"name": "swarmdoctor",
|
||||
"section": 1,
|
||||
"title": "swarm diagnostic doctor",
|
||||
"synopsis": ["swarmdoctor"],
|
||||
"description": "Reads /proc/bare_os/swarm_doctor.json and reports swarm health/remediation.",
|
||||
"title": "swarmdoctor",
|
||||
"synopsis": [
|
||||
"swarmdoctor [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of swarmdoctor. Full behavior is defined in packages/bare-os-coreutils/src/swarmdoctor.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "swarm", "doctor"]
|
||||
"keywords": [
|
||||
"swarmdoctor",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
{
|
||||
"name": "swarmmap",
|
||||
"section": 1,
|
||||
"title": "swarm topology map tui",
|
||||
"synopsis": ["swarmmap"],
|
||||
"description": "Live peer topology viewer for swarm peers and states.",
|
||||
"title": "swarmmap",
|
||||
"synopsis": [
|
||||
"swarmmap [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of swarmmap. Full behavior is defined in packages/bare-os-coreutils/src/swarmmap.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "swarm", "tui", "topology"]
|
||||
"keywords": [
|
||||
"swarmmap",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
{
|
||||
"name": "swarmtop",
|
||||
"section": 1,
|
||||
"title": "p2p fleet dashboard",
|
||||
"title": "swarmtop",
|
||||
"synopsis": [
|
||||
"swarmtop",
|
||||
"swarmtop watch",
|
||||
"swarmtop events [N]",
|
||||
"swarmtop ping [LABEL]"
|
||||
"swarmtop [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Shows a fleet-oriented p2p dashboard for booted Bare OS peers. It combines `/proc/bare_os/swarm` snapshots with `bare-p2p-v1` envelopes transported over the existing swarm chat channel. On a TTY with no subcommand it opens a lightweight full-screen view.",
|
||||
"description": "Bare OS implementation of swarmtop. Full behavior is defined in packages/bare-os-coreutils/src/swarmtop.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "swarm", "dashboard", "tui"],
|
||||
"examples": [
|
||||
{ "caption": "open full-screen dashboard", "code": "swarmtop" },
|
||||
{ "caption": "emit a fleet ping event", "code": "swarmtop ping maintenance-window" }
|
||||
],
|
||||
"seeAlso": [
|
||||
{ "name": "chat", "section": 1 },
|
||||
{ "name": "baretop", "section": 1 }
|
||||
"keywords": [
|
||||
"swarmtop",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
{
|
||||
"name": "taskmesh",
|
||||
"section": 1,
|
||||
"title": "shared p2p task board",
|
||||
"title": "taskmesh",
|
||||
"synopsis": [
|
||||
"taskmesh add TEXT",
|
||||
"taskmesh done TASK_ID",
|
||||
"taskmesh list [open|all]"
|
||||
"taskmesh [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Maintains a swarm-shared append-only task stream using `bare-p2p-v1` envelopes. The local task board is reduced from event history (`task.add`, `task.done`) so multiple booted peers can converge without central coordination.",
|
||||
"description": "Bare OS implementation of taskmesh. Full behavior is defined in packages/bare-os-coreutils/src/taskmesh.js.",
|
||||
"options": [],
|
||||
"keywords": ["p2p", "tasks", "kanban", "events"],
|
||||
"examples": [
|
||||
{ "caption": "create a shared task", "code": "taskmesh add rotate bootstrap relays" },
|
||||
{ "caption": "show open tasks", "code": "taskmesh list" }
|
||||
],
|
||||
"seeAlso": [
|
||||
{ "name": "peernote", "section": 1 },
|
||||
{ "name": "chat", "section": 1 }
|
||||
"keywords": [
|
||||
"taskmesh",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,52 +3,12 @@
|
||||
"section": 1,
|
||||
"title": "telnet",
|
||||
"synopsis": [
|
||||
"telnet [--ipv4|--ipv6] [--connect-timeout-ms N] [--timeout-ms N] [--keepalive-ms N] [--reconnect N] [--reconnect-delay-ms N] [--naws-cols N] [--naws-rows N] [--ttype NAME] host [port]"
|
||||
],
|
||||
"description": "Interactive TELNET client for Bare OS coreutils. Supports RFC-style option negotiation (WILL/WONT/DO/DONT), subnegotiation for NAWS and terminal type, escape-command mode, and reconnect/timeout controls.",
|
||||
"options": [
|
||||
{
|
||||
"flag": "--ipv4",
|
||||
"meaning": "Prefer IPv4 transport."
|
||||
},
|
||||
{
|
||||
"flag": "--ipv6",
|
||||
"meaning": "Prefer IPv6 transport."
|
||||
},
|
||||
{
|
||||
"flag": "--connect-timeout-ms N",
|
||||
"meaning": "Connection timeout in milliseconds."
|
||||
},
|
||||
{
|
||||
"flag": "--timeout-ms N",
|
||||
"meaning": "Session idle timeout in milliseconds (0 disables)."
|
||||
},
|
||||
{
|
||||
"flag": "--keepalive-ms N",
|
||||
"meaning": "Send keepalive pacing in milliseconds (0 disables)."
|
||||
},
|
||||
{
|
||||
"flag": "--reconnect N",
|
||||
"meaning": "Reconnect attempts after disconnect/error."
|
||||
},
|
||||
{
|
||||
"flag": "--reconnect-delay-ms N",
|
||||
"meaning": "Delay between reconnect attempts in milliseconds."
|
||||
},
|
||||
{
|
||||
"flag": "--naws-cols N / --naws-rows N",
|
||||
"meaning": "Advertise terminal dimensions via NAWS."
|
||||
},
|
||||
{
|
||||
"flag": "--ttype NAME",
|
||||
"meaning": "Terminal type sent to server when TTYPE SEND is requested."
|
||||
}
|
||||
"telnet [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of telnet. Full behavior is defined in packages/bare-os-coreutils/src/telnet.js.",
|
||||
"options": [],
|
||||
"keywords": [
|
||||
"telnet",
|
||||
"network",
|
||||
"tty",
|
||||
"terminal",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "trustctl",
|
||||
"section": 1,
|
||||
"title": "trust root and signer policy utility",
|
||||
"title": "trustctl",
|
||||
"synopsis": [
|
||||
"trustctl status",
|
||||
"trustctl inspect TARGET",
|
||||
"trustctl pin add KEY",
|
||||
"trustctl pin rm KEY",
|
||||
"trustctl policy show"
|
||||
"trustctl [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Manages local trust policy (signer pins, relay posture, encryption defaults) and explains acceptance decisions.",
|
||||
"description": "Bare OS implementation of trustctl. Full behavior is defined in packages/bare-os-coreutils/src/trustctl.js.",
|
||||
"options": [],
|
||||
"keywords": ["trust", "security", "signing", "policy"]
|
||||
"keywords": [
|
||||
"trustctl",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,23 +3,12 @@
|
||||
"section": 1,
|
||||
"title": "whois",
|
||||
"synopsis": [
|
||||
"whois [--json] TARGET"
|
||||
],
|
||||
"description": "Query RDAP registration data for domains, IP addresses, and ASNs.",
|
||||
"options": [
|
||||
{
|
||||
"flag": "--json",
|
||||
"meaning": "Print raw RDAP JSON instead of a human-readable summary."
|
||||
},
|
||||
{
|
||||
"flag": "-h, --help",
|
||||
"meaning": "Show usage information."
|
||||
}
|
||||
"whois [OPTION]... [OPERAND]..."
|
||||
],
|
||||
"description": "Bare OS implementation of whois. Full behavior is defined in packages/bare-os-coreutils/src/whois.js.",
|
||||
"options": [],
|
||||
"keywords": [
|
||||
"whois",
|
||||
"rdap",
|
||||
"network",
|
||||
"bare-os",
|
||||
"coreutils"
|
||||
]
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import test from 'brittle'
|
||||
import b4a from 'b4a'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
|
||||
|
||||
async function loadBin(name) {
|
||||
const runtime = await readFile(path.join(__dirname, '../lib/runtime.js'), 'utf8')
|
||||
const body = await readFile(path.join(__dirname, `../src/${name}.js`), 'utf8')
|
||||
return new AsyncFunction(
|
||||
'ctx',
|
||||
'argv',
|
||||
`${runtime}\n${body}\nif (typeof run === 'function') return await run(ctx, argv)\n`
|
||||
)
|
||||
}
|
||||
|
||||
function mkCtx(files = {}) {
|
||||
const logs = []
|
||||
const errs = []
|
||||
return {
|
||||
b4a,
|
||||
exitCode: 0,
|
||||
shellStdin: '',
|
||||
console: {
|
||||
log: (m) => logs.push(String(m)),
|
||||
error: (m) => errs.push(String(m))
|
||||
},
|
||||
vfs: {
|
||||
env: {},
|
||||
async readFile(p) {
|
||||
return Object.prototype.hasOwnProperty.call(files, p) ? files[p] : null
|
||||
}
|
||||
},
|
||||
_logs: logs,
|
||||
_errs: errs
|
||||
}
|
||||
}
|
||||
|
||||
test('locale -a prints available locales', async (t) => {
|
||||
const run = await loadBin('locale')
|
||||
const ctx = mkCtx()
|
||||
await run(ctx, ['locale', '-a'])
|
||||
t.is(ctx.exitCode, 0)
|
||||
t.ok(ctx._logs.join('\n').includes('C.UTF-8'))
|
||||
})
|
||||
|
||||
test('iconv -l lists supported encodings', async (t) => {
|
||||
const run = await loadBin('iconv')
|
||||
const ctx = mkCtx()
|
||||
await run(ctx, ['iconv', '-l'])
|
||||
t.is(ctx.exitCode, 0)
|
||||
t.ok(ctx._logs.join('\n').includes('utf-16le'))
|
||||
})
|
||||
|
||||
test('getconf PAGE_SIZE alias resolves to 4096', async (t) => {
|
||||
const run = await loadBin('getconf')
|
||||
const ctx = mkCtx()
|
||||
await run(ctx, ['getconf', 'PAGE_SIZE'])
|
||||
t.is(ctx.exitCode, 0)
|
||||
t.is(ctx._logs[0], '4096')
|
||||
})
|
||||
|
||||
test('ssh-keygen help subcommand prints usage', async (t) => {
|
||||
const run = await loadBin('ssh-keygen')
|
||||
const ctx = mkCtx()
|
||||
await run(ctx, ['ssh-keygen', 'help'])
|
||||
t.is(ctx.exitCode, 0)
|
||||
t.ok(ctx._logs.join('\n').includes('Usage: ssh-keygen'))
|
||||
})
|
||||
|
||||
test('oidc-publish shows help when --help is non-positional', async (t) => {
|
||||
const run = await loadBin('oidc-publish')
|
||||
const ctx = mkCtx()
|
||||
await run(ctx, ['oidc-publish', '--json', '--help'])
|
||||
t.is(ctx.exitCode, 0)
|
||||
t.ok(ctx._logs.join('\n').includes('oidc-publish'))
|
||||
})
|
||||
|
||||
test('procstat emits empty-table hint when no processes', async (t) => {
|
||||
const run = await loadBin('procstat')
|
||||
const table = b4a.from(JSON.stringify({ schema: 1, processes: [] }))
|
||||
const ctx = mkCtx({ '/proc/bare_os/process_table.json': table })
|
||||
await run(ctx, ['procstat'])
|
||||
t.is(ctx.exitCode, 0)
|
||||
const out = JSON.parse(ctx._logs.join('\n'))
|
||||
t.is(out.processCount, 0)
|
||||
t.ok(String(out.hint || '').includes('empty'))
|
||||
})
|
||||
|
||||
test('whois reports bounded timeout message', async (t) => {
|
||||
const run = await loadBin('whois')
|
||||
const ctx = mkCtx()
|
||||
ctx.vfs.env.BARE_OS_WHOIS_TIMEOUT_MS = '20'
|
||||
ctx.httpFetch = (_url, opts) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const sig = opts && opts.signal
|
||||
if (sig && typeof sig.addEventListener === 'function') {
|
||||
sig.addEventListener('abort', () => reject(new Error('AbortError')))
|
||||
}
|
||||
void resolve
|
||||
})
|
||||
await run(ctx, ['whois', 'google.com'])
|
||||
t.is(ctx.exitCode, 1)
|
||||
t.ok(ctx._errs.join('\n').includes('network timeout'))
|
||||
})
|
||||
@@ -91,7 +91,7 @@ test('telnet connects through ctx.bareOsTelnetConnect', async (t) => {
|
||||
return sock
|
||||
}
|
||||
}
|
||||
await run(ctx, ['telnet', 'localhost', '2323'])
|
||||
await run(ctx, ['telnet', '--no-strict-close', 'localhost', '2323'])
|
||||
t.is(connectCalls, 1)
|
||||
t.is(ctx.exitCode, 0)
|
||||
})
|
||||
@@ -125,7 +125,7 @@ test('telnet falls back to bareOsSyscall socket bridge', async (t) => {
|
||||
return { ok: false, code: 'ENOSYS' }
|
||||
}
|
||||
}
|
||||
await run(ctx, ['telnet', 'localhost', '2323'])
|
||||
await run(ctx, ['telnet', '--no-strict-close', 'localhost', '2323'])
|
||||
t.is(ctx.exitCode, 0)
|
||||
t.is(String(ctx.vfs.env.BARE_OS_POSIX_SOCKET_FD_BRIDGE), '1')
|
||||
})
|
||||
|
||||
@@ -106,7 +106,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)
|
||||
|
||||
@@ -133,6 +133,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',
|
||||
|
||||
@@ -87,7 +87,7 @@ function bareOsEmitRaw(ctx, chunk) {
|
||||
return false
|
||||
}
|
||||
|
||||
var BARE_OS_HELP_BIN_SPACED = "agent appctl arch awk baresay baretop base32 base64 basename basenc btop bundlebee cat chat chgrp chmod chown cksum clear cmp comm corestorectl cp crontab ctxbaredoctor curl cut date dd df dhtctl dhtscan dhttop diff dir dircolors dirname du echo edit env exit expand expr factor false find fmt fold getconf getfacl git git-pear grep groups hdms head help holepunch-view holesail hostid hostname hrpc hypershell-board iconv id install join journalctl jq kernel-boot-diff kernel-doctor kernel-explain kernel-fsck kernel-home-snapshot kernel-manifest-validate kernel-preflight kernel-triage kill link ln logger login logname logout ls man md5sum meshdrop mkdir mkfifo mktemp mount mv nano nice nl nohup nproc numfmt od oidc-publish openssl openssl p2ping p2ptrace paste patch pathcap-verify pathchk pear-runtime-matrix peerctl peerdiscover peernote pkg-swarm-index pr printenv printf procstat ps pwd readlink realpath rev rm rmdir routeview savevault say sed seq setfacl sh sha1sum sha224sum sha256sum sha384sum sha512sum shuf sidecar sleep sort split ssh-keygen ssh-keygen sshd sshd stat sum swarmdoctor swarmmap swarmtop sync systemctl tac tail tar tar taskmesh tee telnet test theme time timeout touch tr true truncate trustctl tsort tty ulimit umount uname unexpand uniq unlink uptime users vdir wc wget which who whoami whois xargs xattr yes"
|
||||
var BARE_OS_HELP_BIN_SPACED = "agent appctl arch awk baresay baretop base32 base64 basename basenc btop bundlebee cat chat chgrp chmod chown cksum clear cmp comm corestorectl cp crontab ctxbaredoctor curl cut date dd df dhtctl dhtscan dhttop diff dir dircolors dirname du echo edit env exit expand expr factor false find fmt fold getconf getfacl git git-pear grep groups hdms head help holepunch-view holesail hostid hostname hrpc hypershell-board iconv id install join journalctl jq kernel-boot-diff kernel-doctor kernel-explain kernel-fsck kernel-home-snapshot kernel-manifest-validate kernel-preflight kernel-triage kill link ln locale logger login logname logout ls man md5sum meshdrop mkdir mkfifo mktemp mount mv nano nice nl nohup nproc numfmt od oidc-publish openssl openssl p2ping p2ptrace paste patch pathcap-verify pathchk pear-runtime-matrix peerctl peerdiscover peernote pkg-swarm-index pr printenv printf procstat ps pwd readlink realpath rev rm rmdir routeview savevault say sed seq setfacl sh sha1sum sha224sum sha256sum sha384sum sha512sum shuf sidecar sleep sort split ssh-keygen ssh-keygen sshd sshd stat sum swarmdoctor swarmmap swarmtop sync systemctl tac tail tar tar taskmesh tee telnet test theme time timeout touch tr true truncate trustctl tsort tty ulimit umount uname unexpand uniq unlink uptime users vdir wc wget which who whoami whois xargs xattr yes"
|
||||
async function run(ctx, argv) {
|
||||
ctx.console.log(
|
||||
'Bare OS — default user: guest | shell builtins: alias, barerc, cd, command, export, exit, login, logout, readonly, type, umask, unalias, unset, : | /bin: ' +
|
||||
|
||||
@@ -152,12 +152,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) {
|
||||
@@ -187,15 +192,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,104 @@
|
||||
/* 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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -92,7 +92,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
|
||||
}
|
||||
|
||||
@@ -92,7 +92,8 @@ function bareOsEmitRaw(ctx, chunk) {
|
||||
* 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
|
||||
|
||||
@@ -109,12 +109,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,
|
||||
|
||||
@@ -91,16 +91,22 @@ function bareOsEmitRaw(ctx, chunk) {
|
||||
* 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(
|
||||
|
||||
@@ -108,7 +108,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'
|
||||
)
|
||||
@@ -365,6 +365,7 @@ function bareTelnetParseArgs(argv) {
|
||||
nawsCols: 80,
|
||||
nawsRows: 24,
|
||||
ttype: 'bare-os'
|
||||
strictClose: true
|
||||
}
|
||||
var pos = []
|
||||
for (var i = 0; i < rest.length; i++) {
|
||||
@@ -410,6 +411,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)
|
||||
}
|
||||
@@ -639,6 +648,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
|
||||
@@ -684,6 +695,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') {
|
||||
@@ -711,6 +723,7 @@ async function bareTelnetRunSession(ctx, host, port, opts) {
|
||||
}
|
||||
if (cmd === 'quit' || cmd === 'close') {
|
||||
ended = true
|
||||
userClosed = true
|
||||
try {
|
||||
socket.end()
|
||||
} catch {
|
||||
@@ -741,6 +754,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) {
|
||||
@@ -806,5 +823,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)
|
||||
}
|
||||
|
||||
@@ -110,12 +110,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')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,355 @@ function bareOsEmitRaw(ctx, chunk) {
|
||||
return false
|
||||
}
|
||||
|
||||
const BARE_P2P_SUITE_PREFIX = '[bare-p2p-v1]'
|
||||
const BARE_P2P_SUITE_MAX_HISTORY = 2000
|
||||
|
||||
function bareP2pNowMs() {
|
||||
return Date.now()
|
||||
}
|
||||
|
||||
function bareP2pId(prefix) {
|
||||
const rnd = Math.floor(Math.random() * 0x7fffffff)
|
||||
return prefix + '-' + bareP2pNowMs().toString(36) + '-' + rnd.toString(36)
|
||||
}
|
||||
|
||||
function bareP2pCommonExamples(argv0, rows) {
|
||||
const out = []
|
||||
for (const r of rows || []) out.push(' ' + argv0 + ' ' + r)
|
||||
return out.join('\n')
|
||||
}
|
||||
|
||||
function bareP2pHelpText(argv0, summary, usage, examples, seeAlso) {
|
||||
const lines = []
|
||||
lines.push('usage: ' + usage)
|
||||
lines.push(summary)
|
||||
lines.push('')
|
||||
lines.push('common options: --help --json --quiet --summary --timeout MS --no-color')
|
||||
if (examples && examples.length) {
|
||||
lines.push('')
|
||||
lines.push('examples:')
|
||||
lines.push(bareP2pCommonExamples(argv0, examples))
|
||||
}
|
||||
if (seeAlso && seeAlso.length) {
|
||||
lines.push('')
|
||||
lines.push('see also: ' + seeAlso.join(', '))
|
||||
}
|
||||
lines.push('')
|
||||
lines.push('troubleshooting: if peer data is empty, run `swarmtop` then `swarmdoctor`.')
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
function bareP2pParseCommonFlags(args) {
|
||||
const rest = []
|
||||
const opt = {
|
||||
help: false,
|
||||
json: false,
|
||||
quiet: false,
|
||||
summary: false,
|
||||
timeoutMs: 4000,
|
||||
noColor: false,
|
||||
dryRun: false,
|
||||
yes: false
|
||||
}
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const a = String(args[i] || '')
|
||||
if (a === '-h' || a === '--help') opt.help = true
|
||||
else if (a === '--json') opt.json = true
|
||||
else if (a === '--quiet') opt.quiet = true
|
||||
else if (a === '--summary') opt.summary = true
|
||||
else if (a === '--dry-run') opt.dryRun = true
|
||||
else if (a === '--yes' || a === '-y') opt.yes = true
|
||||
else if (a === '--no-color') opt.noColor = true
|
||||
else if (a === '--timeout') {
|
||||
const n = parseInt(args[i + 1] || '4000', 10)
|
||||
if (Number.isFinite(n)) opt.timeoutMs = Math.max(250, Math.min(120000, n))
|
||||
i++
|
||||
} else rest.push(a)
|
||||
}
|
||||
return { opt, rest }
|
||||
}
|
||||
|
||||
function bareP2pPrint(ctx, data, opt) {
|
||||
if (opt && opt.quiet) return
|
||||
if (opt && (opt.json || typeof data !== 'string')) {
|
||||
ctx.console.log(typeof data === 'string' ? JSON.stringify({ message: data }) : JSON.stringify(data, null, 2))
|
||||
return
|
||||
}
|
||||
ctx.console.log(String(data))
|
||||
}
|
||||
|
||||
function bareP2pError(ctx, argv0, msg, next, opt) {
|
||||
if (opt && opt.quiet) {
|
||||
ctx.exitCode = 1
|
||||
return
|
||||
}
|
||||
ctx.console.error(argv0 + ': ' + msg + (next ? ' · try: ' + next : ''))
|
||||
ctx.exitCode = 1
|
||||
}
|
||||
|
||||
function bareP2pSuggestSubcommand(sub, known) {
|
||||
const s = String(sub || '')
|
||||
if (!s) return ''
|
||||
let best = ''
|
||||
let bestScore = 1e9
|
||||
for (const k of known || []) {
|
||||
const kk = String(k || '')
|
||||
const d = Math.abs(kk.length - s.length) + (kk[0] === s[0] ? 0 : 2)
|
||||
if (d < bestScore) {
|
||||
bestScore = d
|
||||
best = kk
|
||||
}
|
||||
}
|
||||
return bestScore <= 4 ? best : ''
|
||||
}
|
||||
|
||||
function bareP2pFirstRunHint(ctx, app, hint) {
|
||||
const env = ctx.env && typeof ctx.env === 'object' ? ctx.env : {}
|
||||
if (env.BARE_P2P_NO_HINTS === '1' || env.BARE_P2P_NO_HINTS === 'true') return
|
||||
const key = '__bare_p2p_hint_' + app
|
||||
if (ctx[key]) return
|
||||
ctx[key] = true
|
||||
try {
|
||||
ctx.console.log('hint: ' + hint)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
function bareP2pMaybeNext(ctx, opt, next) {
|
||||
if (opt && opt.quiet) return
|
||||
if (next) ctx.console.log('next: ' + next)
|
||||
}
|
||||
|
||||
function bareP2pHome(ctx) {
|
||||
const env = ctx.env && typeof ctx.env === 'object' ? ctx.env : {}
|
||||
const home = typeof env.HOME === 'string' && env.HOME ? env.HOME : '/home/guest'
|
||||
return home
|
||||
}
|
||||
|
||||
function bareP2pJoinPath(a, b) {
|
||||
if (!a.endsWith('/')) return a + '/' + b
|
||||
return a + b
|
||||
}
|
||||
|
||||
function bareP2pDataDir(ctx) {
|
||||
return bareP2pJoinPath(bareP2pHome(ctx), '.bare/p2p-suite')
|
||||
}
|
||||
|
||||
function bareP2pDataFile(ctx, app) {
|
||||
return bareP2pJoinPath(bareP2pDataDir(ctx), app + '.json')
|
||||
}
|
||||
|
||||
async function bareP2pReadJson(ctx, path) {
|
||||
try {
|
||||
if (!ctx.vfs || typeof ctx.vfs.readFile !== 'function') return null
|
||||
const b = await ctx.vfs.readFile(path)
|
||||
if (!b) return null
|
||||
const t = ctx.b4a.toString(b).trim()
|
||||
if (!t) return null
|
||||
return JSON.parse(t)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function bareP2pWriteJson(ctx, path, obj) {
|
||||
if (!ctx.vfs || typeof ctx.vfs.writeFile !== 'function') return false
|
||||
const body = JSON.stringify(obj, null, 2) + '\n'
|
||||
const b4 = ctx.b4a
|
||||
const buf =
|
||||
b4 && typeof b4.from === 'function'
|
||||
? b4.from(body)
|
||||
: new TextEncoder().encode(body)
|
||||
try {
|
||||
await ctx.vfs.writeFile(path, buf)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function bareP2pDecodeEnvelope(line) {
|
||||
const s = String(line || '')
|
||||
if (!s.startsWith(BARE_P2P_SUITE_PREFIX)) return null
|
||||
const payload = s.slice(BARE_P2P_SUITE_PREFIX.length).trim()
|
||||
if (!payload) return null
|
||||
try {
|
||||
const obj = JSON.parse(payload)
|
||||
if (!obj || typeof obj !== 'object') return null
|
||||
return obj
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function bareP2pEncodeEnvelope(app, kind, payload) {
|
||||
return (
|
||||
BARE_P2P_SUITE_PREFIX +
|
||||
' ' +
|
||||
JSON.stringify({
|
||||
schema: 1,
|
||||
suite: 'bare-p2p',
|
||||
version: '1',
|
||||
app,
|
||||
kind,
|
||||
tsMs: bareP2pNowMs(),
|
||||
payload: payload || {}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function bareP2pSend(ctx, app, kind, payload) {
|
||||
if (app === 'meshdrop' && typeof ctx.bareOsMeshdropSend === 'function') {
|
||||
return ctx.bareOsMeshdropSend({
|
||||
app,
|
||||
kind,
|
||||
payload: payload || {},
|
||||
tsMs: bareP2pNowMs()
|
||||
})
|
||||
}
|
||||
if (typeof ctx.bareOsChatSend !== 'function') {
|
||||
return { ok: false, reason: 'bareOsChatSend unavailable' }
|
||||
}
|
||||
return ctx.bareOsChatSend(bareP2pEncodeEnvelope(app, kind, payload))
|
||||
}
|
||||
|
||||
function bareP2pCollectFromHistory(ctx, app, limit) {
|
||||
if (app === 'meshdrop' && typeof ctx.bareOsMeshdropHistory === 'function') {
|
||||
let hist = []
|
||||
try {
|
||||
hist = ctx.bareOsMeshdropHistory(
|
||||
Math.max(1, Math.min(BARE_P2P_SUITE_MAX_HISTORY, limit || 512))
|
||||
)
|
||||
} catch {
|
||||
hist = []
|
||||
}
|
||||
if (!Array.isArray(hist)) return []
|
||||
const out = []
|
||||
for (const ev of hist) {
|
||||
if (!ev || typeof ev !== 'object') continue
|
||||
if (ev.app !== app) continue
|
||||
out.push({
|
||||
fromPeerKey: typeof ev.fromPeerKey === 'string' ? ev.fromPeerKey : '',
|
||||
displayName: typeof ev.sender === 'string' ? ev.sender : '',
|
||||
local: Boolean(ev.local),
|
||||
receivedAtMs:
|
||||
typeof ev.receivedAtMs === 'number' ? ev.receivedAtMs : bareP2pNowMs(),
|
||||
packet: {
|
||||
app,
|
||||
kind: typeof ev.kind === 'string' ? ev.kind : 'unknown',
|
||||
payload: ev.payload && typeof ev.payload === 'object' ? ev.payload : {}
|
||||
}
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
if (typeof ctx.bareOsChatHistory !== 'function') return []
|
||||
let hist = []
|
||||
try {
|
||||
hist = ctx.bareOsChatHistory(
|
||||
Math.max(1, Math.min(BARE_P2P_SUITE_MAX_HISTORY, limit || 512))
|
||||
)
|
||||
} catch {
|
||||
hist = []
|
||||
}
|
||||
if (!Array.isArray(hist)) return []
|
||||
const out = []
|
||||
for (const ev of hist) {
|
||||
if (!ev || typeof ev !== 'object') continue
|
||||
const body = String(ev.body || '')
|
||||
const decoded = bareP2pDecodeEnvelope(body)
|
||||
if (!decoded || decoded.app !== app) continue
|
||||
out.push({
|
||||
fromPeerKey: typeof ev.fromPeerKey === 'string' ? ev.fromPeerKey : '',
|
||||
displayName: typeof ev.displayName === 'string' ? ev.displayName : '',
|
||||
local: Boolean(ev.local),
|
||||
receivedAtMs:
|
||||
typeof ev.receivedAtMs === 'number' ? ev.receivedAtMs : bareP2pNowMs(),
|
||||
packet: decoded
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
function bareP2pSubscribe(ctx, app, fn) {
|
||||
if (app === 'meshdrop' && typeof ctx.bareOsMeshdropSubscribe === 'function') {
|
||||
return ctx.bareOsMeshdropSubscribe((ev) => {
|
||||
if (!ev || typeof ev !== 'object' || ev.app !== app) return
|
||||
fn({
|
||||
fromPeerKey: typeof ev.fromPeerKey === 'string' ? ev.fromPeerKey : '',
|
||||
displayName: typeof ev.sender === 'string' ? ev.sender : '',
|
||||
local: Boolean(ev.local),
|
||||
receivedAtMs:
|
||||
typeof ev.receivedAtMs === 'number' ? ev.receivedAtMs : bareP2pNowMs(),
|
||||
packet: {
|
||||
app,
|
||||
kind: typeof ev.kind === 'string' ? ev.kind : 'unknown',
|
||||
payload: ev.payload && typeof ev.payload === 'object' ? ev.payload : {}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
if (typeof ctx.bareOsChatSubscribe !== 'function') return () => {}
|
||||
return ctx.bareOsChatSubscribe((ev) => {
|
||||
if (!ev || typeof ev !== 'object') return
|
||||
const body = String(ev.body || '')
|
||||
const decoded = bareP2pDecodeEnvelope(body)
|
||||
if (!decoded || decoded.app !== app) return
|
||||
fn({
|
||||
fromPeerKey: typeof ev.fromPeerKey === 'string' ? ev.fromPeerKey : '',
|
||||
displayName: typeof ev.displayName === 'string' ? ev.displayName : '',
|
||||
local: Boolean(ev.local),
|
||||
receivedAtMs:
|
||||
typeof ev.receivedAtMs === 'number' ? ev.receivedAtMs : bareP2pNowMs(),
|
||||
packet: decoded
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function bareP2pReadSwarmSnapshot(ctx) {
|
||||
const out = {
|
||||
atMs: bareP2pNowMs(),
|
||||
peerCount: null,
|
||||
topicCount: null,
|
||||
peers: []
|
||||
}
|
||||
const snap = await bareP2pReadJson(ctx, '/proc/bare_os/swarm')
|
||||
if (!snap || typeof snap !== 'object') return out
|
||||
if (typeof snap.peerCount === 'number') out.peerCount = snap.peerCount
|
||||
if (typeof snap.topicCount === 'number') out.topicCount = snap.topicCount
|
||||
const peers = Array.isArray(snap.peers) ? snap.peers : []
|
||||
const detailsRaw = await bareP2pReadJson(ctx, '/proc/bare_os/peer_details.json')
|
||||
const details =
|
||||
detailsRaw && typeof detailsRaw === 'object' && Array.isArray(detailsRaw.peers)
|
||||
? detailsRaw.peers
|
||||
: []
|
||||
/** @type {Map<string, Record<string, unknown>>} */
|
||||
const byKey = new Map()
|
||||
for (const d of details) {
|
||||
if (!d || typeof d !== 'object') continue
|
||||
const rk = typeof d.remotePublicKey === 'string' ? d.remotePublicKey : ''
|
||||
const pid = typeof d.peerId === 'string' ? d.peerId : ''
|
||||
if (rk) byKey.set('k:' + rk, d)
|
||||
if (pid) byKey.set('p:' + pid, d)
|
||||
}
|
||||
const merged = []
|
||||
for (const p of peers) {
|
||||
if (!p || typeof p !== 'object') continue
|
||||
const rk = typeof p.remotePublicKey === 'string' ? p.remotePublicKey : ''
|
||||
const pid = typeof p.peerId === 'string' ? p.peerId : ''
|
||||
const d = (rk && byKey.get('k:' + rk)) || (pid && byKey.get('p:' + pid)) || null
|
||||
merged.push(d && typeof d === 'object' ? { ...p, ...d } : p)
|
||||
}
|
||||
out.peers = merged.slice(0, 128)
|
||||
return out
|
||||
}
|
||||
|
||||
async function bareP2pReadProcJson(ctx, path) {
|
||||
const v = await bareP2pReadJson(ctx, path)
|
||||
return v && typeof v === 'object' ? v : {}
|
||||
}
|
||||
|
||||
function bareOsTrustPolicyPath() {
|
||||
return '/etc/bare-os/trust.policy.json'
|
||||
}
|
||||
|
||||
@@ -164,8 +164,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)
|
||||
@@ -380,11 +387,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 = ''
|
||||
@@ -400,10 +419,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) {
|
||||
@@ -411,11 +434,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"schema": 2,
|
||||
"profileId": "bare-os-posix-like",
|
||||
"generatedAt": "2026-04-27T12:14:28.388Z",
|
||||
"generatedAt": "2026-04-27T12:21:33.106Z",
|
||||
"note": "Sparse POSIX Issue 7 coverage hints for /bin utilities. Omitted command names are not yet profiled here.",
|
||||
"commandIndex": [
|
||||
{
|
||||
@@ -312,6 +312,10 @@
|
||||
"name": "ln",
|
||||
"tier": "tier1_bin"
|
||||
},
|
||||
{
|
||||
"name": "locale",
|
||||
"tier": "tier1_bin"
|
||||
},
|
||||
{
|
||||
"name": "logger",
|
||||
"tier": "tier1_bin"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"atMs": 1777292068387,
|
||||
"atMs": 1777292493105,
|
||||
"commands": [
|
||||
"agent",
|
||||
"appctl",
|
||||
@@ -79,6 +79,7 @@
|
||||
"kill",
|
||||
"link",
|
||||
"ln",
|
||||
"locale",
|
||||
"logger",
|
||||
"login",
|
||||
"logname",
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user