TUI CONFIG
This commit is contained in:
+220
-52
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user