TUI CONFIG

This commit is contained in:
Raven Scott
2026-04-22 02:51:45 -04:00
parent bc737094ca
commit 6ee7cfcb57
16 changed files with 690 additions and 170 deletions
+12 -4
View File
@@ -55,7 +55,9 @@ function bareAgentDefaultConfig() {
request_timeout_ms: 120000,
extra_headers: /** @type {Record<string, string>} */ ({}),
allow_delete: false,
require_confirm_token: ''
require_confirm_token: '',
owner_name: '',
agent_label: ''
}
}
@@ -87,7 +89,9 @@ function bareAgentMergeConfig(defaults, src) {
'request_timeout_ms',
'extra_headers',
'allow_delete',
'require_confirm_token'
'require_confirm_token',
'owner_name',
'agent_label'
])
for (const k of Object.keys(src)) {
if (k.startsWith('x-')) continue
@@ -101,7 +105,9 @@ function bareAgentMergeConfig(defaults, src) {
k === 'rest_base_url' ||
k === 'rest_api_key' ||
k === 'model' ||
k === 'provider'
k === 'provider' ||
k === 'owner_name' ||
k === 'agent_label'
) {
out[k] = String(val ?? '')
continue
@@ -149,7 +155,9 @@ function bareAgentValidateConfigShape(raw) {
'request_timeout_ms',
'extra_headers',
'allow_delete',
'require_confirm_token'
'require_confirm_token',
'owner_name',
'agent_label'
]
if (!known.includes(k)) {
throw new Error('unknown config key: ' + k)
@@ -185,7 +185,7 @@ function bareAgentToolDefinitions() {
patch: {
type: 'object',
description:
'Partial config object (rest_base_url, model, temperature, …)'
'Partial config object (rest_base_url, model, temperature, owner_name, agent_label, …)'
}
},
required: ['patch']
@@ -1316,7 +1316,9 @@ function bareAgentMergeConfigPatch(base, patch) {
'tool_parallelism',
'request_timeout_ms',
'allow_delete',
'require_confirm_token'
'require_confirm_token',
'owner_name',
'agent_label'
]
const numKeys = new Set([
'max_tokens',
+205 -42
View File
@@ -44,6 +44,91 @@ function bareAgentWriteOut(out, s) {
}
}
/**
* True when we can run the plain-text setup wizard on stdin/stdout (no REPL readline).
* @param {Record<string, unknown>} ctx
*/
function bareAgentCanPlainSetup(ctx) {
const stdin = /** @type {import('stream').Readable | undefined} */ (
ctx.replStdin || ctx.stdin
)
const stdout =
/** @type {import('stream').Writable | undefined} */ (
ctx.replStdout || ctx.stdout
)
const tty = /** @type {{ isTTY?: boolean }} */ (stdin)
return Boolean(
stdin &&
typeof stdin.on === 'function' &&
tty.isTTY &&
stdout &&
typeof stdout.write === 'function'
)
}
/**
* Read one line from a Readable stream (kernel echoes typed chars on cooked TTY).
* @param {import('stream').Readable} stdin
* @returns {Promise<string>}
*/
function bareAgentReadStreamLineOnce(stdin) {
return new Promise((resolve) => {
let acc = ''
/** @param {string | Uint8Array | Buffer} chunk */
function onData(chunk) {
let s = ''
if (typeof chunk === 'string') s = chunk
else if (chunk instanceof Uint8Array) s = new TextDecoder().decode(chunk)
else if (
typeof Buffer !== 'undefined' &&
typeof Buffer.isBuffer === 'function' &&
Buffer.isBuffer(chunk)
)
s = chunk.toString('utf8')
else s = String(chunk)
acc += s
const n = acc.indexOf('\n')
if (n >= 0) {
cleanup()
resolve(acc.slice(0, n).replace(/\r$/, ''))
}
}
function onEnd() {
cleanup()
resolve(acc.replace(/\r$/, ''))
}
function cleanup() {
stdin.removeListener('data', onData)
stdin.removeListener('end', onEnd)
stdin.removeListener('error', onEnd)
}
stdin.on('data', onData)
stdin.once('end', onEnd)
stdin.once('error', onEnd)
if (typeof stdin.resume === 'function') stdin.resume()
})
}
/**
* Write a prompt and read one line using raw streams (not ctx.readLine / TUI stack).
* @param {Record<string, unknown>} ctx
* @param {string} prompt
*/
async function bareAgentPromptSetupLine(ctx, prompt) {
const stdin = /** @type {import('stream').Readable | undefined} */ (
ctx.replStdin || ctx.stdin
)
const stdout =
/** @type {import('stream').Writable | undefined} */ (
ctx.replStdout || ctx.stdout
)
if (!stdin || typeof stdin.on !== 'function') {
throw new Error('setup: stdin stream unavailable')
}
bareAgentWriteOut(stdout, '\x1b[?25h\x1b[0m' + prompt)
return bareAgentReadStreamLineOnce(stdin)
}
const BARE_AGENT_STATIC_SYSTEM = `You are the Bare OS autonomous agent inside a JavaScript POSIX-like environment on Hyperdrive + Hyperswarm (Pear/Bare runtime).
JavaScript execution on this OS: **Node.js is not installed.** The \`node\`, \`npm\`, and \`npx\` commands **do not exist** and must never appear in plans or in run_command. To run JS as part of your agent work, **you must call the run_js_script tool** (writes under ~/.agent and executes via the Bare kernel). Optional: once a script exists on disk, run_command may invoke it by **absolute path** (e.g. \`/home/.../script.mjs\`)—same mechanism as \`/bin\` scripts—not via \`node\`.
@@ -164,36 +249,73 @@ function bareAgentFinalizeToolCalls(acc) {
* @param {boolean} opts.interactiveSetup
*/
async function bareAgentInteractiveSetup(ctx, argv0, paths, config, opts) {
void opts
bareAgentLog(ctx, argv0 + ': configuring ~/.agent/config.json')
const readLine =
typeof ctx.readLine === 'function'
? /** @type {(p: string) => Promise<string | null>} */ (
ctx.readLine.bind(ctx)
)
: null
if (!readLine) {
if (!bareAgentCanPlainSetup(ctx)) {
bareAgentErr(
ctx,
argv0 +
': interactive setup requires ctx.readLine (TTY session recommended). Edit ' +
': interactive setup needs a TTY with stdin/stdout. Edit ' +
paths.config +
' manually.'
' manually or run from an interactive shell.'
)
return config
}
const url =
(await readLine(`REST base URL [${config.rest_base_url}]: `)) || ''
if (url.trim()) config.rest_base_url = url.trim()
const keyRaw =
(await readLine(`REST API key (paste; may echo) [leave empty to skip]: `)) ||
''
if (keyRaw.trim()) config.rest_api_key = keyRaw.trim()
const modelRaw =
(await readLine(`Model [${config.model}]: `)) || ''
if (modelRaw.trim()) config.model = modelRaw.trim()
const provRaw =
(await readLine(`Provider label [${config.provider}]: `)) || ''
if (provRaw.trim()) config.provider = provRaw.trim()
const stdout = ctx.replStdout || ctx.stdout
bareAgentWriteOut(
stdout,
'\n=== ' +
argv0 +
' configuration ===\n' +
'Answer each question (Enter keeps the [default]). Typed input is echoed by the terminal.\n\n'
)
try {
const owner =
(await bareAgentPromptSetupLine(
ctx,
'Owner / human name (who operates this agent) [' +
(String(config.owner_name || '').trim() || 'unset') +
']: '
)) || ''
if (owner.trim()) config.owner_name = owner.trim()
const label =
(await bareAgentPromptSetupLine(
ctx,
'Agent display name / label (shown in notes and logs) [' +
(String(config.agent_label || '').trim() || 'BareAgent') +
']: '
)) || ''
if (label.trim()) config.agent_label = label.trim()
const url =
(await bareAgentPromptSetupLine(
ctx,
'REST base URL [' + String(config.rest_base_url || '') + ']: '
)) || ''
if (url.trim()) config.rest_base_url = url.trim()
const keyRaw =
(await bareAgentPromptSetupLine(
ctx,
'REST API key (paste; may echo) [leave empty to skip]: '
)) || ''
if (keyRaw.trim()) config.rest_api_key = keyRaw.trim()
const modelRaw =
(await bareAgentPromptSetupLine(
ctx,
'Model [' + String(config.model || '') + ']: '
)) || ''
if (modelRaw.trim()) config.model = modelRaw.trim()
const provRaw =
(await bareAgentPromptSetupLine(
ctx,
'Provider label [' + String(config.provider || '') + ']: '
)) || ''
if (provRaw.trim()) config.provider = provRaw.trim()
} catch (e) {
const msg =
e && typeof e === 'object' && 'message' in e ? String(e.message) : String(e)
bareAgentErr(ctx, argv0 + ': setup input failed: ' + msg)
return config
}
await bareAgentSaveConfig(ctx, paths, config)
bareAgentLog(ctx, 'Configuration saved.')
return config
@@ -208,10 +330,32 @@ async function bareAgentRunSetupOnly(ctx, argv0) {
const home = bareAgentResolveHome(ctx)
const paths = bareAgentPaths(home)
let { config } = await bareAgentLoadOrCreateConfig(ctx, paths)
config = await bareAgentInteractiveSetup(ctx, argv0, paths, config, {
setupFlag: true,
interactiveSetup: true
})
if (!bareAgentCanPlainSetup(ctx)) {
bareAgentErr(
ctx,
argv0 +
': --setup / --config needs an interactive TTY. Edit ' +
paths.config +
' manually.'
)
ctx.exitCode = 1
return
}
let suspended = false
try {
if (typeof ctx.suspendReplForSubprocess === 'function') {
ctx.suspendReplForSubprocess()
suspended = true
}
config = await bareAgentInteractiveSetup(ctx, argv0, paths, config, {
setupFlag: true,
interactiveSetup: true
})
} finally {
if (suspended && typeof ctx.resumeReplAfterSubprocess === 'function') {
ctx.resumeReplAfterSubprocess()
}
}
try {
if (ctx.vfs?.chmod) await ctx.vfs.chmod(paths.config, 0o600)
} catch {
@@ -232,20 +376,29 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
const paths = bareAgentPaths(home)
let { config } = await bareAgentLoadOrCreateConfig(ctx, paths)
const readLine =
typeof ctx.readLine === 'function'
? /** @type {(p: string) => Promise<string | null>} */ (
ctx.readLine.bind(ctx)
)
: null
const stdin = /** @type {{ isTTY?: boolean } | undefined} */ (ctx.replStdin)
const isTTY = Boolean(stdin && stdin.isTTY)
if (
const canWizard = bareAgentCanPlainSetup(ctx)
if (setupFlag && !canWizard) {
bareAgentErr(
ctx,
argv0 +
': --setup / --config needs an interactive TTY. Edit ' +
paths.config +
' manually.'
)
ctx.exitCode = 1
return
}
const needWizard =
setupFlag ||
(!(config.rest_api_key && String(config.rest_api_key).trim()) &&
readLine &&
isTTY)
) {
(!(config.rest_api_key && String(config.rest_api_key).trim()) && canWizard)
let replSuspendedForSetup = false
if (needWizard && typeof ctx.suspendReplForSubprocess === 'function') {
ctx.suspendReplForSubprocess()
replSuspendedForSetup = true
}
if (needWizard) {
config = await bareAgentInteractiveSetup(ctx, argv0, paths, config, {
setupFlag,
interactiveSetup: true
@@ -253,6 +406,10 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
}
if (!config.rest_api_key || !String(config.rest_api_key).trim()) {
if (replSuspendedForSetup && typeof ctx.resumeReplAfterSubprocess === 'function') {
ctx.resumeReplAfterSubprocess()
replSuspendedForSetup = false
}
bareAgentErr(
ctx,
argv0 +
@@ -260,7 +417,9 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
paths.config +
' or run `' +
argv0 +
' --setup`.'
' --setup` / `' +
argv0 +
' --config`.'
)
ctx.exitCode = 1
return
@@ -274,6 +433,10 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
const fetchFn = bareAgentResolveFetch(ctx)
if (!fetchFn) {
if (replSuspendedForSetup && typeof ctx.resumeReplAfterSubprocess === 'function') {
ctx.resumeReplAfterSubprocess()
replSuspendedForSetup = false
}
bareAgentErr(ctx, argv0 + ': no fetch (ctx.httpFetch / bare.fetch / global fetch)')
ctx.exitCode = 1
return
@@ -351,9 +514,9 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
'/chat/completions'
const tools = bareAgentToolDefinitions()
let suspended = false
let suspended = replSuspendedForSetup
try {
if (typeof ctx.suspendReplForSubprocess === 'function') {
if (typeof ctx.suspendReplForSubprocess === 'function' && !suspended) {
ctx.suspendReplForSubprocess()
suspended = true
}
@@ -3,15 +3,19 @@
"section": 1,
"title": "OpenAI-compatible autonomous coding agent",
"synopsis": [
"agent [--setup | --reset]",
"agent [--setup | --config | --reset]",
"agent reset",
"agent [--setup] REQUEST"
"agent [--setup | --config] REQUEST"
],
"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`**, **`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 prompts for REST base URL, API key, model, provider; writes **`~/.agent/config.json`**"
"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",
@@ -57,5 +61,5 @@
{ "name": "wget", "section": 1 },
{ "name": "chat", "section": 1 }
],
"bareOsNotes": "Session blocks shell input via REPL suspend; **`ctx.readLine`** masking for API keys depends on booter capabilities. Prefer **`chmod 600`** on **`config.json`** (best-effort)."
"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)."
}
+10 -4
View File
@@ -9,12 +9,15 @@ async function run(ctx, argv) {
ctx.console.log(
'usage: ' +
argv0 +
' [--setup | --reset] YOUR_REQUEST_HERE\n' +
' [--setup | --config | --reset] YOUR_REQUEST_HERE\n' +
' ' +
argv0 +
' --setup\n' +
' ' +
argv0 +
' --config\n' +
' ' +
argv0 +
' --reset\n' +
' ' +
argv0 +
@@ -22,7 +25,7 @@ async function run(ctx, argv) {
'\n' +
'Runs an autonomous coding/OS agent against any OpenAI-compatible HTTPS API.\n' +
'Configuration: ~/.agent/config.json on your personal drive (created on first run).\n' +
'Use --setup to interactively set API URL, key, model, and provider label.\n' +
'Use --setup or --config to interactively set owner name, agent label, API URL, key, model, and provider (plain TTY prompts).\n' +
'Use --reset or `reset` to clear ~/.agent/history.json and start a fresh chat session.\n' +
'\n' +
'Examples:\n' +
@@ -34,6 +37,9 @@ async function run(ctx, argv) {
' --setup\n' +
' ' +
argv0 +
' --config\n' +
' ' +
argv0 +
' --reset\n' +
'\n' +
'See man agent.'
@@ -48,7 +54,7 @@ async function run(ctx, argv) {
const rest = []
for (let i = 0; i < args.length; i++) {
const a = args[i]
if (a === '--setup') setupFlag = true
if (a === '--setup' || a === '--config') setupFlag = true
else if (a === '--reset') resetFlag = true
else rest.push(a)
}
@@ -72,7 +78,7 @@ async function run(ctx, argv) {
const task = rest.join(' ').trim()
if (!task && !setupFlag) {
ctx.console.error(argv0 + ': missing task (or use --setup)')
ctx.console.error(argv0 + ': missing task (or use --setup / --config)')
ctx.exitCode = 1
return
}
+220 -52
View File
@@ -1090,7 +1090,9 @@ function bareAgentDefaultConfig() {
request_timeout_ms: 120000,
extra_headers: /** @type {Record<string, string>} */ ({}),
allow_delete: false,
require_confirm_token: ''
require_confirm_token: '',
owner_name: '',
agent_label: ''
}
}
@@ -1122,7 +1124,9 @@ function bareAgentMergeConfig(defaults, src) {
'request_timeout_ms',
'extra_headers',
'allow_delete',
'require_confirm_token'
'require_confirm_token',
'owner_name',
'agent_label'
])
for (const k of Object.keys(src)) {
if (k.startsWith('x-')) continue
@@ -1136,7 +1140,9 @@ function bareAgentMergeConfig(defaults, src) {
k === 'rest_base_url' ||
k === 'rest_api_key' ||
k === 'model' ||
k === 'provider'
k === 'provider' ||
k === 'owner_name' ||
k === 'agent_label'
) {
out[k] = String(val ?? '')
continue
@@ -1184,7 +1190,9 @@ function bareAgentValidateConfigShape(raw) {
'request_timeout_ms',
'extra_headers',
'allow_delete',
'require_confirm_token'
'require_confirm_token',
'owner_name',
'agent_label'
]
if (!known.includes(k)) {
throw new Error('unknown config key: ' + k)
@@ -2903,7 +2911,7 @@ function bareAgentToolDefinitions() {
patch: {
type: 'object',
description:
'Partial config object (rest_base_url, model, temperature, …)'
'Partial config object (rest_base_url, model, temperature, owner_name, agent_label, …)'
}
},
required: ['patch']
@@ -4034,7 +4042,9 @@ function bareAgentMergeConfigPatch(base, patch) {
'tool_parallelism',
'request_timeout_ms',
'allow_delete',
'require_confirm_token'
'require_confirm_token',
'owner_name',
'agent_label'
]
const numKeys = new Set([
'max_tokens',
@@ -4131,6 +4141,91 @@ function bareAgentWriteOut(out, s) {
}
}
/**
* True when we can run the plain-text setup wizard on stdin/stdout (no REPL readline).
* @param {Record<string, unknown>} ctx
*/
function bareAgentCanPlainSetup(ctx) {
const stdin = /** @type {import('stream').Readable | undefined} */ (
ctx.replStdin || ctx.stdin
)
const stdout =
/** @type {import('stream').Writable | undefined} */ (
ctx.replStdout || ctx.stdout
)
const tty = /** @type {{ isTTY?: boolean }} */ (stdin)
return Boolean(
stdin &&
typeof stdin.on === 'function' &&
tty.isTTY &&
stdout &&
typeof stdout.write === 'function'
)
}
/**
* Read one line from a Readable stream (kernel echoes typed chars on cooked TTY).
* @param {import('stream').Readable} stdin
* @returns {Promise<string>}
*/
function bareAgentReadStreamLineOnce(stdin) {
return new Promise((resolve) => {
let acc = ''
/** @param {string | Uint8Array | Buffer} chunk */
function onData(chunk) {
let s = ''
if (typeof chunk === 'string') s = chunk
else if (chunk instanceof Uint8Array) s = new TextDecoder().decode(chunk)
else if (
typeof Buffer !== 'undefined' &&
typeof Buffer.isBuffer === 'function' &&
Buffer.isBuffer(chunk)
)
s = chunk.toString('utf8')
else s = String(chunk)
acc += s
const n = acc.indexOf('\n')
if (n >= 0) {
cleanup()
resolve(acc.slice(0, n).replace(/\r$/, ''))
}
}
function onEnd() {
cleanup()
resolve(acc.replace(/\r$/, ''))
}
function cleanup() {
stdin.removeListener('data', onData)
stdin.removeListener('end', onEnd)
stdin.removeListener('error', onEnd)
}
stdin.on('data', onData)
stdin.once('end', onEnd)
stdin.once('error', onEnd)
if (typeof stdin.resume === 'function') stdin.resume()
})
}
/**
* Write a prompt and read one line using raw streams (not ctx.readLine / TUI stack).
* @param {Record<string, unknown>} ctx
* @param {string} prompt
*/
async function bareAgentPromptSetupLine(ctx, prompt) {
const stdin = /** @type {import('stream').Readable | undefined} */ (
ctx.replStdin || ctx.stdin
)
const stdout =
/** @type {import('stream').Writable | undefined} */ (
ctx.replStdout || ctx.stdout
)
if (!stdin || typeof stdin.on !== 'function') {
throw new Error('setup: stdin stream unavailable')
}
bareAgentWriteOut(stdout, '\x1b[?25h\x1b[0m' + prompt)
return bareAgentReadStreamLineOnce(stdin)
}
const BARE_AGENT_STATIC_SYSTEM = `You are the Bare OS autonomous agent inside a JavaScript POSIX-like environment on Hyperdrive + Hyperswarm (Pear/Bare runtime).
JavaScript execution on this OS: **Node.js is not installed.** The \`node\`, \`npm\`, and \`npx\` commands **do not exist** and must never appear in plans or in run_command. To run JS as part of your agent work, **you must call the run_js_script tool** (writes under ~/.agent and executes via the Bare kernel). Optional: once a script exists on disk, run_command may invoke it by **absolute path** (e.g. \`/home/.../script.mjs\`)—same mechanism as \`/bin\` scripts—not via \`node\`.
@@ -4251,36 +4346,73 @@ function bareAgentFinalizeToolCalls(acc) {
* @param {boolean} opts.interactiveSetup
*/
async function bareAgentInteractiveSetup(ctx, argv0, paths, config, opts) {
void opts
bareAgentLog(ctx, argv0 + ': configuring ~/.agent/config.json')
const readLine =
typeof ctx.readLine === 'function'
? /** @type {(p: string) => Promise<string | null>} */ (
ctx.readLine.bind(ctx)
)
: null
if (!readLine) {
if (!bareAgentCanPlainSetup(ctx)) {
bareAgentErr(
ctx,
argv0 +
': interactive setup requires ctx.readLine (TTY session recommended). Edit ' +
': interactive setup needs a TTY with stdin/stdout. Edit ' +
paths.config +
' manually.'
' manually or run from an interactive shell.'
)
return config
}
const url =
(await readLine(`REST base URL [${config.rest_base_url}]: `)) || ''
if (url.trim()) config.rest_base_url = url.trim()
const keyRaw =
(await readLine(`REST API key (paste; may echo) [leave empty to skip]: `)) ||
''
if (keyRaw.trim()) config.rest_api_key = keyRaw.trim()
const modelRaw =
(await readLine(`Model [${config.model}]: `)) || ''
if (modelRaw.trim()) config.model = modelRaw.trim()
const provRaw =
(await readLine(`Provider label [${config.provider}]: `)) || ''
if (provRaw.trim()) config.provider = provRaw.trim()
const stdout = ctx.replStdout || ctx.stdout
bareAgentWriteOut(
stdout,
'\n=== ' +
argv0 +
' configuration ===\n' +
'Answer each question (Enter keeps the [default]). Typed input is echoed by the terminal.\n\n'
)
try {
const owner =
(await bareAgentPromptSetupLine(
ctx,
'Owner / human name (who operates this agent) [' +
(String(config.owner_name || '').trim() || 'unset') +
']: '
)) || ''
if (owner.trim()) config.owner_name = owner.trim()
const label =
(await bareAgentPromptSetupLine(
ctx,
'Agent display name / label (shown in notes and logs) [' +
(String(config.agent_label || '').trim() || 'BareAgent') +
']: '
)) || ''
if (label.trim()) config.agent_label = label.trim()
const url =
(await bareAgentPromptSetupLine(
ctx,
'REST base URL [' + String(config.rest_base_url || '') + ']: '
)) || ''
if (url.trim()) config.rest_base_url = url.trim()
const keyRaw =
(await bareAgentPromptSetupLine(
ctx,
'REST API key (paste; may echo) [leave empty to skip]: '
)) || ''
if (keyRaw.trim()) config.rest_api_key = keyRaw.trim()
const modelRaw =
(await bareAgentPromptSetupLine(
ctx,
'Model [' + String(config.model || '') + ']: '
)) || ''
if (modelRaw.trim()) config.model = modelRaw.trim()
const provRaw =
(await bareAgentPromptSetupLine(
ctx,
'Provider label [' + String(config.provider || '') + ']: '
)) || ''
if (provRaw.trim()) config.provider = provRaw.trim()
} catch (e) {
const msg =
e && typeof e === 'object' && 'message' in e ? String(e.message) : String(e)
bareAgentErr(ctx, argv0 + ': setup input failed: ' + msg)
return config
}
await bareAgentSaveConfig(ctx, paths, config)
bareAgentLog(ctx, 'Configuration saved.')
return config
@@ -4295,10 +4427,21 @@ async function bareAgentRunSetupOnly(ctx, argv0) {
const home = bareAgentResolveHome(ctx)
const paths = bareAgentPaths(home)
let { config } = await bareAgentLoadOrCreateConfig(ctx, paths)
config = await bareAgentInteractiveSetup(ctx, argv0, paths, config, {
setupFlag: true,
interactiveSetup: true
})
let suspended = false
try {
if (typeof ctx.suspendReplForSubprocess === 'function') {
ctx.suspendReplForSubprocess()
suspended = true
}
config = await bareAgentInteractiveSetup(ctx, argv0, paths, config, {
setupFlag: true,
interactiveSetup: true
})
} finally {
if (suspended && typeof ctx.resumeReplAfterSubprocess === 'function') {
ctx.resumeReplAfterSubprocess()
}
}
try {
if (ctx.vfs?.chmod) await ctx.vfs.chmod(paths.config, 0o600)
} catch {
@@ -4319,20 +4462,29 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
const paths = bareAgentPaths(home)
let { config } = await bareAgentLoadOrCreateConfig(ctx, paths)
const readLine =
typeof ctx.readLine === 'function'
? /** @type {(p: string) => Promise<string | null>} */ (
ctx.readLine.bind(ctx)
)
: null
const stdin = /** @type {{ isTTY?: boolean } | undefined} */ (ctx.replStdin)
const isTTY = Boolean(stdin && stdin.isTTY)
if (
const canWizard = bareAgentCanPlainSetup(ctx)
if (setupFlag && !canWizard) {
bareAgentErr(
ctx,
argv0 +
': --setup / --config needs an interactive TTY. Edit ' +
paths.config +
' manually.'
)
ctx.exitCode = 1
return
}
const needWizard =
setupFlag ||
(!(config.rest_api_key && String(config.rest_api_key).trim()) &&
readLine &&
isTTY)
) {
(!(config.rest_api_key && String(config.rest_api_key).trim()) && canWizard)
let replSuspendedForSetup = false
if (needWizard && typeof ctx.suspendReplForSubprocess === 'function') {
ctx.suspendReplForSubprocess()
replSuspendedForSetup = true
}
if (needWizard) {
config = await bareAgentInteractiveSetup(ctx, argv0, paths, config, {
setupFlag,
interactiveSetup: true
@@ -4340,6 +4492,10 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
}
if (!config.rest_api_key || !String(config.rest_api_key).trim()) {
if (replSuspendedForSetup && typeof ctx.resumeReplAfterSubprocess === 'function') {
ctx.resumeReplAfterSubprocess()
replSuspendedForSetup = false
}
bareAgentErr(
ctx,
argv0 +
@@ -4347,7 +4503,9 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
paths.config +
' or run `' +
argv0 +
' --setup`.'
' --setup` / `' +
argv0 +
' --config`.'
)
ctx.exitCode = 1
return
@@ -4361,6 +4519,10 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
const fetchFn = bareAgentResolveFetch(ctx)
if (!fetchFn) {
if (replSuspendedForSetup && typeof ctx.resumeReplAfterSubprocess === 'function') {
ctx.resumeReplAfterSubprocess()
replSuspendedForSetup = false
}
bareAgentErr(ctx, argv0 + ': no fetch (ctx.httpFetch / bare.fetch / global fetch)')
ctx.exitCode = 1
return
@@ -4438,9 +4600,9 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
'/chat/completions'
const tools = bareAgentToolDefinitions()
let suspended = false
let suspended = replSuspendedForSetup
try {
if (typeof ctx.suspendReplForSubprocess === 'function') {
if (typeof ctx.suspendReplForSubprocess === 'function' && !suspended) {
ctx.suspendReplForSubprocess()
suspended = true
}
@@ -4666,12 +4828,15 @@ async function run(ctx, argv) {
ctx.console.log(
'usage: ' +
argv0 +
' [--setup | --reset] YOUR_REQUEST_HERE\n' +
' [--setup | --config | --reset] YOUR_REQUEST_HERE\n' +
' ' +
argv0 +
' --setup\n' +
' ' +
argv0 +
' --config\n' +
' ' +
argv0 +
' --reset\n' +
' ' +
argv0 +
@@ -4679,7 +4844,7 @@ async function run(ctx, argv) {
'\n' +
'Runs an autonomous coding/OS agent against any OpenAI-compatible HTTPS API.\n' +
'Configuration: ~/.agent/config.json on your personal drive (created on first run).\n' +
'Use --setup to interactively set API URL, key, model, and provider label.\n' +
'Use --setup or --config to interactively set owner name, agent label, API URL, key, model, and provider (plain TTY prompts).\n' +
'Use --reset or `reset` to clear ~/.agent/history.json and start a fresh chat session.\n' +
'\n' +
'Examples:\n' +
@@ -4691,6 +4856,9 @@ async function run(ctx, argv) {
' --setup\n' +
' ' +
argv0 +
' --config\n' +
' ' +
argv0 +
' --reset\n' +
'\n' +
'See man agent.'
@@ -4705,7 +4873,7 @@ async function run(ctx, argv) {
const rest = []
for (let i = 0; i < args.length; i++) {
const a = args[i]
if (a === '--setup') setupFlag = true
if (a === '--setup' || a === '--config') setupFlag = true
else if (a === '--reset') resetFlag = true
else rest.push(a)
}
@@ -4729,7 +4897,7 @@ async function run(ctx, argv) {
const task = rest.join(' ').trim()
if (!task && !setupFlag) {
ctx.console.error(argv0 + ': missing task (or use --setup)')
ctx.console.error(argv0 + ': missing task (or use --setup / --config)')
ctx.exitCode = 1
return
}
@@ -1,7 +1,7 @@
{
"schema": 2,
"profileId": "bare-os-posix-like",
"generatedAt": "2026-04-22T06:45:22.874Z",
"generatedAt": "2026-04-22T06:51:11.664Z",
"note": "Sparse POSIX Issue 7 coverage hints for /bin utilities. Omitted command names are not yet profiled here.",
"commandIndex": [
{
@@ -1,6 +1,6 @@
{
"schema": 1,
"atMs": 1776840322873,
"atMs": 1776840671663,
"commands": [
"agent",
"arch",
@@ -18,14 +18,14 @@ Use this skill whenever the user asks for system status, peer count, drive healt
2. Call **`get_swarm_peers`** (and **`get_system_info`** as needed) for swarm / session context when exposed by the booter.
3. Use **`read_proc_file`** on **`/proc/bare_os/*`** mirrors when the user cares about kernel/session metrics (see the **bare-os-kernel-proc** skill for feature/capability JSON).
4. Check SSH/kernel narrative only when **`read_proc_file`** or **`read_man_page`** confirms how this image exposes **`sshd`** (do not assume **`node`** exists).
4. Summarize in clear bullet points:
5. Summarize in clear bullet points:
- Drive sync status
- Active peers
- Any warnings or errors
- Recent MEMORY.md highlights
## Output Format
## Output format
Always respond with:
File diff suppressed because one or more lines are too long