Files
BrowserOS/scripts/dev/chat-cli.ts
Nikhil ee5de61967 feat: support user system prompt (#147)
* feat: add support for userSystemPrompt

* feat: udpate chat-cli to support user system prompt
2026-01-02 09:07:13 -08:00

196 lines
5.2 KiB
TypeScript

#!/usr/bin/env bun
/**
* Chat CLI - Send a chat request to the HTTP Agent Server
*
* Usage:
* bun --env-file=.env.dev tests/agent-cli.ts "your message here"
* bun --env-file=.env.dev tests/agent-cli.ts --provider=openai --model=gpt-4o "your message here"
*
* Options:
* --provider AI provider (default: browseros)
* --model Model name (default: default)
* --port Server port (default: $BROWSEROS_SERVER_PORT or 9100)
* --system-prompt Custom system prompt to append
* --show-full-output Show full tool output (default: truncated to 50 chars)
*/
interface ChatRequest {
conversationId: string
message: string
provider: string
model: string
apiKey?: string
userSystemPrompt?: string
}
function parseArgs(): {
message: string
provider: string
model: string
port: string
userSystemPrompt?: string
showFullOutput: boolean
} {
const args = process.argv.slice(2)
let provider = 'browseros'
let model = 'default'
let port = process.env.BROWSEROS_SERVER_PORT || '9100'
let showFullOutput = false
let userSystemPrompt: string | undefined
let message = ''
for (const arg of args) {
if (arg.startsWith('--provider=')) {
provider = arg.split('=')[1]
} else if (arg.startsWith('--model=')) {
model = arg.split('=')[1]
} else if (arg.startsWith('--port=')) {
port = arg.split('=')[1]
} else if (arg.startsWith('--system-prompt=')) {
userSystemPrompt = arg.split('=').slice(1).join('=')
} else if (arg === '--show-full-output') {
showFullOutput = true
} else if (!arg.startsWith('--')) {
message = arg
}
}
if (!message) {
console.error('Usage: bun scripts/dev/chat-cli.ts [options] "your message"')
console.error('Options:')
console.error(
' --provider=<provider> AI provider (anthropic, openai, google, etc.)',
)
console.error(' --model=<model> Model name')
console.error(
' --port=<port> Server port (default: $BROWSEROS_SERVER_PORT or 9100)',
)
console.error(' --system-prompt=<prompt> Custom system prompt to append')
console.error(
' --show-full-output Show full tool output (default: truncated)',
)
process.exit(1)
}
return { message, provider, model, port, userSystemPrompt, showFullOutput }
}
function truncateOutput(obj: unknown, maxLen = 50): unknown {
if (typeof obj === 'string') {
return obj.length > maxLen ? `${obj.slice(0, maxLen)}...` : obj
}
if (Array.isArray(obj)) {
return obj.map((item) => truncateOutput(item, maxLen))
}
if (obj && typeof obj === 'object') {
const result: Record<string, unknown> = {}
for (const [key, value] of Object.entries(obj)) {
result[key] = truncateOutput(value, maxLen)
}
return result
}
return obj
}
async function chat(config: {
message: string
provider: string
model: string
port: string
userSystemPrompt?: string
showFullOutput: boolean
}) {
const conversationId = crypto.randomUUID()
const request: ChatRequest = {
conversationId,
message: config.message,
provider: config.provider,
model: config.model,
userSystemPrompt: config.userSystemPrompt,
}
console.log('\n--- Request ---')
console.log(JSON.stringify(request, null, 2))
console.log('\n--- Response Stream ---\n')
const response = await fetch(`http://127.0.0.1:${config.port}/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
})
if (!response.ok) {
const error = await response.text()
console.error(`HTTP ${response.status}: ${error}`)
process.exit(1)
}
const reader = response.body?.getReader()
if (!reader) {
console.error('No response body')
process.exit(1)
}
const decoder = new TextDecoder()
let buffer = ''
while (true) {
const { done, value } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
const lines = buffer.split('\n\n')
buffer = lines.pop() || ''
for (const line of lines) {
if (!line.trim()) continue
if (line.startsWith('data: ')) {
const data = line.slice(6)
if (data === '[DONE]') {
console.log('\n--- Done ---\n')
continue
}
try {
const event = JSON.parse(data)
// Stream text deltas inline for readability
if (event.type === 'text-start') {
process.stdout.write('\n💬 ')
continue
}
if (event.type === 'text-delta') {
process.stdout.write(event.delta)
continue
}
if (event.type === 'text-end') {
process.stdout.write('\n\n')
continue
}
let displayEvent = event
if (
!config.showFullOutput &&
event.type === 'tool-output-available'
) {
displayEvent = { ...event, output: truncateOutput(event.output) }
}
console.log(JSON.stringify(displayEvent, null, 2))
} catch {
console.log(data)
}
}
}
}
}
const config = parseArgs()
chat(config).catch((err) => {
console.error('Error:', err.message)
process.exit(1)
})