@schmitech/orbit-cli 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -7
- package/dist/cli.js +41 -44
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,16 +53,21 @@ node bin/orbit-chat.js
|
|
|
53
53
|
|
|
54
54
|
# In the REPL:
|
|
55
55
|
# type a message and press Enter to send a turn
|
|
56
|
+
# type / to see a filtered command menu (arrow keys, Tab/Enter to pick)
|
|
56
57
|
# /new start a new session
|
|
57
|
-
# /agents
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
# /
|
|
61
|
-
# /
|
|
62
|
-
# /
|
|
58
|
+
# /agents pick an agent (arrow keys) — the key's own adapter +
|
|
59
|
+
# enabled skills actually available to it; clears any
|
|
60
|
+
# /model selection
|
|
61
|
+
# /agents <name|#> switch agent directly, same effect
|
|
62
|
+
# /model pick a model for the current agent (arrow keys)
|
|
63
|
+
# /model <id|#> set the model directly, no picker
|
|
64
|
+
# /key switch API key without restarting (masked prompt;
|
|
65
|
+
# re-discovers agents/models for the new key)
|
|
66
|
+
# /clear clear the screen and this session's server-side history
|
|
63
67
|
# /help list commands
|
|
64
|
-
# /exit exit
|
|
68
|
+
# /exit exit (also clears this session's server-side history)
|
|
65
69
|
# Ctrl+C cancel the in-flight turn; again while idle to exit
|
|
70
|
+
# Ctrl+R reverse search through this session's input history
|
|
66
71
|
# Ctrl+D exit
|
|
67
72
|
#
|
|
68
73
|
# @path/to/file inline in a message attaches that file to the turn (works in
|
package/dist/cli.js
CHANGED
|
@@ -348,8 +348,12 @@ async function listAgents(client, ownAdapter) {
|
|
|
348
348
|
{ label: `${ownAdapter.client_name} (default)`, skill: void 0, adapterName: ownAdapter.adapter_name }
|
|
349
349
|
];
|
|
350
350
|
try {
|
|
351
|
-
const { skills } = await
|
|
352
|
-
|
|
351
|
+
const [{ skills }, { available_skills }] = await Promise.all([
|
|
352
|
+
client.getAllSkills(),
|
|
353
|
+
client.getAdapterSkills(ownAdapter.adapter_name)
|
|
354
|
+
]);
|
|
355
|
+
const allowed = new Set(available_skills);
|
|
356
|
+
for (const skill of skills.filter((s) => s.enabled && allowed.has(s.name))) {
|
|
353
357
|
choices.push({
|
|
354
358
|
label: `${skill.name} \u2014 ${skill.description}`,
|
|
355
359
|
skill: skill.name,
|
|
@@ -732,21 +736,16 @@ var LineReader = class {
|
|
|
732
736
|
this.menuVisible = false;
|
|
733
737
|
}
|
|
734
738
|
/**
|
|
735
|
-
* Enter on a visible menu
|
|
736
|
-
*
|
|
737
|
-
*
|
|
738
|
-
*
|
|
739
|
-
*
|
|
740
|
-
*
|
|
739
|
+
* Enter on a visible menu runs the highlighted command immediately —
|
|
740
|
+
* whether or not it takes an optional argument (e.g. `/agents
|
|
741
|
+
* <name|number>` with no argument opens its own interactive picker; there
|
|
742
|
+
* is no dedicated "no-argument" case to special-case around). A command
|
|
743
|
+
* that does take an argument can still be filled into the buffer without
|
|
744
|
+
* running it via Tab, for typing that argument manually before Enter.
|
|
741
745
|
*/
|
|
742
746
|
selectMenuItemOnEnter() {
|
|
743
747
|
const item = this.menuItems[this.menuIndex];
|
|
744
748
|
if (!item) return;
|
|
745
|
-
if (item.usage) {
|
|
746
|
-
this.acceptMenuSelection();
|
|
747
|
-
this.render();
|
|
748
|
-
return;
|
|
749
|
-
}
|
|
750
749
|
this.menuVisible = false;
|
|
751
750
|
this.buffer = item.name;
|
|
752
751
|
this.cursor = graphemes(this.buffer).length;
|
|
@@ -1156,8 +1155,7 @@ var Spinner = class {
|
|
|
1156
1155
|
var COMMANDS = [
|
|
1157
1156
|
{ name: "/new", usage: "", description: "start a new session (clears server-side context)" },
|
|
1158
1157
|
{ name: "/agents", usage: "<name|number>", description: "pick an agent (arrow keys), or /agents <name|number>" },
|
|
1159
|
-
{ name: "/
|
|
1160
|
-
{ name: "/model", usage: "<id|number>", description: "set the model for later turns" },
|
|
1158
|
+
{ name: "/model", usage: "<id|number>", description: "pick a model (arrow keys), or /model <id|number>" },
|
|
1161
1159
|
{ name: "/key", usage: "", description: "switch API key without restarting (masked prompt)" },
|
|
1162
1160
|
{ name: "/clear", usage: "", description: "clear the screen and this session's server-side history" },
|
|
1163
1161
|
{ name: "/help", usage: "", description: "show this message" },
|
|
@@ -1255,6 +1253,32 @@ async function runRepl(initialClient, connection, initialSkill, initialModel) {
|
|
|
1255
1253
|
}
|
|
1256
1254
|
});
|
|
1257
1255
|
process.stdout.write("Connected. Type /help for commands, Ctrl+D to exit.\n");
|
|
1256
|
+
async function pickModel() {
|
|
1257
|
+
try {
|
|
1258
|
+
lastModels = await listModels(client, currentAgent.adapterName, currentAgent.skill);
|
|
1259
|
+
if (lastModels.length === 0) {
|
|
1260
|
+
process.stdout.write("This agent has no restricted model list (uses its configured default).\n");
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
const idx = await rl.selectFromList(
|
|
1264
|
+
"Select model (type to filter, Esc to cancel): ",
|
|
1265
|
+
lastModels.map((m) => {
|
|
1266
|
+
const id = m.name ?? m.id;
|
|
1267
|
+
return `${id}${id === currentModel ? " (current)" : ""}`;
|
|
1268
|
+
})
|
|
1269
|
+
);
|
|
1270
|
+
if (idx !== null) {
|
|
1271
|
+
const picked = lastModels[idx];
|
|
1272
|
+
currentModel = picked.name ?? picked.id;
|
|
1273
|
+
process.stdout.write(`Model set to ${currentModel}.
|
|
1274
|
+
`);
|
|
1275
|
+
}
|
|
1276
|
+
} catch (error) {
|
|
1277
|
+
const { message } = classifyError(error);
|
|
1278
|
+
process.stderr.write(`${message}
|
|
1279
|
+
`);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1258
1282
|
for await (const line of rl) {
|
|
1259
1283
|
const text = line.trim();
|
|
1260
1284
|
if (!text) {
|
|
@@ -1311,36 +1335,9 @@ async function runRepl(initialClient, connection, initialSkill, initialModel) {
|
|
|
1311
1335
|
}
|
|
1312
1336
|
}
|
|
1313
1337
|
break;
|
|
1314
|
-
case "/models":
|
|
1315
|
-
try {
|
|
1316
|
-
lastModels = await listModels(client, currentAgent.adapterName, currentAgent.skill);
|
|
1317
|
-
if (lastModels.length === 0) {
|
|
1318
|
-
process.stdout.write("This agent has no restricted model list (uses its configured default).\n");
|
|
1319
|
-
} else {
|
|
1320
|
-
const idx = await rl.selectFromList(
|
|
1321
|
-
"Select model (type to filter, Esc to cancel): ",
|
|
1322
|
-
lastModels.map((m) => {
|
|
1323
|
-
const id = m.name ?? m.id;
|
|
1324
|
-
return `${id}${id === currentModel ? " (current)" : ""}`;
|
|
1325
|
-
})
|
|
1326
|
-
);
|
|
1327
|
-
if (idx !== null) {
|
|
1328
|
-
const picked = lastModels[idx];
|
|
1329
|
-
currentModel = picked.name ?? picked.id;
|
|
1330
|
-
process.stdout.write(`Model set to ${currentModel}.
|
|
1331
|
-
`);
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
} catch (error) {
|
|
1335
|
-
const { message: message2 } = classifyError(error);
|
|
1336
|
-
process.stderr.write(`${message2}
|
|
1337
|
-
`);
|
|
1338
|
-
}
|
|
1339
|
-
break;
|
|
1340
1338
|
case "/model":
|
|
1341
1339
|
if (!arg) {
|
|
1342
|
-
|
|
1343
|
-
`);
|
|
1340
|
+
await pickModel();
|
|
1344
1341
|
} else {
|
|
1345
1342
|
const found = findModel(lastModels, arg);
|
|
1346
1343
|
currentModel = found ? found.name ?? found.id : arg;
|
|
@@ -1515,7 +1512,7 @@ async function main() {
|
|
|
1515
1512
|
throw error;
|
|
1516
1513
|
}
|
|
1517
1514
|
if (args.version) {
|
|
1518
|
-
process.stdout.write(`orbit-chat ${true ? "0.1.
|
|
1515
|
+
process.stdout.write(`orbit-chat ${true ? "0.1.2" : "dev"}
|
|
1519
1516
|
`);
|
|
1520
1517
|
process.exit(EXIT_OK);
|
|
1521
1518
|
}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/args.ts","../src/prompt.ts","../src/connection.ts","../src/client.ts","../src/exit-codes.ts","../src/errors.ts","../src/attachments.ts","../src/artifacts.ts","../src/one-shot.ts","../src/repl.ts","../src/agents.ts","../src/input.ts","../src/tty.ts","../src/models.ts","../src/markdown.ts","../src/spinner.ts","../src/stdin.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { ArgsError, parseArgs } from './args.js';\nimport { ConfigError, resolveConnection } from './connection.js';\nimport { createClient } from './client.js';\nimport { classifyError } from './errors.js';\nimport { runOneShot } from './one-shot.js';\nimport { runRepl } from './repl.js';\nimport { readStdin } from './stdin.js';\nimport { EXIT_OK, EXIT_USAGE } from './exit-codes.js';\n\nasync function main(): Promise<void> {\n let args;\n try {\n args = parseArgs(process.argv.slice(2));\n } catch (error) {\n if (error instanceof ArgsError) {\n process.stderr.write(`${error.message}\\n`);\n process.exit(EXIT_USAGE);\n }\n throw error;\n }\n\n if (args.version) {\n process.stdout.write(`orbit-chat ${typeof __ORBIT_CLI_VERSION__ === 'string' ? __ORBIT_CLI_VERSION__ : 'dev'}\\n`);\n process.exit(EXIT_OK);\n }\n\n let connection;\n try {\n connection = await resolveConnection(args);\n } catch (error) {\n if (error instanceof ConfigError) {\n process.stderr.write(`${error.message}\\n`);\n process.exit(EXIT_USAGE);\n }\n throw error;\n }\n\n // The server requires a non-empty session ID on every chat turn. One-shot\n // mode is a single, disposable turn, so a fresh UUID per run is enough —\n // there is no REPL state to carry it across turns yet (Phase 2).\n const client = createClient(connection, args.health ? undefined : randomUUID());\n\n if (args.health) {\n try {\n // getHealth() is unauthenticated, so it only proves the server is\n // reachable. Also call getAdapterInfo(), which is key-scoped and\n // rejects invalid/disabled keys — without it a bad key would still\n // report success.\n const [health, adapterInfo] = await Promise.all([\n client.getHealth(),\n client.getAdapterInfo(),\n ]);\n process.stdout.write(`${JSON.stringify({ ...health, adapter: adapterInfo })}\\n`);\n process.exit(EXIT_OK);\n } catch (error) {\n const { code, message } = classifyError(error);\n process.stderr.write(`${message}\\n`);\n process.exit(code);\n }\n }\n\n const rawMessage = args.positional[0];\n if (rawMessage === undefined) {\n if (!process.stdin.isTTY) {\n process.stderr.write(\n 'No message provided and stdin is not a terminal. Pass a message, or \"-\" to read one from stdin.\\n'\n );\n process.exit(EXIT_USAGE);\n }\n const code = await runRepl(client, connection, args.agent, args.model);\n process.exitCode = code;\n return;\n }\n\n const message = rawMessage === '-' ? (await readStdin()).trim() : rawMessage;\n if (!message) {\n process.stderr.write('No message provided.\\n');\n process.exit(EXIT_USAGE);\n }\n\n // A broken pipe (e.g. `orbit-chat \"hi\" | head -1`) should exit quietly,\n // not throw an unhandled EPIPE error.\n process.stdout.on('error', (error) => {\n if ((error as NodeJS.ErrnoException).code === 'EPIPE') {\n process.exit(EXIT_OK);\n }\n });\n\n const controller = new AbortController();\n process.once('SIGINT', () => controller.abort());\n\n const code = await runOneShot(client, message, {\n skill: args.agent,\n model: args.model,\n signal: controller.signal,\n });\n // Set exitCode and let the event loop drain naturally rather than calling\n // process.exit(), which can terminate the process before buffered\n // stdout.write calls finish — truncating the response when it is large or\n // stdout is a pipe.\n process.exitCode = code;\n}\n\nmain().catch((error) => {\n process.stderr.write(`Unexpected error: ${error instanceof Error ? error.stack ?? error.message : String(error)}\\n`);\n process.exit(1);\n});\n","export interface ParsedArgs {\n url?: string;\n key?: string;\n health: boolean;\n version: boolean;\n agent?: string;\n model?: string;\n positional: string[];\n}\n\nexport class ArgsError extends Error {}\n\nconst FLAGS = new Set(['--url', '--key', '--health', '--agent', '--model', '--version', '-v']);\n\nfunction isFlagLike(value: string | undefined): boolean {\n return value === undefined || FLAGS.has(value);\n}\n\n/**\n * Minimal, dependency-free argv parser:\n * --url <url> | --key <key> | --health | --agent <name> | --model <id>\n * Anything else is collected as positional args, e.g. the one-shot message\n * in `orbit-chat \"question\"` or `orbit-chat -` (read stdin).\n *\n * A value-taking flag with no value, or one immediately followed by another\n * flag, is rejected rather than silently swallowing the next flag as its\n * value (e.g. `--url --health` must not disable --health).\n */\nexport function parseArgs(argv: string[]): ParsedArgs {\n const result: ParsedArgs = { health: false, version: false, positional: [] };\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n switch (arg) {\n case '--version':\n case '-v':\n result.version = true;\n break;\n case '--url': {\n const value = argv[i + 1];\n if (isFlagLike(value)) {\n throw new ArgsError(`--url requires a value`);\n }\n result.url = value;\n i++;\n break;\n }\n case '--key': {\n const value = argv[i + 1];\n if (isFlagLike(value)) {\n throw new ArgsError(`--key requires a value`);\n }\n result.key = value;\n i++;\n break;\n }\n case '--health':\n result.health = true;\n break;\n case '--agent': {\n const value = argv[i + 1];\n if (isFlagLike(value)) {\n throw new ArgsError(`--agent requires a value`);\n }\n result.agent = value;\n i++;\n break;\n }\n case '--model': {\n const value = argv[i + 1];\n if (isFlagLike(value)) {\n throw new ArgsError(`--model requires a value`);\n }\n result.model = value;\n i++;\n break;\n }\n default:\n result.positional.push(arg);\n }\n }\n\n return result;\n}\n","import * as readline from 'node:readline';\n\nconst ETX = '\\x03'; // Ctrl+C\nconst DEL = '\\x7f'; // Backspace (most terminals)\nconst BS = '\\x08'; // Backspace (some terminals)\n\n/** Prompt on stderr so stdout stays clean for piped/scripted output. */\nexport function ask(question: string): Promise<string> {\n const rl = readline.createInterface({ input: process.stdin, output: process.stderr });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\n/** Prompt for a secret without echoing it to the terminal. */\nexport function askSecret(question: string): Promise<string> {\n return new Promise((resolve) => {\n const stdin = process.stdin;\n process.stderr.write(question);\n\n const wasRaw = stdin.isTTY ? stdin.isRaw : false;\n if (stdin.isTTY) stdin.setRawMode(true);\n stdin.resume();\n stdin.setEncoding('utf8');\n\n let value = '';\n const cleanup = () => {\n stdin.removeListener('data', onData);\n if (stdin.isTTY) stdin.setRawMode(wasRaw);\n stdin.pause();\n };\n\n const onData = (chunk: string) => {\n for (const char of chunk) {\n if (char === '\\n' || char === '\\r') {\n cleanup();\n process.stderr.write('\\n');\n resolve(value.trim());\n return;\n } else if (char === ETX) {\n cleanup();\n process.stderr.write('\\n');\n process.exit(130);\n } else if (char === DEL || char === BS) {\n value = value.slice(0, -1);\n } else {\n value += char;\n }\n }\n };\n\n stdin.on('data', onData);\n });\n}\n","import type { ParsedArgs } from './args.js';\nimport { ask, askSecret } from './prompt.js';\n\nexport interface Connection {\n url: string;\n apiKey: string;\n}\n\nexport class ConfigError extends Error {}\n\n/** Assumed for a locally-run ORBIT server, same as most CLI tools default to localhost. */\nconst DEFAULT_URL = 'http://localhost:3000';\n\n/**\n * Resolve the server URL and API key.\n *\n * Order: CLI flags -> env vars -> `DEFAULT_URL` (or an interactive prompt\n * that itself defaults to it, on a TTY). Nothing is persisted to disk — each\n * run resolves its own connection.\n */\nexport async function resolveConnection(args: ParsedArgs): Promise<Connection> {\n let url = args.url ?? process.env.ORBIT_URL;\n let apiKey = args.key ?? process.env.ORBIT_API_KEY;\n\n if (!url) {\n if (!process.stdin.isTTY) {\n url = DEFAULT_URL;\n } else {\n const answer = await ask(`ORBIT server URL [${DEFAULT_URL}]: `);\n url = answer || DEFAULT_URL;\n }\n }\n\n if (!apiKey) {\n if (!process.stdin.isTTY) {\n throw new ConfigError(\n 'Missing API key. Pass --key <key> or set ORBIT_API_KEY (preferred over --key, which lands in shell history).'\n );\n }\n apiKey = await askSecret('ORBIT API key: ');\n }\n\n url = url.trim().replace(/\\/+$/, '');\n if (!url) {\n throw new ConfigError('Server URL cannot be empty.');\n }\n if (!apiKey.trim()) {\n throw new ConfigError('API key cannot be empty.');\n }\n\n return { url, apiKey: apiKey.trim() };\n}\n","import { ApiClient, type StreamResponse } from '@schmitech/chatbot-api';\nimport type { Connection } from './connection.js';\n\nexport function createClient(connection: Connection, sessionId?: string): ApiClient {\n return new ApiClient({\n apiUrl: connection.url,\n apiKey: connection.apiKey,\n sessionId: sessionId ?? null,\n });\n}\n\nexport interface ChatOptions {\n stream?: boolean;\n fileIds?: string[];\n threadId?: string;\n abortSignal?: AbortSignal;\n model?: string;\n skill?: string;\n}\n\n/**\n * Named-options wrapper around ApiClient.streamChat's 15-argument positional\n * signature. This is the only place the CLI touches that signature directly —\n * every caller elsewhere in the CLI goes through this function.\n */\nexport function streamChat(\n client: ApiClient,\n message: string,\n options: ChatOptions = {}\n): AsyncGenerator<StreamResponse> {\n return client.streamChat(\n message,\n options.stream ?? true,\n options.fileIds,\n options.threadId,\n undefined, // audioInput\n undefined, // audioFormat\n undefined, // language\n undefined, // returnAudio\n undefined, // ttsVoice\n undefined, // sourceLanguage\n undefined, // targetLanguage\n options.abortSignal,\n options.model,\n options.skill,\n undefined // regenerateOfMessageId\n );\n}\n","export const EXIT_OK = 0;\nexport const EXIT_SERVER_ERROR = 1;\nexport const EXIT_USAGE = 2;\nexport const EXIT_AUTH = 3;\nexport const EXIT_NETWORK = 4;\nexport const EXIT_CANCELLED = 130;\n","import { EXIT_AUTH, EXIT_NETWORK, EXIT_SERVER_ERROR } from './exit-codes.js';\n\n/**\n * Classify an error thrown by the SDK into one of the CLI's exit codes.\n *\n * The SDK (clients/node-api) wraps most failures in a plain Error whose\n * message embeds the HTTP status code, and its network-error detection\n * (\"Failed to fetch\") is written for browser fetch — Node's undici throws a\n * differently worded TypeError, so it is matched separately here.\n */\nexport function classifyError(error: unknown): { code: number; message: string } {\n const message = error instanceof Error ? error.message : String(error);\n\n if (/ECONNREFUSED|ENOTFOUND|EAI_AGAIN|fetch failed|Could not connect to the server/i.test(message)) {\n return { code: EXIT_NETWORK, message: 'Could not connect to the server. Check the URL and that it is running.' };\n }\n\n if (/:\\s*401\\b/.test(message) || /invalid|disabled|no associated adapter/i.test(message)) {\n return { code: EXIT_AUTH, message: 'API key is invalid, disabled, or lacks an adapter.' };\n }\n\n return { code: EXIT_SERVER_ERROR, message };\n}\n","import { readFile } from 'node:fs/promises';\nimport { basename, extname } from 'node:path';\nimport type { ApiClient } from '@schmitech/chatbot-api';\n\nconst MIME_TYPES: Record<string, string> = {\n '.txt': 'text/plain',\n '.md': 'text/markdown',\n '.json': 'application/json',\n '.csv': 'text/csv',\n '.pdf': 'application/pdf',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n};\n\nfunction mimeTypeFor(path: string): string {\n return MIME_TYPES[extname(path).toLowerCase()] ?? 'application/octet-stream';\n}\n\n/** Matches an `@path` token: `@` followed by non-whitespace. */\nconst ATTACHMENT_PATTERN = /@(\\S+)/g;\n\nexport class AttachmentError extends Error {}\n\n/**\n * Extract `@path` tokens from a message, upload each file, and return the\n * message with those tokens stripped plus the resulting file ids.\n *\n * Attachments are per-turn: the caller passes the returned file ids as this\n * turn's fileIds only, so they don't silently persist onto later turns.\n */\nexport async function extractAttachments(\n client: ApiClient,\n message: string\n): Promise<{ text: string; fileIds: string[] }> {\n const paths = [...message.matchAll(ATTACHMENT_PATTERN)].map((match) => match[1]);\n if (paths.length === 0) {\n return { text: message, fileIds: [] };\n }\n\n const fileIds: string[] = [];\n for (const path of paths) {\n let bytes: Buffer;\n try {\n bytes = await readFile(path);\n } catch {\n throw new AttachmentError(`Attachment not found: ${path}`);\n }\n const file = new File([bytes], basename(path), { type: mimeTypeFor(path) });\n const uploaded = await client.uploadFile(file);\n fileIds.push(uploaded.file_id);\n }\n\n const text = message.replace(ATTACHMENT_PATTERN, '').replace(/\\s+/g, ' ').trim();\n return { text: text || 'Please review the attached file(s).', fileIds };\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { randomUUID } from 'node:crypto';\nimport type { ApiClient, StreamResponse } from '@schmitech/chatbot-api';\n\nconst OUT_DIR = './.orbit-out';\n\n// The two shapes the SDK returns for generated media: inline base64 (with a\n// *_format field) and a persistent server-side URL that needs an\n// authenticated download via ApiClient.downloadArtifact().\nconst INLINE_FIELDS = [\n { data: 'image', format: 'image_format', kind: 'image' },\n { data: 'video', format: 'video_format', kind: 'video' },\n { data: 'document', format: 'document_format', kind: 'document' },\n] as const;\n\nconst URL_FIELDS = [\n { url: 'image_url', kind: 'image' },\n { url: 'video_url', kind: 'video' },\n { url: 'document_url', kind: 'document' },\n { url: 'generated_audio_url', kind: 'audio' },\n] as const;\n\nasync function writeArtifact(kind: string, format: string | undefined, bytes: Buffer): Promise<string> {\n await mkdir(OUT_DIR, { recursive: true });\n const ext = format ? `.${format}` : '';\n const path = `${OUT_DIR}/${kind}-${randomUUID()}${ext}`;\n await writeFile(path, bytes);\n return path;\n}\n\nfunction extFromContentType(contentType: string | null): string {\n if (!contentType) return '';\n const subtype = contentType.split('/')[1]?.split(';')[0];\n return subtype ? `.${subtype}` : '';\n}\n\n/**\n * Save any artifacts present on a stream chunk to ./.orbit-out/ and return\n * the paths written. Most chunks carry none of these fields; this is a\n * cheap no-op check for the common case.\n */\nexport async function saveArtifacts(client: ApiClient, chunk: StreamResponse): Promise<string[]> {\n const paths: string[] = [];\n const record = chunk as unknown as Record<string, string | undefined>;\n const savedKinds = new Set<string>();\n\n for (const { data, format, kind } of INLINE_FIELDS) {\n const base64 = record[data];\n if (base64) {\n paths.push(await writeArtifact(kind, record[format], Buffer.from(base64, 'base64')));\n savedKinds.add(kind);\n }\n }\n\n // The server can persist both an inline copy and a refreshable URL for the\n // same generated media on one chunk — save it once, preferring the inline\n // copy already handled above since it needs no extra download round-trip.\n for (const { url, kind } of URL_FIELDS) {\n if (savedKinds.has(kind)) continue;\n const value = record[url];\n if (value) {\n const { data, contentType } = await client.downloadArtifact(value);\n paths.push(await writeArtifact(kind, extFromContentType(contentType).replace(/^\\./, '') || undefined, Buffer.from(data)));\n }\n }\n\n return paths;\n}\n","import type { ApiClient } from '@schmitech/chatbot-api';\nimport { AttachmentError, extractAttachments } from './attachments.js';\nimport { saveArtifacts } from './artifacts.js';\nimport { streamChat, type ChatOptions } from './client.js';\nimport { classifyError } from './errors.js';\nimport { EXIT_CANCELLED, EXIT_OK, EXIT_USAGE } from './exit-codes.js';\n\n/**\n * Send one turn and stream the reply to stdout as plain text — no ANSI, no\n * progress noise. stdout carries the response only; errors, artifact\n * notices, and progress go to stderr. Returns the process exit code;\n * callers are responsible for process.exit.\n */\nexport async function runOneShot(\n client: ApiClient,\n rawMessage: string,\n options: ChatOptions & { signal?: AbortSignal }\n): Promise<number> {\n let message: string;\n let fileIds: string[];\n try {\n ({ text: message, fileIds } = await extractAttachments(client, rawMessage));\n } catch (error) {\n if (error instanceof AttachmentError) {\n process.stderr.write(`${error.message}\\n`);\n return EXIT_USAGE;\n }\n const { code, message: errorMessage } = classifyError(error);\n process.stderr.write(`${errorMessage}\\n`);\n return code;\n }\n\n try {\n for await (const chunk of streamChat(client, message, {\n ...options,\n fileIds,\n abortSignal: options.signal,\n })) {\n if (chunk.text) {\n process.stdout.write(chunk.text);\n }\n for (const path of await saveArtifacts(client, chunk)) {\n process.stderr.write(`Saved artifact: ${path}\\n`);\n }\n }\n process.stdout.write('\\n');\n return EXIT_OK;\n } catch (error) {\n if (options.signal?.aborted) {\n return EXIT_CANCELLED;\n }\n const { code, message: errorMessage } = classifyError(error);\n process.stderr.write(`${errorMessage}\\n`);\n return code;\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport * as fs from 'node:fs';\nimport type { ApiClient } from '@schmitech/chatbot-api';\nimport { findAgent, listAgents, type AgentChoice } from './agents.js';\nimport { extractAttachments, AttachmentError } from './attachments.js';\nimport { saveArtifacts } from './artifacts.js';\nimport { createClient, streamChat } from './client.js';\nimport type { Connection } from './connection.js';\nimport { classifyError } from './errors.js';\nimport { LineReader, type CommandSpec } from './input.js';\nimport { findModel, listModels } from './models.js';\nimport { MarkdownRenderer } from './markdown.js';\nimport { Spinner } from './spinner.js';\n\n/** Single source of truth for both `/help` text and the interactive `/` menu, so the two can't drift. */\nconst COMMANDS: CommandSpec[] = [\n { name: '/new', usage: '', description: 'start a new session (clears server-side context)' },\n { name: '/agents', usage: '<name|number>', description: 'pick an agent (arrow keys), or /agents <name|number>' },\n { name: '/models', usage: '', description: 'pick a model for the current agent (arrow keys)' },\n { name: '/model', usage: '<id|number>', description: 'set the model for later turns' },\n { name: '/key', usage: '', description: 'switch API key without restarting (masked prompt)' },\n { name: '/clear', usage: '', description: 'clear the screen and this session\\'s server-side history' },\n { name: '/help', usage: '', description: 'show this message' },\n { name: '/exit', usage: '', description: 'exit' },\n];\n\nconst HELP = `Commands:\n${COMMANDS.map((c) => ` ${(c.name + ' ' + c.usage).padEnd(21)}${c.description}`).join('\\n')}\n Ctrl+C cancel the in-flight turn; again (while idle) to exit\n Ctrl+R reverse search through this session's input history\n Ctrl+D exit\n@path/to/file inline in a message attaches that file to the turn.\nType / to see a filtered command menu (arrow keys + Tab/Enter to pick).\nAnything else is sent as a chat turn.`;\n\n/** Completes an `@path` token against the filesystem as the user types it. */\nfunction completer(line: string): [string[], string] {\n const match = /(?:^|\\s)@(\\S*)$/.exec(line);\n if (!match) {\n return [[], line];\n }\n\n const partial = match[1];\n const slash = partial.lastIndexOf('/');\n const dir = slash === -1 ? '.' : partial.slice(0, slash) || '/';\n const prefix = slash === -1 ? partial : partial.slice(slash + 1);\n const dirPrefix = slash === -1 ? '' : `${partial.slice(0, slash)}/`;\n\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return [[], partial];\n }\n\n const hits = entries\n .filter((entry) => entry.name.startsWith(prefix))\n .map((entry) => `${dirPrefix}${entry.name}${entry.isDirectory() ? '/' : ''}`);\n\n return [hits, partial];\n}\n\nconst CLEAR_HISTORY_TIMEOUT_MS = 1500;\n\n/**\n * Delete a session's server-side history without letting a stalled or\n * unreachable network hang the caller — the SDK call has no abort signal, so\n * this races it against a short timer instead of awaiting it directly. Used\n * by `/new`, `/clear`, and exit-time cleanup, so all three share one bound\n * rather than each risking drifting out of sync (or a hang) independently.\n *\n * `timedOut` tells the caller the request is being abandoned, not confirmed\n * done — e.g. exit-time cleanup uses that to decide whether it still needs\n * to force the process closed.\n */\nasync function clearHistoryBestEffort(\n client: ApiClient,\n sessionId: string\n): Promise<{ timedOut: boolean; error?: string }> {\n let timedOut = false;\n let error: string | undefined;\n await new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n timedOut = true;\n resolve();\n }, CLEAR_HISTORY_TIMEOUT_MS);\n client\n .clearConversationHistory(sessionId)\n .catch((err) => {\n error = classifyError(err).message;\n })\n .then(() => {\n clearTimeout(timer);\n resolve();\n });\n });\n return { timedOut, error };\n}\n\n/**\n * Interactive REPL: a prompt line plus streaming transcript. Input is a\n * hand-rolled raw-mode line editor (`input.ts`) rather than plain\n * `node:readline` — needed to render the `/` command menu and Ctrl+R\n * reverse-search overlay, neither of which readline's completer hook\n * supports. `input.ts` still exposes an async iterator, so the turn-handling\n * logic below is unchanged from the readline-based version.\n */\nexport async function runRepl(\n initialClient: ApiClient,\n connection: Connection,\n initialSkill: string | undefined,\n initialModel: string | undefined\n): Promise<number> {\n let client = initialClient;\n let ownAdapter;\n try {\n ownAdapter = await client.getAdapterInfo();\n } catch (error) {\n const { code, message } = classifyError(error);\n process.stderr.write(`${message}\\n`);\n return code;\n }\n\n let agents = await listAgents(client, ownAdapter);\n let currentAgent: AgentChoice = agents[0];\n if (initialSkill) {\n const found = findAgent(agents, initialSkill);\n if (found) {\n currentAgent = found;\n } else {\n process.stderr.write(`Unknown agent \"${initialSkill}\"; using the default adapter.\\n`);\n }\n }\n\n let currentModel = initialModel;\n let lastModels: Awaited<ReturnType<typeof listModels>> = [];\n\n let controller: AbortController | null = null;\n let currentRequestId: string | undefined;\n\n // Set whenever any clearHistoryBestEffort call times out (from /new,\n // /clear, or exit itself): that call's underlying fetch has no abort\n // signal, so it keeps running — and its open socket can keep the process\n // alive — well past the point our own await gave up on it. A /new whose\n // background cleanup already timed out earlier must not let a later,\n // unrelated fast exit-time cleanup return normally and leave that\n // dangling fetch as the reason the process never actually exits.\n let hasAbandonedCleanup = false;\n // A /new-triggered cleanup that hasn't settled yet (hasn't even hit its\n // own timeout) at the moment of exit is a second, distinct race:\n // `hasAbandonedCleanup` alone wouldn't catch it, since nothing has timed\n // out *yet* — it might resolve fine a moment later, or it might not. Exit\n // must not gamble on which; tracking it here means exit can force-close\n // rather than leave that outcome to chance.\n const pendingBackgroundCleanups = new Set<Promise<unknown>>();\n\n const rl = new LineReader({\n prompt: '> ',\n commands: COMMANDS,\n completer,\n onSigint: () => {\n if (controller) {\n controller.abort();\n const sessionId = client.getSessionId();\n if (sessionId && currentRequestId) {\n client.stopChat(sessionId, currentRequestId).catch(() => {});\n }\n process.stdout.write('\\n[cancelled]\\n');\n // The turn's catch block restores the prompt once the abort unwinds.\n } else {\n rl.requestExit();\n }\n },\n });\n\n process.stdout.write('Connected. Type /help for commands, Ctrl+D to exit.\\n');\n\n for await (const line of rl) {\n const text = line.trim();\n\n if (!text) {\n continue;\n }\n\n if (text.startsWith('/')) {\n const [command, ...rest] = text.split(/\\s+/);\n const arg = rest.join(' ');\n switch (command) {\n case '/new': {\n const oldSessionId = client.getSessionId();\n client.setSessionId(randomUUID());\n process.stdout.write('Started a new session.\\n');\n // Fire-and-forget: /new's whole point is switching immediately, so\n // it must not block the next prompt on a network round trip (an\n // unreachable server would otherwise freeze the REPL for the full\n // clearHistoryBestEffort timeout). Otherwise the abandoned\n // session's history sits in the database with nothing left\n // pointing at it — the same orphan this command's own description\n // promises not to leave behind — so still report it if cleanup\n // doesn't land, just asynchronously once it settles.\n if (oldSessionId) {\n const cleanup = clearHistoryBestEffort(client, oldSessionId)\n .then(({ timedOut, error }) => {\n if (timedOut) {\n hasAbandonedCleanup = true;\n }\n if (timedOut || error) {\n process.stderr.write(`Previous session's history was not cleared: ${error ?? 'timed out'}\\n`);\n }\n })\n .catch(() => {});\n pendingBackgroundCleanups.add(cleanup);\n cleanup.finally(() => pendingBackgroundCleanups.delete(cleanup));\n }\n break;\n }\n\n case '/agents':\n if (!arg) {\n const idx = await rl.selectFromList(\n 'Select agent (type to filter, Esc to cancel): ',\n agents.map((a) => `${a.label}${a === currentAgent ? ' (current)' : ''}`)\n );\n if (idx !== null) {\n currentAgent = agents[idx];\n currentModel = undefined;\n lastModels = [];\n process.stdout.write(`Switched to ${currentAgent.label}.\\n`);\n }\n } else {\n const found = findAgent(agents, arg);\n if (!found) {\n process.stdout.write(`No agent matches \"${arg}\". Try /agents to list them.\\n`);\n } else {\n currentAgent = found;\n currentModel = undefined;\n lastModels = [];\n process.stdout.write(`Switched to ${found.label}.\\n`);\n }\n }\n break;\n\n case '/models':\n try {\n lastModels = await listModels(client, currentAgent.adapterName, currentAgent.skill);\n if (lastModels.length === 0) {\n process.stdout.write('This agent has no restricted model list (uses its configured default).\\n');\n } else {\n const idx = await rl.selectFromList(\n 'Select model (type to filter, Esc to cancel): ',\n lastModels.map((m) => {\n const id = m.name ?? m.id;\n return `${id}${id === currentModel ? ' (current)' : ''}`;\n })\n );\n if (idx !== null) {\n const picked = lastModels[idx];\n currentModel = picked.name ?? picked.id;\n process.stdout.write(`Model set to ${currentModel}.\\n`);\n }\n }\n } catch (error) {\n const { message } = classifyError(error);\n process.stderr.write(`${message}\\n`);\n }\n break;\n\n case '/model':\n if (!arg) {\n process.stdout.write(`Current model: ${currentModel ?? '(adapter default)'}\\n`);\n } else {\n const found = findModel(lastModels, arg);\n currentModel = found ? found.name ?? found.id : arg;\n process.stdout.write(`Model set to ${currentModel}.\\n`);\n }\n break;\n\n case '/key': {\n if (arg) {\n // The full \"/key <arg>\" line is already in scrollback and\n // history by the time this runs (LineReader.finish() prints and\n // records it before dispatch) — accepting the key here would\n // just launder an already-leaked secret. Bare `/key` instead\n // routes through the masked prompt below, which never echoes or\n // records what's typed.\n process.stdout.write(\n 'For your key\\'s safety, run /key with no argument — it will prompt you separately with input hidden.\\n'\n );\n break;\n }\n const newKey = await rl.readSecret('API key: ');\n if (newKey === null) {\n process.stdout.write('Cancelled.\\n');\n break;\n }\n if (!newKey.trim()) {\n process.stderr.write('API key cannot be empty.\\n');\n break;\n }\n const candidate = createClient(\n { url: connection.url, apiKey: newKey.trim() },\n client.getSessionId() ?? undefined\n );\n let newAdapter;\n try {\n newAdapter = await candidate.getAdapterInfo();\n } catch (error) {\n const { message } = classifyError(error);\n process.stderr.write(`Could not switch key: ${message}\\n`);\n break;\n }\n // A different key can map to a different adapter with its own\n // skills/models, so re-discover agents and drop the old\n // model/skill selection rather than carrying it over silently.\n client = candidate;\n ownAdapter = newAdapter;\n agents = await listAgents(client, ownAdapter);\n currentAgent = agents[0];\n currentModel = undefined;\n lastModels = [];\n process.stdout.write(`Switched API key. Now using ${currentAgent.label}.\\n`);\n break;\n }\n\n case '/clear': {\n // Best-effort: also delete the server-side conversation history for\n // this session (DELETE /admin/chat-history/{id}, ownership-checked\n // by API key server-side) so \"clear\" matches what it looks like —\n // a fresh conversation, not just a repainted terminal. A server\n // without a conversational adapter configured 503s here; that's\n // not a reason to skip the terminal clear itself. Bounded the same\n // way as /new and exit-time cleanup, so a stalled network can't\n // hang this command either.\n const sessionId = client.getSessionId();\n let historyError: string | undefined;\n if (sessionId) {\n const result = await clearHistoryBestEffort(client, sessionId);\n if (result.timedOut) {\n hasAbandonedCleanup = true;\n }\n historyError = result.timedOut ? 'timed out' : result.error;\n }\n // Home + erase screen + erase saved lines (the same sequence the\n // `clear` utility emits). Erasing only the visible screen (the\n // previous `cursorTo`/`clearScreenDown` pair) left the prior\n // conversation sitting in the terminal's scrollback just above the\n // now-blank viewport — visually it looked like the app had\n // \"scrolled away,\" reachable only via the scrollbar. Clearing\n // scrollback too means there's nothing left to scroll up to.\n process.stdout.write('\\x1b[H\\x1b[2J\\x1b[3J');\n if (historyError) {\n process.stderr.write(`Terminal cleared, but server-side history was not: ${historyError}\\n`);\n }\n break;\n }\n\n case '/help':\n process.stdout.write(`${HELP}\\n`);\n break;\n\n case '/exit':\n rl.requestExit();\n continue;\n\n default:\n process.stdout.write(`Unknown command: ${command} (try /help)\\n`);\n }\n continue;\n }\n\n let message: string;\n let fileIds: string[];\n try {\n ({ text: message, fileIds } = await extractAttachments(client, text));\n } catch (error) {\n if (error instanceof AttachmentError) {\n process.stderr.write(`${error.message}\\n`);\n } else {\n const { message: msg } = classifyError(error);\n process.stderr.write(`${msg}\\n`);\n }\n continue;\n }\n\n controller = new AbortController();\n currentRequestId = undefined;\n const renderer = new MarkdownRenderer();\n const spinner = new Spinner();\n spinner.start();\n\n try {\n for await (const chunk of streamChat(client, message, {\n skill: currentAgent.skill,\n model: currentModel,\n fileIds,\n abortSignal: controller.signal,\n })) {\n if (chunk.request_id) {\n currentRequestId = chunk.request_id;\n }\n if (chunk.text) {\n spinner.stop();\n renderer.push(chunk.text);\n }\n const artifactPaths = await saveArtifacts(client, chunk);\n if (artifactPaths.length > 0) {\n spinner.stop();\n // The buffered trailing partial line (typically the final chunk's\n // text, with no newline yet) must land before the notice, or it\n // reads as if the artifact arrived before the reply that mentions it.\n renderer.flush();\n for (const path of artifactPaths) {\n process.stdout.write(`Saved artifact: ${path}\\n`);\n }\n }\n }\n spinner.stop();\n renderer.flush();\n process.stdout.write('\\n');\n } catch (error) {\n spinner.stop();\n renderer.flush();\n if (!controller.signal.aborted) {\n const { message: msg } = classifyError(error);\n process.stderr.write(`${msg}\\n`);\n }\n } finally {\n spinner.stop();\n controller = null;\n currentRequestId = undefined;\n }\n }\n\n process.stdout.write('\\n');\n\n // The loop above ends on every exit path (/exit, Ctrl+D, idle Ctrl+C), so\n // this one spot covers all of them. Best-effort: an unreachable server or\n // a deployment without a conversational adapter (503) shouldn't block or\n // spam an error on the way out — but leaving the row behind would orphan\n // this session's history in the database with nothing left to ever clear it.\n //\n // clearHistoryBestEffort bounds the SDK call, which has no abort signal of\n // its own, against a short timer instead of an OS-level TCP timeout. The\n // two outcomes are handled differently: if cleanup wins, its request is\n // done and there's nothing left dangling, so this falls through to the\n // normal `return 0` below — which matters when stdout/stderr are\n // redirected to a file or pipe, since only the normal return path lets\n // that output drain before the process exits. Only a timeout — this call's\n // own, or an earlier /new's fire-and-forget one that never got the chance\n // to force-exit itself (it isn't the one deciding whether the REPL is\n // exiting) — force-exits: at that point some request is being abandoned\n // outright, its fetch potentially still open, and there is no other way to\n // stop waiting on it.\n const sessionId = client.getSessionId();\n if (sessionId) {\n const { timedOut } = await clearHistoryBestEffort(client, sessionId);\n if (timedOut) {\n hasAbandonedCleanup = true;\n }\n }\n\n // A /new-triggered cleanup can still be mid-flight here, not yet past its\n // own timeout — indistinguishable at this instant from one that's about to\n // succeed. Exiting normally would gamble the process's shutdown on which;\n // its still-open fetch either way is reason enough to force-close rather\n // than wait and find out.\n if (hasAbandonedCleanup || pendingBackgroundCleanups.size > 0) {\n process.exit(0);\n }\n\n return 0;\n}\n","import type { AdapterInfo, ApiClient } from '@schmitech/chatbot-api';\n\nexport interface AgentChoice {\n label: string;\n /** undefined selects the key's own adapter (the default) rather than a skill. */\n skill: string | undefined;\n adapterName: string;\n}\n\n/**\n * List the key's own adapter plus every enabled skill, per the roadmap's\n * `/agents` design: the chat adapter is fixed by the API key, but a skill\n * re-targets a turn to that skill's own adapter (see\n * docs/roadmap/orbit-cli-repl.md's \"`/agents` — what it can and cannot do\").\n * Skill discovery is best-effort — a server with none registered, or a\n * failing call, just leaves the single default adapter.\n */\nexport async function listAgents(client: ApiClient, ownAdapter: AdapterInfo): Promise<AgentChoice[]> {\n const choices: AgentChoice[] = [\n { label: `${ownAdapter.client_name} (default)`, skill: undefined, adapterName: ownAdapter.adapter_name },\n ];\n\n try {\n const { skills } = await client.getAllSkills();\n for (const skill of skills.filter((s) => s.enabled)) {\n choices.push({\n label: `${skill.name} — ${skill.description}`,\n skill: skill.name,\n adapterName: skill.adapter_name,\n });\n }\n } catch {\n // No skills registered, or discovery failed — single-agent is fine.\n }\n\n return choices;\n}\n\nexport function findAgent(agents: AgentChoice[], query: string): AgentChoice | undefined {\n const index = Number(query);\n if (Number.isInteger(index) && index >= 1 && index <= agents.length) {\n return agents[index - 1];\n }\n return agents.find((a) => a.skill?.toLowerCase() === query.toLowerCase());\n}\n","import * as readline from 'node:readline';\nimport { ansiEnabled } from './tty.js';\n\nconst RESET = '\\x1b[0m';\nconst DIM = '\\x1b[2m';\nconst INVERT = '\\x1b[7m';\nconst CTRL_C = '\\x03';\n\nexport interface CommandSpec {\n name: string;\n usage: string;\n description: string;\n}\n\ntype Completer = (line: string) => [string[], string];\n\nexport interface LineReaderOptions {\n prompt: string;\n commands: CommandSpec[];\n completer: Completer;\n /** Called on Ctrl+C outside of reverse-search; caller decides abort-turn vs. exit-REPL. */\n onSigint: () => void;\n}\n\n// eslint-disable-next-line no-control-regex -- matches the ANSI escape byte itself, to strip color codes before measuring display width.\nconst ANSI_PATTERN = /\\x1b\\[[0-9;]*m/g;\n\nfunction isWideCodePoint(cp: number): boolean {\n return (\n (cp >= 0x1100 && cp <= 0x115f) || // Hangul Jamo\n cp === 0x2329 ||\n cp === 0x232a ||\n (cp >= 0x2e80 && cp <= 0x303e) || // CJK Radicals..CJK Symbols\n (cp >= 0x3041 && cp <= 0x33ff) || // Hiragana..CJK Compatibility\n (cp >= 0x3400 && cp <= 0x4dbf) || // CJK Extension A\n (cp >= 0x4e00 && cp <= 0x9fff) || // CJK Unified Ideographs\n (cp >= 0xa000 && cp <= 0xa4cf) || // Yi\n (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables\n (cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs\n (cp >= 0xfe30 && cp <= 0xfe4f) || // CJK Compatibility Forms\n (cp >= 0xff00 && cp <= 0xff60) || // Fullwidth Forms\n (cp >= 0xffe0 && cp <= 0xffe6) ||\n (cp >= 0x1f300 && cp <= 0x1faff) || // emoji blocks (approximate)\n (cp >= 0x20000 && cp <= 0x3fffd) // CJK Extension B and beyond\n );\n}\n\nfunction isZeroWidthCodePoint(cp: number): boolean {\n return (\n cp === 0x200b || // zero width space\n cp === 0x200c ||\n cp === 0x200d || // zero width joiner\n cp === 0xfeff ||\n (cp >= 0x0300 && cp <= 0x036f) || // combining diacritics\n (cp >= 0x1ab0 && cp <= 0x1aff) ||\n (cp >= 0x1dc0 && cp <= 0x1dff) ||\n (cp >= 0x20d0 && cp <= 0x20ff) ||\n (cp >= 0xfe00 && cp <= 0xfe0f) // variation selectors\n );\n}\n\n/** Terminal column width of a string: strips ANSI escapes, counts wide/full-width code points as 2 columns and zero-width marks as 0. */\nfunction displayWidth(str: string): number {\n const clean = str.replace(ANSI_PATTERN, '');\n let width = 0;\n for (const ch of clean) {\n const cp = ch.codePointAt(0) ?? 0;\n if (isZeroWidthCodePoint(cp)) continue;\n width += isWideCodePoint(cp) ? 2 : 1;\n }\n return width;\n}\n\n/** Terminal rows a string wraps into at the given column width. */\nfunction rowsFor(text: string, width: number): number {\n return Math.max(1, Math.ceil(displayWidth(text) / width) || 1);\n}\n\nconst graphemeSegmenter =\n typeof Intl !== 'undefined' && 'Segmenter' in Intl ? new Intl.Segmenter(undefined, { granularity: 'grapheme' }) : null;\n\n/** Split a string into grapheme clusters (falling back to code points) so cursor movement never lands inside a surrogate pair or a combined emoji. */\nfunction graphemes(str: string): string[] {\n if (graphemeSegmenter) {\n return Array.from(graphemeSegmenter.segment(str), (s) => s.segment);\n }\n return Array.from(str);\n}\n\nfunction commonPrefix(strings: string[]): string {\n if (strings.length === 0) return '';\n let prefix = strings[0];\n for (const s of strings.slice(1)) {\n let i = 0;\n while (i < prefix.length && i < s.length && prefix[i] === s[i]) i++;\n prefix = prefix.slice(0, i);\n }\n return prefix;\n}\n\n/**\n * Hand-rolled raw-mode line editor, replacing `node:readline`'s line editing\n * so the REPL can render UI that readline has no hook for: a live `/`\n * command dropdown and a Ctrl+R reverse-search overlay. Modeled on the\n * masked-input loop `prompt.ts::askSecret()` already uses, extended with\n * cursor movement, in-memory history, and the two overlays above.\n */\nexport class LineReader {\n private readonly prompt: string;\n private readonly commands: CommandSpec[];\n private readonly completer: Completer;\n private readonly onSigint: () => void;\n private readonly stdin = process.stdin;\n private readonly history: string[] = [];\n\n private buffer = '';\n private cursor = 0;\n private historyIndex = 0;\n\n private menuVisible = false;\n private menuIndex = 0;\n private menuItems: CommandSpec[] = [];\n\n private searchMode = false;\n private searchQuery = '';\n private searchMatch: string | undefined;\n private searchScanIndex = 0;\n\n private secretMode = false;\n private secretPrompt = '';\n private secretBuffer = '';\n private resolveSecret: ((value: string | null) => void) | null = null;\n\n private listMode = false;\n private listPrompt = '';\n private listQuery = '';\n private listAllItems: string[] = [];\n private listFiltered: number[] = [];\n private listIndex = 0;\n private resolveList: ((index: number | null) => void) | null = null;\n\n private resolveLine: ((line: string | null) => void) | null = null;\n private closed = false;\n private wasRaw = false;\n /** Row offset (relative to the input's first row) the cursor was left on by the last render, for wrapped-line redraw bookkeeping. */\n private lastCursorRow = 0;\n\n constructor(options: LineReaderOptions) {\n this.prompt = options.prompt;\n this.commands = options.commands;\n this.completer = options.completer;\n this.onSigint = options.onSigint;\n\n if (this.stdin.isTTY) {\n readline.emitKeypressEvents(this.stdin);\n this.wasRaw = this.stdin.isRaw;\n this.stdin.setRawMode(true);\n }\n this.stdin.resume();\n this.stdin.setEncoding('utf8');\n this.stdin.on('keypress', this.onKeypress);\n }\n\n async *[Symbol.asyncIterator](): AsyncGenerator<string> {\n while (!this.closed) {\n const line = await this.readLine();\n if (line === null) {\n return;\n }\n yield line;\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.stdin.removeListener('keypress', this.onKeypress);\n if (this.stdin.isTTY) {\n this.stdin.setRawMode(this.wasRaw);\n }\n // The constructor calls stdin.resume() to receive keypress events; a\n // resumed, flowing stdin keeps the event loop alive on its own, so\n // without pausing it here the process never exits after /exit or Ctrl+D.\n this.stdin.pause();\n }\n\n /**\n * `cursor` counts grapheme clusters, not UTF-16 code units — a `cursor--`\n * next to an emoji or a surrogate-pair character must skip the whole\n * cluster, not land inside it. This converts that cluster count to the\n * actual string offset needed for `buffer.slice(...)`.\n */\n private cursorStringIndex(): number {\n return graphemes(this.buffer).slice(0, this.cursor).join('').length;\n }\n\n /**\n * Insert `text` at string offset `idx` and recompute the cursor by\n * re-segmenting the resulting prefix, rather than adding `graphemes(text)`\n * to the old cursor. The terminal delivers a multi-codepoint emoji (e.g. a\n * ZWJ family sequence) as separate keypress events one codepoint at a\n * time; segmented alone, a joiner counts as its own cluster, but Unicode's\n * grapheme rules attach it to whatever already precedes it in the buffer.\n * Re-segmenting the real prefix after each keystroke keeps the cursor's\n * cluster count correct instead of drifting ahead of the buffer's actual\n * cluster count (which crashes the next backspace/delete on an\n * out-of-range index).\n */\n private insertAt(idx: number, text: string): void {\n this.buffer = this.buffer.slice(0, idx) + text + this.buffer.slice(idx);\n this.cursor = graphemes(this.buffer.slice(0, idx + text.length)).length;\n }\n\n /**\n * Read one line of masked input (e.g. an API key) without it ever being\n * echoed, written to terminal scrollback, or added to line history — only\n * a `*` per typed character is shown, same masking `prompt.ts::askSecret()`\n * uses for the initial connection prompt. Ctrl+C cancels and resolves\n * `null` instead of exiting the process, since a REPL command is in\n * progress rather than startup.\n */\n readSecret(promptText: string): Promise<string | null> {\n this.secretMode = true;\n this.secretPrompt = promptText;\n this.secretBuffer = '';\n this.lastCursorRow = 0;\n this.renderSecret();\n return new Promise((resolve) => {\n this.resolveSecret = resolve;\n });\n }\n\n /**\n * Present `items` as an arrow-key-navigable list (type to filter, like the\n * `/` command menu) and resolve the chosen index, or `null` on Esc/Ctrl+C.\n * Used for `/agents` and `/models` so picking one doesn't require typing\n * its number or name.\n */\n selectFromList(promptText: string, items: string[]): Promise<number | null> {\n this.listMode = true;\n this.listPrompt = promptText;\n this.listQuery = '';\n this.listAllItems = items;\n this.listFiltered = items.map((_, i) => i);\n this.listIndex = 0;\n this.lastCursorRow = 0;\n this.renderList();\n return new Promise((resolve) => {\n this.resolveList = resolve;\n });\n }\n\n private readLine(): Promise<string | null> {\n this.buffer = '';\n this.cursor = 0;\n this.historyIndex = this.history.length;\n this.menuVisible = false;\n this.searchMode = false;\n this.secretMode = false;\n this.listMode = false;\n this.lastCursorRow = 0;\n this.render();\n return new Promise((resolve) => {\n this.resolveLine = resolve;\n });\n }\n\n private onKeypress = (str: string | undefined, key: readline.Key): void => {\n if (this.closed) return;\n\n const isCtrlC = key.sequence === CTRL_C || (key.ctrl && key.name === 'c');\n if (isCtrlC) {\n if (this.secretMode) {\n this.finishSecret(null);\n return;\n }\n if (this.listMode) {\n this.finishList(null);\n return;\n }\n if (this.searchMode) {\n this.exitSearch();\n this.render();\n return;\n }\n this.onSigint();\n return;\n }\n\n if (this.secretMode) {\n this.handleSecretKey(str, key);\n return;\n }\n\n if (this.listMode) {\n this.handleListKey(str, key);\n return;\n }\n\n // Between finish() clearing resolveLine and the next readLine() call (e.g.\n // while a chat turn is streaming), there is no buffer to edit — only\n // cancellation (handled above) is meaningful here. Anything else would\n // corrupt the streamed output and be silently discarded on the next read.\n if (!this.resolveLine) {\n return;\n }\n\n if (this.searchMode) {\n this.handleSearchKey(str, key);\n return;\n }\n\n if (key.ctrl && key.name === 'r') {\n this.searchMode = true;\n this.searchQuery = '';\n this.searchMatch = undefined;\n this.searchScanIndex = this.history.length;\n this.render();\n return;\n }\n\n if (key.ctrl && key.name === 'd') {\n if (this.buffer.length === 0) {\n this.finish(null);\n }\n return;\n }\n\n switch (key.name) {\n case 'return':\n if (this.menuVisible && this.menuItems.length > 0) {\n this.selectMenuItemOnEnter();\n return;\n }\n this.submit();\n return;\n case 'backspace': {\n if (this.cursor > 0) {\n const segs = graphemes(this.buffer);\n const idx = segs.slice(0, this.cursor - 1).join('').length;\n const removed = segs[this.cursor - 1];\n this.buffer = this.buffer.slice(0, idx) + this.buffer.slice(idx + removed.length);\n this.cursor--;\n }\n break;\n }\n case 'delete': {\n const segs = graphemes(this.buffer);\n if (this.cursor < segs.length) {\n const idx = segs.slice(0, this.cursor).join('').length;\n const removed = segs[this.cursor];\n this.buffer = this.buffer.slice(0, idx) + this.buffer.slice(idx + removed.length);\n }\n break;\n }\n case 'left':\n if (this.cursor > 0) this.cursor--;\n break;\n case 'right':\n if (this.cursor < graphemes(this.buffer).length) this.cursor++;\n break;\n case 'up':\n if (this.menuVisible && this.menuItems.length > 0) {\n this.menuIndex = (this.menuIndex - 1 + this.menuItems.length) % this.menuItems.length;\n } else {\n this.historyUp();\n }\n break;\n case 'down':\n if (this.menuVisible && this.menuItems.length > 0) {\n this.menuIndex = (this.menuIndex + 1) % this.menuItems.length;\n } else {\n this.historyDown();\n }\n break;\n case 'tab':\n if (this.menuVisible && this.menuItems.length > 0) {\n this.acceptMenuSelection();\n } else {\n this.applyCompletion();\n }\n break;\n case 'escape':\n // Dismiss without calling updateMenu() below — the buffer is\n // unchanged by Esc, so it would still match `/...` and immediately\n // reopen the menu it was just told to close.\n this.menuVisible = false;\n this.render();\n return;\n default:\n if (str && !key.ctrl && !key.meta && str >= ' ') {\n this.insertAt(this.cursorStringIndex(), str);\n }\n }\n\n this.updateMenu();\n this.render();\n };\n\n private historyUp(): void {\n if (this.historyIndex > 0) {\n this.historyIndex--;\n this.buffer = this.history[this.historyIndex];\n this.cursor = graphemes(this.buffer).length;\n }\n }\n\n private historyDown(): void {\n if (this.historyIndex < this.history.length - 1) {\n this.historyIndex++;\n this.buffer = this.history[this.historyIndex];\n this.cursor = graphemes(this.buffer).length;\n } else {\n this.historyIndex = this.history.length;\n this.buffer = '';\n this.cursor = 0;\n }\n }\n\n private updateMenu(): void {\n const match = /^\\/(\\S*)$/.exec(this.buffer);\n if (!match) {\n this.menuVisible = false;\n return;\n }\n const query = match[1].toLowerCase();\n const items = this.commands.filter((c) => c.name.slice(1).toLowerCase().startsWith(query));\n if (items.length === 0) {\n this.menuVisible = false;\n return;\n }\n this.menuItems = items;\n if (this.menuIndex >= items.length) {\n this.menuIndex = 0;\n }\n this.menuVisible = true;\n }\n\n private acceptMenuSelection(): void {\n const item = this.menuItems[this.menuIndex];\n if (!item) return;\n this.buffer = `${item.name} `;\n this.cursor = graphemes(this.buffer).length;\n this.menuVisible = false;\n }\n\n /**\n * Enter on a visible menu: a command that takes no further argument (empty\n * `usage`, e.g. `/models`) runs immediately, since there's nothing left to\n * type — otherwise Enter would just fill the buffer and force a second\n * Enter to actually submit it. A command that does take an argument (e.g.\n * `/agents <name|number>`) still only fills the buffer, the same as Tab,\n * so the user can type that argument before submitting.\n */\n private selectMenuItemOnEnter(): void {\n const item = this.menuItems[this.menuIndex];\n if (!item) return;\n if (item.usage) {\n this.acceptMenuSelection();\n this.render();\n return;\n }\n this.menuVisible = false;\n this.buffer = item.name;\n this.cursor = graphemes(this.buffer).length;\n this.submit();\n }\n\n private applyCompletion(): void {\n const idx = this.cursorStringIndex();\n const [hits, partial] = this.completer(this.buffer.slice(0, idx));\n if (hits.length === 1) {\n this.insertAt(idx, hits[0].slice(partial.length));\n } else if (hits.length > 1) {\n const common = commonPrefix(hits);\n if (common.length > partial.length) {\n this.insertAt(idx, common.slice(partial.length));\n }\n }\n }\n\n private handleSearchKey(str: string | undefined, key: readline.Key): void {\n if (key.name === 'return') {\n this.buffer = this.searchMatch ?? this.searchQuery;\n this.cursor = graphemes(this.buffer).length;\n this.exitSearch();\n this.submit();\n return;\n }\n if (key.name === 'escape' || (key.ctrl && key.name === 'g')) {\n this.exitSearch();\n this.render();\n return;\n }\n if (key.ctrl && key.name === 'r') {\n this.findSearchMatch(true);\n this.render();\n return;\n }\n if (key.name === 'backspace') {\n this.searchQuery = graphemes(this.searchQuery).slice(0, -1).join('');\n this.searchScanIndex = this.history.length;\n this.findSearchMatch(false);\n this.render();\n return;\n }\n if (str && !key.ctrl && !key.meta && str >= ' ') {\n this.searchQuery += str;\n this.searchScanIndex = this.history.length;\n this.findSearchMatch(false);\n }\n this.render();\n }\n\n private handleSecretKey(str: string | undefined, key: readline.Key): void {\n if (key.name === 'return') {\n this.finishSecret(this.secretBuffer);\n return;\n }\n if (key.name === 'backspace') {\n this.secretBuffer = graphemes(this.secretBuffer).slice(0, -1).join('');\n this.renderSecret();\n return;\n }\n if (key.name === 'escape') {\n this.finishSecret(null);\n return;\n }\n if (str && !key.ctrl && !key.meta && str >= ' ') {\n this.secretBuffer += str;\n this.renderSecret();\n }\n }\n\n private renderSecret(): void {\n if (this.lastCursorRow > 0) {\n readline.moveCursor(process.stdout, 0, -this.lastCursorRow);\n }\n readline.cursorTo(process.stdout, 0);\n readline.clearScreenDown(process.stdout);\n const masked = '*'.repeat(graphemes(this.secretBuffer).length);\n const text = this.secretPrompt + masked;\n process.stdout.write(text);\n const width = process.stdout.columns || 80;\n this.lastCursorRow = rowsFor(text, width) - 1;\n }\n\n private finishSecret(value: string | null): void {\n this.secretMode = false;\n if (this.lastCursorRow > 0) {\n readline.moveCursor(process.stdout, 0, -this.lastCursorRow);\n }\n readline.cursorTo(process.stdout, 0);\n readline.clearScreenDown(process.stdout);\n process.stdout.write('\\n');\n this.lastCursorRow = 0;\n const resolve = this.resolveSecret;\n this.resolveSecret = null;\n this.secretBuffer = '';\n resolve?.(value);\n }\n\n private handleListKey(str: string | undefined, key: readline.Key): void {\n if (key.name === 'return') {\n const idx = this.listFiltered[this.listIndex];\n this.finishList(idx === undefined ? null : idx);\n return;\n }\n if (key.name === 'escape') {\n this.finishList(null);\n return;\n }\n if (key.name === 'up') {\n if (this.listFiltered.length > 0) {\n this.listIndex = (this.listIndex - 1 + this.listFiltered.length) % this.listFiltered.length;\n }\n this.renderList();\n return;\n }\n if (key.name === 'down') {\n if (this.listFiltered.length > 0) {\n this.listIndex = (this.listIndex + 1) % this.listFiltered.length;\n }\n this.renderList();\n return;\n }\n if (key.name === 'backspace') {\n this.listQuery = graphemes(this.listQuery).slice(0, -1).join('');\n this.updateListFilter();\n this.renderList();\n return;\n }\n if (str && !key.ctrl && !key.meta && str >= ' ') {\n this.listQuery += str;\n this.updateListFilter();\n }\n this.renderList();\n }\n\n private updateListFilter(): void {\n const query = this.listQuery.toLowerCase();\n this.listFiltered = this.listAllItems\n .map((label, i) => ({ label, i }))\n .filter(({ label }) => label.toLowerCase().includes(query))\n .map(({ i }) => i);\n if (this.listIndex >= this.listFiltered.length) {\n this.listIndex = 0;\n }\n }\n\n private renderList(): void {\n const width = process.stdout.columns || 80;\n if (this.lastCursorRow > 0) {\n readline.moveCursor(process.stdout, 0, -this.lastCursorRow);\n }\n readline.cursorTo(process.stdout, 0);\n readline.clearScreenDown(process.stdout);\n const color = ansiEnabled();\n\n const headerText = `${this.listPrompt}${this.listQuery}`;\n process.stdout.write(headerText);\n\n let listRows = 0;\n if (this.listFiltered.length === 0) {\n const text = `${color ? DIM : ''} (no matches)${color ? RESET : ''}`;\n process.stdout.write(`\\n${text}`);\n listRows += rowsFor(text, width);\n } else {\n for (const [pos, idx] of this.listFiltered.entries()) {\n const selected = pos === this.listIndex;\n const marker = selected ? '›' : ' ';\n const text = `${marker} ${this.listAllItems[idx]}`;\n const rendered = `${selected && color ? INVERT : ''}${text}${selected && color ? RESET : ''}`;\n process.stdout.write(`\\n${rendered}`);\n listRows += rowsFor(rendered, width);\n }\n }\n\n // Same wrapped-row bookkeeping as the main render(): walk back up to the\n // header's first row, then back down to where the header text (which can\n // itself wrap) actually leaves the cursor.\n const headerRows = rowsFor(headerText, width);\n const rowsBelowFirst = headerRows - 1 + listRows;\n if (rowsBelowFirst > 0) {\n readline.moveCursor(process.stdout, 0, -rowsBelowFirst);\n }\n\n const cursorAbsolute = displayWidth(headerText);\n const cursorRow = Math.floor(cursorAbsolute / width);\n const cursorCol = cursorAbsolute % width;\n if (cursorRow > 0) {\n readline.moveCursor(process.stdout, 0, cursorRow);\n }\n readline.cursorTo(process.stdout, cursorCol);\n this.lastCursorRow = cursorRow;\n }\n\n private finishList(index: number | null): void {\n this.listMode = false;\n if (this.lastCursorRow > 0) {\n readline.moveCursor(process.stdout, 0, -this.lastCursorRow);\n }\n readline.cursorTo(process.stdout, 0);\n readline.clearScreenDown(process.stdout);\n this.lastCursorRow = 0;\n const resolve = this.resolveList;\n this.resolveList = null;\n resolve?.(index);\n }\n\n private findSearchMatch(next: boolean): void {\n if (!this.searchQuery) {\n this.searchMatch = undefined;\n return;\n }\n const start = next ? this.searchScanIndex - 1 : this.history.length - 1;\n for (let i = start; i >= 0; i--) {\n if (this.history[i].includes(this.searchQuery)) {\n this.searchMatch = this.history[i];\n this.searchScanIndex = i;\n return;\n }\n }\n this.searchMatch = undefined;\n }\n\n private exitSearch(): void {\n this.searchMode = false;\n this.searchQuery = '';\n this.searchMatch = undefined;\n }\n\n private submit(): void {\n const line = this.buffer;\n if (line.trim().length > 0) {\n this.history.push(line);\n }\n this.finish(line);\n }\n\n private finish(line: string | null): void {\n this.menuVisible = false;\n if (this.lastCursorRow > 0) {\n readline.moveCursor(process.stdout, 0, -this.lastCursorRow);\n }\n readline.cursorTo(process.stdout, 0);\n readline.clearScreenDown(process.stdout);\n process.stdout.write(line !== null ? `${this.prompt}${line}\\n` : '\\n');\n this.lastCursorRow = 0;\n const resolve = this.resolveLine;\n this.resolveLine = null;\n if (line === null) {\n this.close();\n }\n resolve?.(line);\n }\n\n /** Force the in-flight read to end as EOF, e.g. on an idle Ctrl+C. */\n requestExit(): void {\n if (this.searchMode) {\n this.exitSearch();\n }\n this.finish(null);\n }\n\n private render(): void {\n const width = process.stdout.columns || 80;\n\n // The previous render left the cursor `lastCursorRow` rows below the\n // input's first row (input can wrap past one terminal row). Walk back up\n // to that first row before clearing, or a long line's earlier rows are\n // left stale on screen.\n if (this.lastCursorRow > 0) {\n readline.moveCursor(process.stdout, 0, -this.lastCursorRow);\n }\n readline.cursorTo(process.stdout, 0);\n readline.clearScreenDown(process.stdout);\n const color = ansiEnabled();\n\n if (this.searchMode) {\n const label = `(reverse-i-search)\\`${this.searchQuery}': `;\n const text = label + (this.searchMatch ?? '');\n process.stdout.write(text);\n this.lastCursorRow = rowsFor(text, width) - 1;\n return;\n }\n\n const inputText = this.prompt + this.buffer;\n process.stdout.write(inputText);\n\n // Rows each rendered menu entry wraps into (a long description, or wide\n // characters, can push a single entry past one row) — summed, not one\n // row per item, or a narrow terminal misplaces the redraw origin.\n let menuRows = 0;\n if (this.menuVisible) {\n for (const [i, item] of this.menuItems.entries()) {\n const selected = i === this.menuIndex;\n const marker = selected ? '›' : ' ';\n const usage = item.usage ? ` ${item.usage}` : '';\n const text = `${marker} ${item.name}${usage} ${color ? DIM : ''}— ${item.description}${color ? RESET : ''}`;\n const rendered = `${selected && color ? INVERT : ''}${text}${selected && color ? RESET : ''}`;\n process.stdout.write(`\\n${rendered}`);\n menuRows += rowsFor(rendered, width);\n }\n }\n\n // Rows the input line itself wraps into, plus the menu rows below it —\n // together, how far below the input's first row the cursor now sits.\n const inputRows = rowsFor(inputText, width);\n const rowsBelowFirst = inputRows - 1 + menuRows;\n if (rowsBelowFirst > 0) {\n readline.moveCursor(process.stdout, 0, -rowsBelowFirst);\n }\n\n const cursorAbsolute = displayWidth(this.prompt) + displayWidth(this.buffer.slice(0, this.cursorStringIndex()));\n const cursorRow = Math.floor(cursorAbsolute / width);\n const cursorCol = cursorAbsolute % width;\n if (cursorRow > 0) {\n readline.moveCursor(process.stdout, 0, cursorRow);\n }\n readline.cursorTo(process.stdout, cursorCol);\n this.lastCursorRow = cursorRow;\n }\n}\n","/** True when stdout is a real terminal and the user hasn't opted out via `NO_COLOR`. */\nexport function ansiEnabled(): boolean {\n return Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;\n}\n","import type { AdapterModelInfo, ApiClient } from '@schmitech/chatbot-api';\n\n/**\n * List the models allowed for the currently active agent.\n *\n * The server resolves models for the API key's own adapter regardless of\n * `adapterName` unless `skill` is also passed — so a selected skill must be\n * threaded through here, not just its backing adapter name, or this quietly\n * falls back to the base adapter's models.\n */\nexport async function listModels(\n client: ApiClient,\n adapterName: string,\n skill: string | undefined\n): Promise<AdapterModelInfo[]> {\n const { models } = await client.getAdapterModels(adapterName, skill);\n return models;\n}\n\nexport function findModel(models: AdapterModelInfo[], query: string): AdapterModelInfo | undefined {\n const index = Number(query);\n if (Number.isInteger(index) && index >= 1 && index <= models.length) {\n return models[index - 1];\n }\n return models.find((m) => m.name?.toLowerCase() === query.toLowerCase() || m.id?.toLowerCase() === query.toLowerCase());\n}\n","import { highlight, supportsLanguage } from 'cli-highlight';\nimport { ansiEnabled } from './tty.js';\n\nconst RESET = '\\x1b[0m';\nconst BOLD = '\\x1b[1m';\nconst DIM = '\\x1b[2m';\nconst ITALIC = '\\x1b[3m';\nconst HEADING = '\\x1b[1;36m';\n\nfunction styleLine(line: string): string {\n const heading = /^(#{1,6})\\s+(.*)$/.exec(line);\n if (heading) {\n return `${HEADING}${heading[2]}${RESET}`;\n }\n\n let out = line;\n out = out.replace(/\\*\\*(.+?)\\*\\*/g, `${BOLD}$1${RESET}`);\n out = out.replace(/__(.+?)__/g, `${BOLD}$1${RESET}`);\n out = out.replace(/(?<!\\*)\\*([^*]+)\\*(?!\\*)/g, `${ITALIC}$1${RESET}`);\n out = out.replace(/`([^`]+)`/g, `${DIM}$1${RESET}`);\n out = out.replace(/^(\\s*)[-*+]\\s+/, '$1• ');\n out = out.replace(/^(\\s*>)\\s?/, `${DIM}│${RESET} `);\n return out;\n}\n\nfunction stripLine(line: string): string {\n return line\n .replace(/^#{1,6}\\s+/, '')\n .replace(/\\*\\*(.+?)\\*\\*/g, '$1')\n .replace(/__(.+?)__/g, '$1')\n .replace(/`([^`]+)`/g, '$1')\n .replace(/^(\\s*)[-*+]\\s+/, '$1- ');\n}\n\n/**\n * Line-buffered markdown -> ANSI renderer for the REPL's streaming\n * transcript: headings, emphasis, inline code, list markers, and a dimmed\n * treatment for fenced code blocks. Deliberately line-buffered rather than\n * block-buffered with a retroactive tail re-render — a completed line is\n * final and is never rewritten, which is simpler and avoids the cursor\n * bookkeeping a live-redraw renderer needs, at the cost of not syntax\n * highlighting code by language and not reflowing tables. Respects\n * `NO_COLOR` and non-TTY by falling back to the markdown stripped of its\n * syntax rather than raw asterisks/hashes.\n */\nexport class MarkdownRenderer {\n private buffer = '';\n private inFence = false;\n private fenceLang: string | undefined;\n\n push(text: string): void {\n this.buffer += text;\n let newlineIndex: number;\n while ((newlineIndex = this.buffer.indexOf('\\n')) !== -1) {\n const line = this.buffer.slice(0, newlineIndex);\n this.buffer = this.buffer.slice(newlineIndex + 1);\n this.emitLine(line);\n }\n }\n\n /** Flush any trailing partial line (the model's reply need not end in \\n). */\n flush(): void {\n if (this.buffer.length > 0) {\n this.emitLine(this.buffer);\n this.buffer = '';\n }\n }\n\n private emitLine(line: string): void {\n const color = ansiEnabled();\n\n const fenceMatch = /^```\\s*(\\S*)/.exec(line.trim());\n if (fenceMatch) {\n this.inFence = !this.inFence;\n this.fenceLang = this.inFence ? fenceMatch[1] || undefined : undefined;\n if (color) {\n process.stdout.write(`${DIM}${line}${RESET}\\n`);\n }\n // Plain-text fallback strips markdown syntax; the fence delimiter\n // itself carries no content, so it is dropped rather than printed.\n return;\n }\n\n if (this.inFence) {\n if (!color) {\n process.stdout.write(`${line}\\n`);\n return;\n }\n if (this.fenceLang && supportsLanguage(this.fenceLang)) {\n try {\n process.stdout.write(`${highlight(line, { language: this.fenceLang, ignoreIllegals: true })}\\n`);\n return;\n } catch {\n // Fall through to the flat dim treatment below.\n }\n }\n process.stdout.write(`${DIM}${line}${RESET}\\n`);\n return;\n }\n\n process.stdout.write(`${color ? styleLine(line) : stripLine(line)}\\n`);\n }\n}\n","import { ansiEnabled } from './tty.js';\n\nconst FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\nconst INTERVAL_MS = 80;\n\n/** A `\\r`-overwritten spinner shown while waiting for the first stream chunk. No-op without a color-capable TTY. */\nexport class Spinner {\n private timer: ReturnType<typeof setInterval> | null = null;\n private frame = 0;\n\n start(): void {\n if (!ansiEnabled() || this.timer) {\n return;\n }\n this.timer = setInterval(() => {\n process.stdout.write(`\\r${FRAMES[this.frame]} `);\n this.frame = (this.frame + 1) % FRAMES.length;\n }, INTERVAL_MS);\n }\n\n /** Stop and erase the spinner line so following output starts at column 0. */\n stop(): void {\n if (!this.timer) {\n return;\n }\n clearInterval(this.timer);\n this.timer = null;\n process.stdout.write('\\r\\x1b[K');\n }\n}\n","/** Read all of stdin as utf8 text, for `orbit-chat -`. */\nexport async function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n"],"mappings":"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","names":["randomUUID","randomUUID","readline","RESET","DIM","sessionId","randomUUID","message","randomUUID","code","message"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/args.ts","../src/prompt.ts","../src/connection.ts","../src/client.ts","../src/exit-codes.ts","../src/errors.ts","../src/attachments.ts","../src/artifacts.ts","../src/one-shot.ts","../src/repl.ts","../src/agents.ts","../src/input.ts","../src/tty.ts","../src/models.ts","../src/markdown.ts","../src/spinner.ts","../src/stdin.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { ArgsError, parseArgs } from './args.js';\nimport { ConfigError, resolveConnection } from './connection.js';\nimport { createClient } from './client.js';\nimport { classifyError } from './errors.js';\nimport { runOneShot } from './one-shot.js';\nimport { runRepl } from './repl.js';\nimport { readStdin } from './stdin.js';\nimport { EXIT_OK, EXIT_USAGE } from './exit-codes.js';\n\nasync function main(): Promise<void> {\n let args;\n try {\n args = parseArgs(process.argv.slice(2));\n } catch (error) {\n if (error instanceof ArgsError) {\n process.stderr.write(`${error.message}\\n`);\n process.exit(EXIT_USAGE);\n }\n throw error;\n }\n\n if (args.version) {\n process.stdout.write(`orbit-chat ${typeof __ORBIT_CLI_VERSION__ === 'string' ? __ORBIT_CLI_VERSION__ : 'dev'}\\n`);\n process.exit(EXIT_OK);\n }\n\n let connection;\n try {\n connection = await resolveConnection(args);\n } catch (error) {\n if (error instanceof ConfigError) {\n process.stderr.write(`${error.message}\\n`);\n process.exit(EXIT_USAGE);\n }\n throw error;\n }\n\n // The server requires a non-empty session ID on every chat turn. One-shot\n // mode is a single, disposable turn, so a fresh UUID per run is enough —\n // there is no REPL state to carry it across turns yet (Phase 2).\n const client = createClient(connection, args.health ? undefined : randomUUID());\n\n if (args.health) {\n try {\n // getHealth() is unauthenticated, so it only proves the server is\n // reachable. Also call getAdapterInfo(), which is key-scoped and\n // rejects invalid/disabled keys — without it a bad key would still\n // report success.\n const [health, adapterInfo] = await Promise.all([\n client.getHealth(),\n client.getAdapterInfo(),\n ]);\n process.stdout.write(`${JSON.stringify({ ...health, adapter: adapterInfo })}\\n`);\n process.exit(EXIT_OK);\n } catch (error) {\n const { code, message } = classifyError(error);\n process.stderr.write(`${message}\\n`);\n process.exit(code);\n }\n }\n\n const rawMessage = args.positional[0];\n if (rawMessage === undefined) {\n if (!process.stdin.isTTY) {\n process.stderr.write(\n 'No message provided and stdin is not a terminal. Pass a message, or \"-\" to read one from stdin.\\n'\n );\n process.exit(EXIT_USAGE);\n }\n const code = await runRepl(client, connection, args.agent, args.model);\n process.exitCode = code;\n return;\n }\n\n const message = rawMessage === '-' ? (await readStdin()).trim() : rawMessage;\n if (!message) {\n process.stderr.write('No message provided.\\n');\n process.exit(EXIT_USAGE);\n }\n\n // A broken pipe (e.g. `orbit-chat \"hi\" | head -1`) should exit quietly,\n // not throw an unhandled EPIPE error.\n process.stdout.on('error', (error) => {\n if ((error as NodeJS.ErrnoException).code === 'EPIPE') {\n process.exit(EXIT_OK);\n }\n });\n\n const controller = new AbortController();\n process.once('SIGINT', () => controller.abort());\n\n const code = await runOneShot(client, message, {\n skill: args.agent,\n model: args.model,\n signal: controller.signal,\n });\n // Set exitCode and let the event loop drain naturally rather than calling\n // process.exit(), which can terminate the process before buffered\n // stdout.write calls finish — truncating the response when it is large or\n // stdout is a pipe.\n process.exitCode = code;\n}\n\nmain().catch((error) => {\n process.stderr.write(`Unexpected error: ${error instanceof Error ? error.stack ?? error.message : String(error)}\\n`);\n process.exit(1);\n});\n","export interface ParsedArgs {\n url?: string;\n key?: string;\n health: boolean;\n version: boolean;\n agent?: string;\n model?: string;\n positional: string[];\n}\n\nexport class ArgsError extends Error {}\n\nconst FLAGS = new Set(['--url', '--key', '--health', '--agent', '--model', '--version', '-v']);\n\nfunction isFlagLike(value: string | undefined): boolean {\n return value === undefined || FLAGS.has(value);\n}\n\n/**\n * Minimal, dependency-free argv parser:\n * --url <url> | --key <key> | --health | --agent <name> | --model <id>\n * Anything else is collected as positional args, e.g. the one-shot message\n * in `orbit-chat \"question\"` or `orbit-chat -` (read stdin).\n *\n * A value-taking flag with no value, or one immediately followed by another\n * flag, is rejected rather than silently swallowing the next flag as its\n * value (e.g. `--url --health` must not disable --health).\n */\nexport function parseArgs(argv: string[]): ParsedArgs {\n const result: ParsedArgs = { health: false, version: false, positional: [] };\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n switch (arg) {\n case '--version':\n case '-v':\n result.version = true;\n break;\n case '--url': {\n const value = argv[i + 1];\n if (isFlagLike(value)) {\n throw new ArgsError(`--url requires a value`);\n }\n result.url = value;\n i++;\n break;\n }\n case '--key': {\n const value = argv[i + 1];\n if (isFlagLike(value)) {\n throw new ArgsError(`--key requires a value`);\n }\n result.key = value;\n i++;\n break;\n }\n case '--health':\n result.health = true;\n break;\n case '--agent': {\n const value = argv[i + 1];\n if (isFlagLike(value)) {\n throw new ArgsError(`--agent requires a value`);\n }\n result.agent = value;\n i++;\n break;\n }\n case '--model': {\n const value = argv[i + 1];\n if (isFlagLike(value)) {\n throw new ArgsError(`--model requires a value`);\n }\n result.model = value;\n i++;\n break;\n }\n default:\n result.positional.push(arg);\n }\n }\n\n return result;\n}\n","import * as readline from 'node:readline';\n\nconst ETX = '\\x03'; // Ctrl+C\nconst DEL = '\\x7f'; // Backspace (most terminals)\nconst BS = '\\x08'; // Backspace (some terminals)\n\n/** Prompt on stderr so stdout stays clean for piped/scripted output. */\nexport function ask(question: string): Promise<string> {\n const rl = readline.createInterface({ input: process.stdin, output: process.stderr });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\n/** Prompt for a secret without echoing it to the terminal. */\nexport function askSecret(question: string): Promise<string> {\n return new Promise((resolve) => {\n const stdin = process.stdin;\n process.stderr.write(question);\n\n const wasRaw = stdin.isTTY ? stdin.isRaw : false;\n if (stdin.isTTY) stdin.setRawMode(true);\n stdin.resume();\n stdin.setEncoding('utf8');\n\n let value = '';\n const cleanup = () => {\n stdin.removeListener('data', onData);\n if (stdin.isTTY) stdin.setRawMode(wasRaw);\n stdin.pause();\n };\n\n const onData = (chunk: string) => {\n for (const char of chunk) {\n if (char === '\\n' || char === '\\r') {\n cleanup();\n process.stderr.write('\\n');\n resolve(value.trim());\n return;\n } else if (char === ETX) {\n cleanup();\n process.stderr.write('\\n');\n process.exit(130);\n } else if (char === DEL || char === BS) {\n value = value.slice(0, -1);\n } else {\n value += char;\n }\n }\n };\n\n stdin.on('data', onData);\n });\n}\n","import type { ParsedArgs } from './args.js';\nimport { ask, askSecret } from './prompt.js';\n\nexport interface Connection {\n url: string;\n apiKey: string;\n}\n\nexport class ConfigError extends Error {}\n\n/** Assumed for a locally-run ORBIT server, same as most CLI tools default to localhost. */\nconst DEFAULT_URL = 'http://localhost:3000';\n\n/**\n * Resolve the server URL and API key.\n *\n * Order: CLI flags -> env vars -> `DEFAULT_URL` (or an interactive prompt\n * that itself defaults to it, on a TTY). Nothing is persisted to disk — each\n * run resolves its own connection.\n */\nexport async function resolveConnection(args: ParsedArgs): Promise<Connection> {\n let url = args.url ?? process.env.ORBIT_URL;\n let apiKey = args.key ?? process.env.ORBIT_API_KEY;\n\n if (!url) {\n if (!process.stdin.isTTY) {\n url = DEFAULT_URL;\n } else {\n const answer = await ask(`ORBIT server URL [${DEFAULT_URL}]: `);\n url = answer || DEFAULT_URL;\n }\n }\n\n if (!apiKey) {\n if (!process.stdin.isTTY) {\n throw new ConfigError(\n 'Missing API key. Pass --key <key> or set ORBIT_API_KEY (preferred over --key, which lands in shell history).'\n );\n }\n apiKey = await askSecret('ORBIT API key: ');\n }\n\n url = url.trim().replace(/\\/+$/, '');\n if (!url) {\n throw new ConfigError('Server URL cannot be empty.');\n }\n if (!apiKey.trim()) {\n throw new ConfigError('API key cannot be empty.');\n }\n\n return { url, apiKey: apiKey.trim() };\n}\n","import { ApiClient, type StreamResponse } from '@schmitech/chatbot-api';\nimport type { Connection } from './connection.js';\n\nexport function createClient(connection: Connection, sessionId?: string): ApiClient {\n return new ApiClient({\n apiUrl: connection.url,\n apiKey: connection.apiKey,\n sessionId: sessionId ?? null,\n });\n}\n\nexport interface ChatOptions {\n stream?: boolean;\n fileIds?: string[];\n threadId?: string;\n abortSignal?: AbortSignal;\n model?: string;\n skill?: string;\n}\n\n/**\n * Named-options wrapper around ApiClient.streamChat's 15-argument positional\n * signature. This is the only place the CLI touches that signature directly —\n * every caller elsewhere in the CLI goes through this function.\n */\nexport function streamChat(\n client: ApiClient,\n message: string,\n options: ChatOptions = {}\n): AsyncGenerator<StreamResponse> {\n return client.streamChat(\n message,\n options.stream ?? true,\n options.fileIds,\n options.threadId,\n undefined, // audioInput\n undefined, // audioFormat\n undefined, // language\n undefined, // returnAudio\n undefined, // ttsVoice\n undefined, // sourceLanguage\n undefined, // targetLanguage\n options.abortSignal,\n options.model,\n options.skill,\n undefined // regenerateOfMessageId\n );\n}\n","export const EXIT_OK = 0;\nexport const EXIT_SERVER_ERROR = 1;\nexport const EXIT_USAGE = 2;\nexport const EXIT_AUTH = 3;\nexport const EXIT_NETWORK = 4;\nexport const EXIT_CANCELLED = 130;\n","import { EXIT_AUTH, EXIT_NETWORK, EXIT_SERVER_ERROR } from './exit-codes.js';\n\n/**\n * Classify an error thrown by the SDK into one of the CLI's exit codes.\n *\n * The SDK (clients/node-api) wraps most failures in a plain Error whose\n * message embeds the HTTP status code, and its network-error detection\n * (\"Failed to fetch\") is written for browser fetch — Node's undici throws a\n * differently worded TypeError, so it is matched separately here.\n */\nexport function classifyError(error: unknown): { code: number; message: string } {\n const message = error instanceof Error ? error.message : String(error);\n\n if (/ECONNREFUSED|ENOTFOUND|EAI_AGAIN|fetch failed|Could not connect to the server/i.test(message)) {\n return { code: EXIT_NETWORK, message: 'Could not connect to the server. Check the URL and that it is running.' };\n }\n\n if (/:\\s*401\\b/.test(message) || /invalid|disabled|no associated adapter/i.test(message)) {\n return { code: EXIT_AUTH, message: 'API key is invalid, disabled, or lacks an adapter.' };\n }\n\n return { code: EXIT_SERVER_ERROR, message };\n}\n","import { readFile } from 'node:fs/promises';\nimport { basename, extname } from 'node:path';\nimport type { ApiClient } from '@schmitech/chatbot-api';\n\nconst MIME_TYPES: Record<string, string> = {\n '.txt': 'text/plain',\n '.md': 'text/markdown',\n '.json': 'application/json',\n '.csv': 'text/csv',\n '.pdf': 'application/pdf',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n};\n\nfunction mimeTypeFor(path: string): string {\n return MIME_TYPES[extname(path).toLowerCase()] ?? 'application/octet-stream';\n}\n\n/** Matches an `@path` token: `@` followed by non-whitespace. */\nconst ATTACHMENT_PATTERN = /@(\\S+)/g;\n\nexport class AttachmentError extends Error {}\n\n/**\n * Extract `@path` tokens from a message, upload each file, and return the\n * message with those tokens stripped plus the resulting file ids.\n *\n * Attachments are per-turn: the caller passes the returned file ids as this\n * turn's fileIds only, so they don't silently persist onto later turns.\n */\nexport async function extractAttachments(\n client: ApiClient,\n message: string\n): Promise<{ text: string; fileIds: string[] }> {\n const paths = [...message.matchAll(ATTACHMENT_PATTERN)].map((match) => match[1]);\n if (paths.length === 0) {\n return { text: message, fileIds: [] };\n }\n\n const fileIds: string[] = [];\n for (const path of paths) {\n let bytes: Buffer;\n try {\n bytes = await readFile(path);\n } catch {\n throw new AttachmentError(`Attachment not found: ${path}`);\n }\n const file = new File([bytes], basename(path), { type: mimeTypeFor(path) });\n const uploaded = await client.uploadFile(file);\n fileIds.push(uploaded.file_id);\n }\n\n const text = message.replace(ATTACHMENT_PATTERN, '').replace(/\\s+/g, ' ').trim();\n return { text: text || 'Please review the attached file(s).', fileIds };\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { randomUUID } from 'node:crypto';\nimport type { ApiClient, StreamResponse } from '@schmitech/chatbot-api';\n\nconst OUT_DIR = './.orbit-out';\n\n// The two shapes the SDK returns for generated media: inline base64 (with a\n// *_format field) and a persistent server-side URL that needs an\n// authenticated download via ApiClient.downloadArtifact().\nconst INLINE_FIELDS = [\n { data: 'image', format: 'image_format', kind: 'image' },\n { data: 'video', format: 'video_format', kind: 'video' },\n { data: 'document', format: 'document_format', kind: 'document' },\n] as const;\n\nconst URL_FIELDS = [\n { url: 'image_url', kind: 'image' },\n { url: 'video_url', kind: 'video' },\n { url: 'document_url', kind: 'document' },\n { url: 'generated_audio_url', kind: 'audio' },\n] as const;\n\nasync function writeArtifact(kind: string, format: string | undefined, bytes: Buffer): Promise<string> {\n await mkdir(OUT_DIR, { recursive: true });\n const ext = format ? `.${format}` : '';\n const path = `${OUT_DIR}/${kind}-${randomUUID()}${ext}`;\n await writeFile(path, bytes);\n return path;\n}\n\nfunction extFromContentType(contentType: string | null): string {\n if (!contentType) return '';\n const subtype = contentType.split('/')[1]?.split(';')[0];\n return subtype ? `.${subtype}` : '';\n}\n\n/**\n * Save any artifacts present on a stream chunk to ./.orbit-out/ and return\n * the paths written. Most chunks carry none of these fields; this is a\n * cheap no-op check for the common case.\n */\nexport async function saveArtifacts(client: ApiClient, chunk: StreamResponse): Promise<string[]> {\n const paths: string[] = [];\n const record = chunk as unknown as Record<string, string | undefined>;\n const savedKinds = new Set<string>();\n\n for (const { data, format, kind } of INLINE_FIELDS) {\n const base64 = record[data];\n if (base64) {\n paths.push(await writeArtifact(kind, record[format], Buffer.from(base64, 'base64')));\n savedKinds.add(kind);\n }\n }\n\n // The server can persist both an inline copy and a refreshable URL for the\n // same generated media on one chunk — save it once, preferring the inline\n // copy already handled above since it needs no extra download round-trip.\n for (const { url, kind } of URL_FIELDS) {\n if (savedKinds.has(kind)) continue;\n const value = record[url];\n if (value) {\n const { data, contentType } = await client.downloadArtifact(value);\n paths.push(await writeArtifact(kind, extFromContentType(contentType).replace(/^\\./, '') || undefined, Buffer.from(data)));\n }\n }\n\n return paths;\n}\n","import type { ApiClient } from '@schmitech/chatbot-api';\nimport { AttachmentError, extractAttachments } from './attachments.js';\nimport { saveArtifacts } from './artifacts.js';\nimport { streamChat, type ChatOptions } from './client.js';\nimport { classifyError } from './errors.js';\nimport { EXIT_CANCELLED, EXIT_OK, EXIT_USAGE } from './exit-codes.js';\n\n/**\n * Send one turn and stream the reply to stdout as plain text — no ANSI, no\n * progress noise. stdout carries the response only; errors, artifact\n * notices, and progress go to stderr. Returns the process exit code;\n * callers are responsible for process.exit.\n */\nexport async function runOneShot(\n client: ApiClient,\n rawMessage: string,\n options: ChatOptions & { signal?: AbortSignal }\n): Promise<number> {\n let message: string;\n let fileIds: string[];\n try {\n ({ text: message, fileIds } = await extractAttachments(client, rawMessage));\n } catch (error) {\n if (error instanceof AttachmentError) {\n process.stderr.write(`${error.message}\\n`);\n return EXIT_USAGE;\n }\n const { code, message: errorMessage } = classifyError(error);\n process.stderr.write(`${errorMessage}\\n`);\n return code;\n }\n\n try {\n for await (const chunk of streamChat(client, message, {\n ...options,\n fileIds,\n abortSignal: options.signal,\n })) {\n if (chunk.text) {\n process.stdout.write(chunk.text);\n }\n for (const path of await saveArtifacts(client, chunk)) {\n process.stderr.write(`Saved artifact: ${path}\\n`);\n }\n }\n process.stdout.write('\\n');\n return EXIT_OK;\n } catch (error) {\n if (options.signal?.aborted) {\n return EXIT_CANCELLED;\n }\n const { code, message: errorMessage } = classifyError(error);\n process.stderr.write(`${errorMessage}\\n`);\n return code;\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport * as fs from 'node:fs';\nimport type { ApiClient } from '@schmitech/chatbot-api';\nimport { findAgent, listAgents, type AgentChoice } from './agents.js';\nimport { extractAttachments, AttachmentError } from './attachments.js';\nimport { saveArtifacts } from './artifacts.js';\nimport { createClient, streamChat } from './client.js';\nimport type { Connection } from './connection.js';\nimport { classifyError } from './errors.js';\nimport { LineReader, type CommandSpec } from './input.js';\nimport { findModel, listModels } from './models.js';\nimport { MarkdownRenderer } from './markdown.js';\nimport { Spinner } from './spinner.js';\n\n/** Single source of truth for both `/help` text and the interactive `/` menu, so the two can't drift. */\nconst COMMANDS: CommandSpec[] = [\n { name: '/new', usage: '', description: 'start a new session (clears server-side context)' },\n { name: '/agents', usage: '<name|number>', description: 'pick an agent (arrow keys), or /agents <name|number>' },\n { name: '/model', usage: '<id|number>', description: 'pick a model (arrow keys), or /model <id|number>' },\n { name: '/key', usage: '', description: 'switch API key without restarting (masked prompt)' },\n { name: '/clear', usage: '', description: 'clear the screen and this session\\'s server-side history' },\n { name: '/help', usage: '', description: 'show this message' },\n { name: '/exit', usage: '', description: 'exit' },\n];\n\nconst HELP = `Commands:\n${COMMANDS.map((c) => ` ${(c.name + ' ' + c.usage).padEnd(21)}${c.description}`).join('\\n')}\n Ctrl+C cancel the in-flight turn; again (while idle) to exit\n Ctrl+R reverse search through this session's input history\n Ctrl+D exit\n@path/to/file inline in a message attaches that file to the turn.\nType / to see a filtered command menu (arrow keys + Tab/Enter to pick).\nAnything else is sent as a chat turn.`;\n\n/** Completes an `@path` token against the filesystem as the user types it. */\nfunction completer(line: string): [string[], string] {\n const match = /(?:^|\\s)@(\\S*)$/.exec(line);\n if (!match) {\n return [[], line];\n }\n\n const partial = match[1];\n const slash = partial.lastIndexOf('/');\n const dir = slash === -1 ? '.' : partial.slice(0, slash) || '/';\n const prefix = slash === -1 ? partial : partial.slice(slash + 1);\n const dirPrefix = slash === -1 ? '' : `${partial.slice(0, slash)}/`;\n\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return [[], partial];\n }\n\n const hits = entries\n .filter((entry) => entry.name.startsWith(prefix))\n .map((entry) => `${dirPrefix}${entry.name}${entry.isDirectory() ? '/' : ''}`);\n\n return [hits, partial];\n}\n\nconst CLEAR_HISTORY_TIMEOUT_MS = 1500;\n\n/**\n * Delete a session's server-side history without letting a stalled or\n * unreachable network hang the caller — the SDK call has no abort signal, so\n * this races it against a short timer instead of awaiting it directly. Used\n * by `/new`, `/clear`, and exit-time cleanup, so all three share one bound\n * rather than each risking drifting out of sync (or a hang) independently.\n *\n * `timedOut` tells the caller the request is being abandoned, not confirmed\n * done — e.g. exit-time cleanup uses that to decide whether it still needs\n * to force the process closed.\n */\nasync function clearHistoryBestEffort(\n client: ApiClient,\n sessionId: string\n): Promise<{ timedOut: boolean; error?: string }> {\n let timedOut = false;\n let error: string | undefined;\n await new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n timedOut = true;\n resolve();\n }, CLEAR_HISTORY_TIMEOUT_MS);\n client\n .clearConversationHistory(sessionId)\n .catch((err) => {\n error = classifyError(err).message;\n })\n .then(() => {\n clearTimeout(timer);\n resolve();\n });\n });\n return { timedOut, error };\n}\n\n/**\n * Interactive REPL: a prompt line plus streaming transcript. Input is a\n * hand-rolled raw-mode line editor (`input.ts`) rather than plain\n * `node:readline` — needed to render the `/` command menu and Ctrl+R\n * reverse-search overlay, neither of which readline's completer hook\n * supports. `input.ts` still exposes an async iterator, so the turn-handling\n * logic below is unchanged from the readline-based version.\n */\nexport async function runRepl(\n initialClient: ApiClient,\n connection: Connection,\n initialSkill: string | undefined,\n initialModel: string | undefined\n): Promise<number> {\n let client = initialClient;\n let ownAdapter;\n try {\n ownAdapter = await client.getAdapterInfo();\n } catch (error) {\n const { code, message } = classifyError(error);\n process.stderr.write(`${message}\\n`);\n return code;\n }\n\n let agents = await listAgents(client, ownAdapter);\n let currentAgent: AgentChoice = agents[0];\n if (initialSkill) {\n const found = findAgent(agents, initialSkill);\n if (found) {\n currentAgent = found;\n } else {\n process.stderr.write(`Unknown agent \"${initialSkill}\"; using the default adapter.\\n`);\n }\n }\n\n let currentModel = initialModel;\n let lastModels: Awaited<ReturnType<typeof listModels>> = [];\n\n let controller: AbortController | null = null;\n let currentRequestId: string | undefined;\n\n // Set whenever any clearHistoryBestEffort call times out (from /new,\n // /clear, or exit itself): that call's underlying fetch has no abort\n // signal, so it keeps running — and its open socket can keep the process\n // alive — well past the point our own await gave up on it. A /new whose\n // background cleanup already timed out earlier must not let a later,\n // unrelated fast exit-time cleanup return normally and leave that\n // dangling fetch as the reason the process never actually exits.\n let hasAbandonedCleanup = false;\n // A /new-triggered cleanup that hasn't settled yet (hasn't even hit its\n // own timeout) at the moment of exit is a second, distinct race:\n // `hasAbandonedCleanup` alone wouldn't catch it, since nothing has timed\n // out *yet* — it might resolve fine a moment later, or it might not. Exit\n // must not gamble on which; tracking it here means exit can force-close\n // rather than leave that outcome to chance.\n const pendingBackgroundCleanups = new Set<Promise<unknown>>();\n\n const rl = new LineReader({\n prompt: '> ',\n commands: COMMANDS,\n completer,\n onSigint: () => {\n if (controller) {\n controller.abort();\n const sessionId = client.getSessionId();\n if (sessionId && currentRequestId) {\n client.stopChat(sessionId, currentRequestId).catch(() => {});\n }\n process.stdout.write('\\n[cancelled]\\n');\n // The turn's catch block restores the prompt once the abort unwinds.\n } else {\n rl.requestExit();\n }\n },\n });\n\n process.stdout.write('Connected. Type /help for commands, Ctrl+D to exit.\\n');\n\n /**\n * Fetch the current agent's models and let the user pick one interactively.\n * Used by bare `/model` (no argument) — there used to be a separate\n * `/models` command for this, but once bare `/model` did the exact same\n * thing (rather than printing a static \"Current model: (adapter default)\"\n * that told the user nothing useful), the two were pure duplicates, so\n * `/models` was folded into `/model`.\n */\n async function pickModel(): Promise<void> {\n try {\n lastModels = await listModels(client, currentAgent.adapterName, currentAgent.skill);\n if (lastModels.length === 0) {\n process.stdout.write('This agent has no restricted model list (uses its configured default).\\n');\n return;\n }\n const idx = await rl.selectFromList(\n 'Select model (type to filter, Esc to cancel): ',\n lastModels.map((m) => {\n const id = m.name ?? m.id;\n return `${id}${id === currentModel ? ' (current)' : ''}`;\n })\n );\n if (idx !== null) {\n const picked = lastModels[idx];\n currentModel = picked.name ?? picked.id;\n process.stdout.write(`Model set to ${currentModel}.\\n`);\n }\n } catch (error) {\n const { message } = classifyError(error);\n process.stderr.write(`${message}\\n`);\n }\n }\n\n for await (const line of rl) {\n const text = line.trim();\n\n if (!text) {\n continue;\n }\n\n if (text.startsWith('/')) {\n const [command, ...rest] = text.split(/\\s+/);\n const arg = rest.join(' ');\n switch (command) {\n case '/new': {\n const oldSessionId = client.getSessionId();\n client.setSessionId(randomUUID());\n process.stdout.write('Started a new session.\\n');\n // Fire-and-forget: /new's whole point is switching immediately, so\n // it must not block the next prompt on a network round trip (an\n // unreachable server would otherwise freeze the REPL for the full\n // clearHistoryBestEffort timeout). Otherwise the abandoned\n // session's history sits in the database with nothing left\n // pointing at it — the same orphan this command's own description\n // promises not to leave behind — so still report it if cleanup\n // doesn't land, just asynchronously once it settles.\n if (oldSessionId) {\n const cleanup = clearHistoryBestEffort(client, oldSessionId)\n .then(({ timedOut, error }) => {\n if (timedOut) {\n hasAbandonedCleanup = true;\n }\n if (timedOut || error) {\n process.stderr.write(`Previous session's history was not cleared: ${error ?? 'timed out'}\\n`);\n }\n })\n .catch(() => {});\n pendingBackgroundCleanups.add(cleanup);\n cleanup.finally(() => pendingBackgroundCleanups.delete(cleanup));\n }\n break;\n }\n\n case '/agents':\n if (!arg) {\n const idx = await rl.selectFromList(\n 'Select agent (type to filter, Esc to cancel): ',\n agents.map((a) => `${a.label}${a === currentAgent ? ' (current)' : ''}`)\n );\n if (idx !== null) {\n currentAgent = agents[idx];\n currentModel = undefined;\n lastModels = [];\n process.stdout.write(`Switched to ${currentAgent.label}.\\n`);\n }\n } else {\n const found = findAgent(agents, arg);\n if (!found) {\n process.stdout.write(`No agent matches \"${arg}\". Try /agents to list them.\\n`);\n } else {\n currentAgent = found;\n currentModel = undefined;\n lastModels = [];\n process.stdout.write(`Switched to ${found.label}.\\n`);\n }\n }\n break;\n\n case '/model':\n if (!arg) {\n await pickModel();\n } else {\n const found = findModel(lastModels, arg);\n currentModel = found ? found.name ?? found.id : arg;\n process.stdout.write(`Model set to ${currentModel}.\\n`);\n }\n break;\n\n case '/key': {\n if (arg) {\n // The full \"/key <arg>\" line is already in scrollback and\n // history by the time this runs (LineReader.finish() prints and\n // records it before dispatch) — accepting the key here would\n // just launder an already-leaked secret. Bare `/key` instead\n // routes through the masked prompt below, which never echoes or\n // records what's typed.\n process.stdout.write(\n 'For your key\\'s safety, run /key with no argument — it will prompt you separately with input hidden.\\n'\n );\n break;\n }\n const newKey = await rl.readSecret('API key: ');\n if (newKey === null) {\n process.stdout.write('Cancelled.\\n');\n break;\n }\n if (!newKey.trim()) {\n process.stderr.write('API key cannot be empty.\\n');\n break;\n }\n const candidate = createClient(\n { url: connection.url, apiKey: newKey.trim() },\n client.getSessionId() ?? undefined\n );\n let newAdapter;\n try {\n newAdapter = await candidate.getAdapterInfo();\n } catch (error) {\n const { message } = classifyError(error);\n process.stderr.write(`Could not switch key: ${message}\\n`);\n break;\n }\n // A different key can map to a different adapter with its own\n // skills/models, so re-discover agents and drop the old\n // model/skill selection rather than carrying it over silently.\n client = candidate;\n ownAdapter = newAdapter;\n agents = await listAgents(client, ownAdapter);\n currentAgent = agents[0];\n currentModel = undefined;\n lastModels = [];\n process.stdout.write(`Switched API key. Now using ${currentAgent.label}.\\n`);\n break;\n }\n\n case '/clear': {\n // Best-effort: also delete the server-side conversation history for\n // this session (DELETE /admin/chat-history/{id}, ownership-checked\n // by API key server-side) so \"clear\" matches what it looks like —\n // a fresh conversation, not just a repainted terminal. A server\n // without a conversational adapter configured 503s here; that's\n // not a reason to skip the terminal clear itself. Bounded the same\n // way as /new and exit-time cleanup, so a stalled network can't\n // hang this command either.\n const sessionId = client.getSessionId();\n let historyError: string | undefined;\n if (sessionId) {\n const result = await clearHistoryBestEffort(client, sessionId);\n if (result.timedOut) {\n hasAbandonedCleanup = true;\n }\n historyError = result.timedOut ? 'timed out' : result.error;\n }\n // Home + erase screen + erase saved lines (the same sequence the\n // `clear` utility emits). Erasing only the visible screen (the\n // previous `cursorTo`/`clearScreenDown` pair) left the prior\n // conversation sitting in the terminal's scrollback just above the\n // now-blank viewport — visually it looked like the app had\n // \"scrolled away,\" reachable only via the scrollbar. Clearing\n // scrollback too means there's nothing left to scroll up to.\n process.stdout.write('\\x1b[H\\x1b[2J\\x1b[3J');\n if (historyError) {\n process.stderr.write(`Terminal cleared, but server-side history was not: ${historyError}\\n`);\n }\n break;\n }\n\n case '/help':\n process.stdout.write(`${HELP}\\n`);\n break;\n\n case '/exit':\n rl.requestExit();\n continue;\n\n default:\n process.stdout.write(`Unknown command: ${command} (try /help)\\n`);\n }\n continue;\n }\n\n let message: string;\n let fileIds: string[];\n try {\n ({ text: message, fileIds } = await extractAttachments(client, text));\n } catch (error) {\n if (error instanceof AttachmentError) {\n process.stderr.write(`${error.message}\\n`);\n } else {\n const { message: msg } = classifyError(error);\n process.stderr.write(`${msg}\\n`);\n }\n continue;\n }\n\n controller = new AbortController();\n currentRequestId = undefined;\n const renderer = new MarkdownRenderer();\n const spinner = new Spinner();\n spinner.start();\n\n try {\n for await (const chunk of streamChat(client, message, {\n skill: currentAgent.skill,\n model: currentModel,\n fileIds,\n abortSignal: controller.signal,\n })) {\n if (chunk.request_id) {\n currentRequestId = chunk.request_id;\n }\n if (chunk.text) {\n spinner.stop();\n renderer.push(chunk.text);\n }\n const artifactPaths = await saveArtifacts(client, chunk);\n if (artifactPaths.length > 0) {\n spinner.stop();\n // The buffered trailing partial line (typically the final chunk's\n // text, with no newline yet) must land before the notice, or it\n // reads as if the artifact arrived before the reply that mentions it.\n renderer.flush();\n for (const path of artifactPaths) {\n process.stdout.write(`Saved artifact: ${path}\\n`);\n }\n }\n }\n spinner.stop();\n renderer.flush();\n process.stdout.write('\\n');\n } catch (error) {\n spinner.stop();\n renderer.flush();\n if (!controller.signal.aborted) {\n const { message: msg } = classifyError(error);\n process.stderr.write(`${msg}\\n`);\n }\n } finally {\n spinner.stop();\n controller = null;\n currentRequestId = undefined;\n }\n }\n\n process.stdout.write('\\n');\n\n // The loop above ends on every exit path (/exit, Ctrl+D, idle Ctrl+C), so\n // this one spot covers all of them. Best-effort: an unreachable server or\n // a deployment without a conversational adapter (503) shouldn't block or\n // spam an error on the way out — but leaving the row behind would orphan\n // this session's history in the database with nothing left to ever clear it.\n //\n // clearHistoryBestEffort bounds the SDK call, which has no abort signal of\n // its own, against a short timer instead of an OS-level TCP timeout. The\n // two outcomes are handled differently: if cleanup wins, its request is\n // done and there's nothing left dangling, so this falls through to the\n // normal `return 0` below — which matters when stdout/stderr are\n // redirected to a file or pipe, since only the normal return path lets\n // that output drain before the process exits. Only a timeout — this call's\n // own, or an earlier /new's fire-and-forget one that never got the chance\n // to force-exit itself (it isn't the one deciding whether the REPL is\n // exiting) — force-exits: at that point some request is being abandoned\n // outright, its fetch potentially still open, and there is no other way to\n // stop waiting on it.\n const sessionId = client.getSessionId();\n if (sessionId) {\n const { timedOut } = await clearHistoryBestEffort(client, sessionId);\n if (timedOut) {\n hasAbandonedCleanup = true;\n }\n }\n\n // A /new-triggered cleanup can still be mid-flight here, not yet past its\n // own timeout — indistinguishable at this instant from one that's about to\n // succeed. Exiting normally would gamble the process's shutdown on which;\n // its still-open fetch either way is reason enough to force-close rather\n // than wait and find out.\n if (hasAbandonedCleanup || pendingBackgroundCleanups.size > 0) {\n process.exit(0);\n }\n\n return 0;\n}\n","import type { AdapterInfo, ApiClient } from '@schmitech/chatbot-api';\n\nexport interface AgentChoice {\n label: string;\n /** undefined selects the key's own adapter (the default) rather than a skill. */\n skill: string | undefined;\n adapterName: string;\n}\n\n/**\n * List the key's own adapter plus every enabled skill actually available to\n * it, per the roadmap's `/agents` design: the chat adapter is fixed by the\n * API key, but a skill re-targets a turn to that skill's own adapter (see\n * docs/roadmap/orbit-cli-repl.md's \"`/agents` — what it can and cannot do\").\n *\n * `getAllSkills()` is global — every skill registered anywhere on the\n * server, regardless of the calling key. Offering all of them let a turn\n * \"succeed\" at switching agents and then fail server-side on the next\n * message (\"Skill 'Video' is not available for adapter 'simple-chat'\").\n * `getAdapterSkills()` is the key-scoped counterpart (same endpoint\n * OrbitChat's `useSkills` hook calls): it returns the current adapter's\n * `available_skills` allowlist, which we intersect with `getAllSkills()`'s\n * metadata (name/description) the same way OrbitChat does. Skill discovery\n * is best-effort — a server with none registered, or a failing call (either\n * call, since an unfiltered list would just reintroduce the bug this\n * intersection exists to prevent), just leaves the single default adapter.\n */\nexport async function listAgents(client: ApiClient, ownAdapter: AdapterInfo): Promise<AgentChoice[]> {\n const choices: AgentChoice[] = [\n { label: `${ownAdapter.client_name} (default)`, skill: undefined, adapterName: ownAdapter.adapter_name },\n ];\n\n try {\n const [{ skills }, { available_skills }] = await Promise.all([\n client.getAllSkills(),\n client.getAdapterSkills(ownAdapter.adapter_name),\n ]);\n const allowed = new Set(available_skills);\n for (const skill of skills.filter((s) => s.enabled && allowed.has(s.name))) {\n choices.push({\n label: `${skill.name} — ${skill.description}`,\n skill: skill.name,\n adapterName: skill.adapter_name,\n });\n }\n } catch {\n // No skills registered, or discovery failed — single-agent is fine.\n }\n\n return choices;\n}\n\nexport function findAgent(agents: AgentChoice[], query: string): AgentChoice | undefined {\n const index = Number(query);\n if (Number.isInteger(index) && index >= 1 && index <= agents.length) {\n return agents[index - 1];\n }\n return agents.find((a) => a.skill?.toLowerCase() === query.toLowerCase());\n}\n","import * as readline from 'node:readline';\nimport { ansiEnabled } from './tty.js';\n\nconst RESET = '\\x1b[0m';\nconst DIM = '\\x1b[2m';\nconst INVERT = '\\x1b[7m';\nconst CTRL_C = '\\x03';\n\nexport interface CommandSpec {\n name: string;\n usage: string;\n description: string;\n}\n\ntype Completer = (line: string) => [string[], string];\n\nexport interface LineReaderOptions {\n prompt: string;\n commands: CommandSpec[];\n completer: Completer;\n /** Called on Ctrl+C outside of reverse-search; caller decides abort-turn vs. exit-REPL. */\n onSigint: () => void;\n}\n\n// eslint-disable-next-line no-control-regex -- matches the ANSI escape byte itself, to strip color codes before measuring display width.\nconst ANSI_PATTERN = /\\x1b\\[[0-9;]*m/g;\n\nfunction isWideCodePoint(cp: number): boolean {\n return (\n (cp >= 0x1100 && cp <= 0x115f) || // Hangul Jamo\n cp === 0x2329 ||\n cp === 0x232a ||\n (cp >= 0x2e80 && cp <= 0x303e) || // CJK Radicals..CJK Symbols\n (cp >= 0x3041 && cp <= 0x33ff) || // Hiragana..CJK Compatibility\n (cp >= 0x3400 && cp <= 0x4dbf) || // CJK Extension A\n (cp >= 0x4e00 && cp <= 0x9fff) || // CJK Unified Ideographs\n (cp >= 0xa000 && cp <= 0xa4cf) || // Yi\n (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables\n (cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs\n (cp >= 0xfe30 && cp <= 0xfe4f) || // CJK Compatibility Forms\n (cp >= 0xff00 && cp <= 0xff60) || // Fullwidth Forms\n (cp >= 0xffe0 && cp <= 0xffe6) ||\n (cp >= 0x1f300 && cp <= 0x1faff) || // emoji blocks (approximate)\n (cp >= 0x20000 && cp <= 0x3fffd) // CJK Extension B and beyond\n );\n}\n\nfunction isZeroWidthCodePoint(cp: number): boolean {\n return (\n cp === 0x200b || // zero width space\n cp === 0x200c ||\n cp === 0x200d || // zero width joiner\n cp === 0xfeff ||\n (cp >= 0x0300 && cp <= 0x036f) || // combining diacritics\n (cp >= 0x1ab0 && cp <= 0x1aff) ||\n (cp >= 0x1dc0 && cp <= 0x1dff) ||\n (cp >= 0x20d0 && cp <= 0x20ff) ||\n (cp >= 0xfe00 && cp <= 0xfe0f) // variation selectors\n );\n}\n\n/** Terminal column width of a string: strips ANSI escapes, counts wide/full-width code points as 2 columns and zero-width marks as 0. */\nfunction displayWidth(str: string): number {\n const clean = str.replace(ANSI_PATTERN, '');\n let width = 0;\n for (const ch of clean) {\n const cp = ch.codePointAt(0) ?? 0;\n if (isZeroWidthCodePoint(cp)) continue;\n width += isWideCodePoint(cp) ? 2 : 1;\n }\n return width;\n}\n\n/** Terminal rows a string wraps into at the given column width. */\nfunction rowsFor(text: string, width: number): number {\n return Math.max(1, Math.ceil(displayWidth(text) / width) || 1);\n}\n\nconst graphemeSegmenter =\n typeof Intl !== 'undefined' && 'Segmenter' in Intl ? new Intl.Segmenter(undefined, { granularity: 'grapheme' }) : null;\n\n/** Split a string into grapheme clusters (falling back to code points) so cursor movement never lands inside a surrogate pair or a combined emoji. */\nfunction graphemes(str: string): string[] {\n if (graphemeSegmenter) {\n return Array.from(graphemeSegmenter.segment(str), (s) => s.segment);\n }\n return Array.from(str);\n}\n\nfunction commonPrefix(strings: string[]): string {\n if (strings.length === 0) return '';\n let prefix = strings[0];\n for (const s of strings.slice(1)) {\n let i = 0;\n while (i < prefix.length && i < s.length && prefix[i] === s[i]) i++;\n prefix = prefix.slice(0, i);\n }\n return prefix;\n}\n\n/**\n * Hand-rolled raw-mode line editor, replacing `node:readline`'s line editing\n * so the REPL can render UI that readline has no hook for: a live `/`\n * command dropdown and a Ctrl+R reverse-search overlay. Modeled on the\n * masked-input loop `prompt.ts::askSecret()` already uses, extended with\n * cursor movement, in-memory history, and the two overlays above.\n */\nexport class LineReader {\n private readonly prompt: string;\n private readonly commands: CommandSpec[];\n private readonly completer: Completer;\n private readonly onSigint: () => void;\n private readonly stdin = process.stdin;\n private readonly history: string[] = [];\n\n private buffer = '';\n private cursor = 0;\n private historyIndex = 0;\n\n private menuVisible = false;\n private menuIndex = 0;\n private menuItems: CommandSpec[] = [];\n\n private searchMode = false;\n private searchQuery = '';\n private searchMatch: string | undefined;\n private searchScanIndex = 0;\n\n private secretMode = false;\n private secretPrompt = '';\n private secretBuffer = '';\n private resolveSecret: ((value: string | null) => void) | null = null;\n\n private listMode = false;\n private listPrompt = '';\n private listQuery = '';\n private listAllItems: string[] = [];\n private listFiltered: number[] = [];\n private listIndex = 0;\n private resolveList: ((index: number | null) => void) | null = null;\n\n private resolveLine: ((line: string | null) => void) | null = null;\n private closed = false;\n private wasRaw = false;\n /** Row offset (relative to the input's first row) the cursor was left on by the last render, for wrapped-line redraw bookkeeping. */\n private lastCursorRow = 0;\n\n constructor(options: LineReaderOptions) {\n this.prompt = options.prompt;\n this.commands = options.commands;\n this.completer = options.completer;\n this.onSigint = options.onSigint;\n\n if (this.stdin.isTTY) {\n readline.emitKeypressEvents(this.stdin);\n this.wasRaw = this.stdin.isRaw;\n this.stdin.setRawMode(true);\n }\n this.stdin.resume();\n this.stdin.setEncoding('utf8');\n this.stdin.on('keypress', this.onKeypress);\n }\n\n async *[Symbol.asyncIterator](): AsyncGenerator<string> {\n while (!this.closed) {\n const line = await this.readLine();\n if (line === null) {\n return;\n }\n yield line;\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.stdin.removeListener('keypress', this.onKeypress);\n if (this.stdin.isTTY) {\n this.stdin.setRawMode(this.wasRaw);\n }\n // The constructor calls stdin.resume() to receive keypress events; a\n // resumed, flowing stdin keeps the event loop alive on its own, so\n // without pausing it here the process never exits after /exit or Ctrl+D.\n this.stdin.pause();\n }\n\n /**\n * `cursor` counts grapheme clusters, not UTF-16 code units — a `cursor--`\n * next to an emoji or a surrogate-pair character must skip the whole\n * cluster, not land inside it. This converts that cluster count to the\n * actual string offset needed for `buffer.slice(...)`.\n */\n private cursorStringIndex(): number {\n return graphemes(this.buffer).slice(0, this.cursor).join('').length;\n }\n\n /**\n * Insert `text` at string offset `idx` and recompute the cursor by\n * re-segmenting the resulting prefix, rather than adding `graphemes(text)`\n * to the old cursor. The terminal delivers a multi-codepoint emoji (e.g. a\n * ZWJ family sequence) as separate keypress events one codepoint at a\n * time; segmented alone, a joiner counts as its own cluster, but Unicode's\n * grapheme rules attach it to whatever already precedes it in the buffer.\n * Re-segmenting the real prefix after each keystroke keeps the cursor's\n * cluster count correct instead of drifting ahead of the buffer's actual\n * cluster count (which crashes the next backspace/delete on an\n * out-of-range index).\n */\n private insertAt(idx: number, text: string): void {\n this.buffer = this.buffer.slice(0, idx) + text + this.buffer.slice(idx);\n this.cursor = graphemes(this.buffer.slice(0, idx + text.length)).length;\n }\n\n /**\n * Read one line of masked input (e.g. an API key) without it ever being\n * echoed, written to terminal scrollback, or added to line history — only\n * a `*` per typed character is shown, same masking `prompt.ts::askSecret()`\n * uses for the initial connection prompt. Ctrl+C cancels and resolves\n * `null` instead of exiting the process, since a REPL command is in\n * progress rather than startup.\n */\n readSecret(promptText: string): Promise<string | null> {\n this.secretMode = true;\n this.secretPrompt = promptText;\n this.secretBuffer = '';\n this.lastCursorRow = 0;\n this.renderSecret();\n return new Promise((resolve) => {\n this.resolveSecret = resolve;\n });\n }\n\n /**\n * Present `items` as an arrow-key-navigable list (type to filter, like the\n * `/` command menu) and resolve the chosen index, or `null` on Esc/Ctrl+C.\n * Used for `/agents` and `/models` so picking one doesn't require typing\n * its number or name.\n */\n selectFromList(promptText: string, items: string[]): Promise<number | null> {\n this.listMode = true;\n this.listPrompt = promptText;\n this.listQuery = '';\n this.listAllItems = items;\n this.listFiltered = items.map((_, i) => i);\n this.listIndex = 0;\n this.lastCursorRow = 0;\n this.renderList();\n return new Promise((resolve) => {\n this.resolveList = resolve;\n });\n }\n\n private readLine(): Promise<string | null> {\n this.buffer = '';\n this.cursor = 0;\n this.historyIndex = this.history.length;\n this.menuVisible = false;\n this.searchMode = false;\n this.secretMode = false;\n this.listMode = false;\n this.lastCursorRow = 0;\n this.render();\n return new Promise((resolve) => {\n this.resolveLine = resolve;\n });\n }\n\n private onKeypress = (str: string | undefined, key: readline.Key): void => {\n if (this.closed) return;\n\n const isCtrlC = key.sequence === CTRL_C || (key.ctrl && key.name === 'c');\n if (isCtrlC) {\n if (this.secretMode) {\n this.finishSecret(null);\n return;\n }\n if (this.listMode) {\n this.finishList(null);\n return;\n }\n if (this.searchMode) {\n this.exitSearch();\n this.render();\n return;\n }\n this.onSigint();\n return;\n }\n\n if (this.secretMode) {\n this.handleSecretKey(str, key);\n return;\n }\n\n if (this.listMode) {\n this.handleListKey(str, key);\n return;\n }\n\n // Between finish() clearing resolveLine and the next readLine() call (e.g.\n // while a chat turn is streaming), there is no buffer to edit — only\n // cancellation (handled above) is meaningful here. Anything else would\n // corrupt the streamed output and be silently discarded on the next read.\n if (!this.resolveLine) {\n return;\n }\n\n if (this.searchMode) {\n this.handleSearchKey(str, key);\n return;\n }\n\n if (key.ctrl && key.name === 'r') {\n this.searchMode = true;\n this.searchQuery = '';\n this.searchMatch = undefined;\n this.searchScanIndex = this.history.length;\n this.render();\n return;\n }\n\n if (key.ctrl && key.name === 'd') {\n if (this.buffer.length === 0) {\n this.finish(null);\n }\n return;\n }\n\n switch (key.name) {\n case 'return':\n if (this.menuVisible && this.menuItems.length > 0) {\n this.selectMenuItemOnEnter();\n return;\n }\n this.submit();\n return;\n case 'backspace': {\n if (this.cursor > 0) {\n const segs = graphemes(this.buffer);\n const idx = segs.slice(0, this.cursor - 1).join('').length;\n const removed = segs[this.cursor - 1];\n this.buffer = this.buffer.slice(0, idx) + this.buffer.slice(idx + removed.length);\n this.cursor--;\n }\n break;\n }\n case 'delete': {\n const segs = graphemes(this.buffer);\n if (this.cursor < segs.length) {\n const idx = segs.slice(0, this.cursor).join('').length;\n const removed = segs[this.cursor];\n this.buffer = this.buffer.slice(0, idx) + this.buffer.slice(idx + removed.length);\n }\n break;\n }\n case 'left':\n if (this.cursor > 0) this.cursor--;\n break;\n case 'right':\n if (this.cursor < graphemes(this.buffer).length) this.cursor++;\n break;\n case 'up':\n if (this.menuVisible && this.menuItems.length > 0) {\n this.menuIndex = (this.menuIndex - 1 + this.menuItems.length) % this.menuItems.length;\n } else {\n this.historyUp();\n }\n break;\n case 'down':\n if (this.menuVisible && this.menuItems.length > 0) {\n this.menuIndex = (this.menuIndex + 1) % this.menuItems.length;\n } else {\n this.historyDown();\n }\n break;\n case 'tab':\n if (this.menuVisible && this.menuItems.length > 0) {\n this.acceptMenuSelection();\n } else {\n this.applyCompletion();\n }\n break;\n case 'escape':\n // Dismiss without calling updateMenu() below — the buffer is\n // unchanged by Esc, so it would still match `/...` and immediately\n // reopen the menu it was just told to close.\n this.menuVisible = false;\n this.render();\n return;\n default:\n if (str && !key.ctrl && !key.meta && str >= ' ') {\n this.insertAt(this.cursorStringIndex(), str);\n }\n }\n\n this.updateMenu();\n this.render();\n };\n\n private historyUp(): void {\n if (this.historyIndex > 0) {\n this.historyIndex--;\n this.buffer = this.history[this.historyIndex];\n this.cursor = graphemes(this.buffer).length;\n }\n }\n\n private historyDown(): void {\n if (this.historyIndex < this.history.length - 1) {\n this.historyIndex++;\n this.buffer = this.history[this.historyIndex];\n this.cursor = graphemes(this.buffer).length;\n } else {\n this.historyIndex = this.history.length;\n this.buffer = '';\n this.cursor = 0;\n }\n }\n\n private updateMenu(): void {\n const match = /^\\/(\\S*)$/.exec(this.buffer);\n if (!match) {\n this.menuVisible = false;\n return;\n }\n const query = match[1].toLowerCase();\n const items = this.commands.filter((c) => c.name.slice(1).toLowerCase().startsWith(query));\n if (items.length === 0) {\n this.menuVisible = false;\n return;\n }\n this.menuItems = items;\n if (this.menuIndex >= items.length) {\n this.menuIndex = 0;\n }\n this.menuVisible = true;\n }\n\n private acceptMenuSelection(): void {\n const item = this.menuItems[this.menuIndex];\n if (!item) return;\n this.buffer = `${item.name} `;\n this.cursor = graphemes(this.buffer).length;\n this.menuVisible = false;\n }\n\n /**\n * Enter on a visible menu runs the highlighted command immediately —\n * whether or not it takes an optional argument (e.g. `/agents\n * <name|number>` with no argument opens its own interactive picker; there\n * is no dedicated \"no-argument\" case to special-case around). A command\n * that does take an argument can still be filled into the buffer without\n * running it via Tab, for typing that argument manually before Enter.\n */\n private selectMenuItemOnEnter(): void {\n const item = this.menuItems[this.menuIndex];\n if (!item) return;\n this.menuVisible = false;\n this.buffer = item.name;\n this.cursor = graphemes(this.buffer).length;\n this.submit();\n }\n\n private applyCompletion(): void {\n const idx = this.cursorStringIndex();\n const [hits, partial] = this.completer(this.buffer.slice(0, idx));\n if (hits.length === 1) {\n this.insertAt(idx, hits[0].slice(partial.length));\n } else if (hits.length > 1) {\n const common = commonPrefix(hits);\n if (common.length > partial.length) {\n this.insertAt(idx, common.slice(partial.length));\n }\n }\n }\n\n private handleSearchKey(str: string | undefined, key: readline.Key): void {\n if (key.name === 'return') {\n this.buffer = this.searchMatch ?? this.searchQuery;\n this.cursor = graphemes(this.buffer).length;\n this.exitSearch();\n this.submit();\n return;\n }\n if (key.name === 'escape' || (key.ctrl && key.name === 'g')) {\n this.exitSearch();\n this.render();\n return;\n }\n if (key.ctrl && key.name === 'r') {\n this.findSearchMatch(true);\n this.render();\n return;\n }\n if (key.name === 'backspace') {\n this.searchQuery = graphemes(this.searchQuery).slice(0, -1).join('');\n this.searchScanIndex = this.history.length;\n this.findSearchMatch(false);\n this.render();\n return;\n }\n if (str && !key.ctrl && !key.meta && str >= ' ') {\n this.searchQuery += str;\n this.searchScanIndex = this.history.length;\n this.findSearchMatch(false);\n }\n this.render();\n }\n\n private handleSecretKey(str: string | undefined, key: readline.Key): void {\n if (key.name === 'return') {\n this.finishSecret(this.secretBuffer);\n return;\n }\n if (key.name === 'backspace') {\n this.secretBuffer = graphemes(this.secretBuffer).slice(0, -1).join('');\n this.renderSecret();\n return;\n }\n if (key.name === 'escape') {\n this.finishSecret(null);\n return;\n }\n if (str && !key.ctrl && !key.meta && str >= ' ') {\n this.secretBuffer += str;\n this.renderSecret();\n }\n }\n\n private renderSecret(): void {\n if (this.lastCursorRow > 0) {\n readline.moveCursor(process.stdout, 0, -this.lastCursorRow);\n }\n readline.cursorTo(process.stdout, 0);\n readline.clearScreenDown(process.stdout);\n const masked = '*'.repeat(graphemes(this.secretBuffer).length);\n const text = this.secretPrompt + masked;\n process.stdout.write(text);\n const width = process.stdout.columns || 80;\n this.lastCursorRow = rowsFor(text, width) - 1;\n }\n\n private finishSecret(value: string | null): void {\n this.secretMode = false;\n if (this.lastCursorRow > 0) {\n readline.moveCursor(process.stdout, 0, -this.lastCursorRow);\n }\n readline.cursorTo(process.stdout, 0);\n readline.clearScreenDown(process.stdout);\n process.stdout.write('\\n');\n this.lastCursorRow = 0;\n const resolve = this.resolveSecret;\n this.resolveSecret = null;\n this.secretBuffer = '';\n resolve?.(value);\n }\n\n private handleListKey(str: string | undefined, key: readline.Key): void {\n if (key.name === 'return') {\n const idx = this.listFiltered[this.listIndex];\n this.finishList(idx === undefined ? null : idx);\n return;\n }\n if (key.name === 'escape') {\n this.finishList(null);\n return;\n }\n if (key.name === 'up') {\n if (this.listFiltered.length > 0) {\n this.listIndex = (this.listIndex - 1 + this.listFiltered.length) % this.listFiltered.length;\n }\n this.renderList();\n return;\n }\n if (key.name === 'down') {\n if (this.listFiltered.length > 0) {\n this.listIndex = (this.listIndex + 1) % this.listFiltered.length;\n }\n this.renderList();\n return;\n }\n if (key.name === 'backspace') {\n this.listQuery = graphemes(this.listQuery).slice(0, -1).join('');\n this.updateListFilter();\n this.renderList();\n return;\n }\n if (str && !key.ctrl && !key.meta && str >= ' ') {\n this.listQuery += str;\n this.updateListFilter();\n }\n this.renderList();\n }\n\n private updateListFilter(): void {\n const query = this.listQuery.toLowerCase();\n this.listFiltered = this.listAllItems\n .map((label, i) => ({ label, i }))\n .filter(({ label }) => label.toLowerCase().includes(query))\n .map(({ i }) => i);\n if (this.listIndex >= this.listFiltered.length) {\n this.listIndex = 0;\n }\n }\n\n private renderList(): void {\n const width = process.stdout.columns || 80;\n if (this.lastCursorRow > 0) {\n readline.moveCursor(process.stdout, 0, -this.lastCursorRow);\n }\n readline.cursorTo(process.stdout, 0);\n readline.clearScreenDown(process.stdout);\n const color = ansiEnabled();\n\n const headerText = `${this.listPrompt}${this.listQuery}`;\n process.stdout.write(headerText);\n\n let listRows = 0;\n if (this.listFiltered.length === 0) {\n const text = `${color ? DIM : ''} (no matches)${color ? RESET : ''}`;\n process.stdout.write(`\\n${text}`);\n listRows += rowsFor(text, width);\n } else {\n for (const [pos, idx] of this.listFiltered.entries()) {\n const selected = pos === this.listIndex;\n const marker = selected ? '›' : ' ';\n const text = `${marker} ${this.listAllItems[idx]}`;\n const rendered = `${selected && color ? INVERT : ''}${text}${selected && color ? RESET : ''}`;\n process.stdout.write(`\\n${rendered}`);\n listRows += rowsFor(rendered, width);\n }\n }\n\n // Same wrapped-row bookkeeping as the main render(): walk back up to the\n // header's first row, then back down to where the header text (which can\n // itself wrap) actually leaves the cursor.\n const headerRows = rowsFor(headerText, width);\n const rowsBelowFirst = headerRows - 1 + listRows;\n if (rowsBelowFirst > 0) {\n readline.moveCursor(process.stdout, 0, -rowsBelowFirst);\n }\n\n const cursorAbsolute = displayWidth(headerText);\n const cursorRow = Math.floor(cursorAbsolute / width);\n const cursorCol = cursorAbsolute % width;\n if (cursorRow > 0) {\n readline.moveCursor(process.stdout, 0, cursorRow);\n }\n readline.cursorTo(process.stdout, cursorCol);\n this.lastCursorRow = cursorRow;\n }\n\n private finishList(index: number | null): void {\n this.listMode = false;\n if (this.lastCursorRow > 0) {\n readline.moveCursor(process.stdout, 0, -this.lastCursorRow);\n }\n readline.cursorTo(process.stdout, 0);\n readline.clearScreenDown(process.stdout);\n this.lastCursorRow = 0;\n const resolve = this.resolveList;\n this.resolveList = null;\n resolve?.(index);\n }\n\n private findSearchMatch(next: boolean): void {\n if (!this.searchQuery) {\n this.searchMatch = undefined;\n return;\n }\n const start = next ? this.searchScanIndex - 1 : this.history.length - 1;\n for (let i = start; i >= 0; i--) {\n if (this.history[i].includes(this.searchQuery)) {\n this.searchMatch = this.history[i];\n this.searchScanIndex = i;\n return;\n }\n }\n this.searchMatch = undefined;\n }\n\n private exitSearch(): void {\n this.searchMode = false;\n this.searchQuery = '';\n this.searchMatch = undefined;\n }\n\n private submit(): void {\n const line = this.buffer;\n if (line.trim().length > 0) {\n this.history.push(line);\n }\n this.finish(line);\n }\n\n private finish(line: string | null): void {\n this.menuVisible = false;\n if (this.lastCursorRow > 0) {\n readline.moveCursor(process.stdout, 0, -this.lastCursorRow);\n }\n readline.cursorTo(process.stdout, 0);\n readline.clearScreenDown(process.stdout);\n process.stdout.write(line !== null ? `${this.prompt}${line}\\n` : '\\n');\n this.lastCursorRow = 0;\n const resolve = this.resolveLine;\n this.resolveLine = null;\n if (line === null) {\n this.close();\n }\n resolve?.(line);\n }\n\n /** Force the in-flight read to end as EOF, e.g. on an idle Ctrl+C. */\n requestExit(): void {\n if (this.searchMode) {\n this.exitSearch();\n }\n this.finish(null);\n }\n\n private render(): void {\n const width = process.stdout.columns || 80;\n\n // The previous render left the cursor `lastCursorRow` rows below the\n // input's first row (input can wrap past one terminal row). Walk back up\n // to that first row before clearing, or a long line's earlier rows are\n // left stale on screen.\n if (this.lastCursorRow > 0) {\n readline.moveCursor(process.stdout, 0, -this.lastCursorRow);\n }\n readline.cursorTo(process.stdout, 0);\n readline.clearScreenDown(process.stdout);\n const color = ansiEnabled();\n\n if (this.searchMode) {\n const label = `(reverse-i-search)\\`${this.searchQuery}': `;\n const text = label + (this.searchMatch ?? '');\n process.stdout.write(text);\n this.lastCursorRow = rowsFor(text, width) - 1;\n return;\n }\n\n const inputText = this.prompt + this.buffer;\n process.stdout.write(inputText);\n\n // Rows each rendered menu entry wraps into (a long description, or wide\n // characters, can push a single entry past one row) — summed, not one\n // row per item, or a narrow terminal misplaces the redraw origin.\n let menuRows = 0;\n if (this.menuVisible) {\n for (const [i, item] of this.menuItems.entries()) {\n const selected = i === this.menuIndex;\n const marker = selected ? '›' : ' ';\n const usage = item.usage ? ` ${item.usage}` : '';\n const text = `${marker} ${item.name}${usage} ${color ? DIM : ''}— ${item.description}${color ? RESET : ''}`;\n const rendered = `${selected && color ? INVERT : ''}${text}${selected && color ? RESET : ''}`;\n process.stdout.write(`\\n${rendered}`);\n menuRows += rowsFor(rendered, width);\n }\n }\n\n // Rows the input line itself wraps into, plus the menu rows below it —\n // together, how far below the input's first row the cursor now sits.\n const inputRows = rowsFor(inputText, width);\n const rowsBelowFirst = inputRows - 1 + menuRows;\n if (rowsBelowFirst > 0) {\n readline.moveCursor(process.stdout, 0, -rowsBelowFirst);\n }\n\n const cursorAbsolute = displayWidth(this.prompt) + displayWidth(this.buffer.slice(0, this.cursorStringIndex()));\n const cursorRow = Math.floor(cursorAbsolute / width);\n const cursorCol = cursorAbsolute % width;\n if (cursorRow > 0) {\n readline.moveCursor(process.stdout, 0, cursorRow);\n }\n readline.cursorTo(process.stdout, cursorCol);\n this.lastCursorRow = cursorRow;\n }\n}\n","/** True when stdout is a real terminal and the user hasn't opted out via `NO_COLOR`. */\nexport function ansiEnabled(): boolean {\n return Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;\n}\n","import type { AdapterModelInfo, ApiClient } from '@schmitech/chatbot-api';\n\n/**\n * List the models allowed for the currently active agent.\n *\n * The server resolves models for the API key's own adapter regardless of\n * `adapterName` unless `skill` is also passed — so a selected skill must be\n * threaded through here, not just its backing adapter name, or this quietly\n * falls back to the base adapter's models.\n */\nexport async function listModels(\n client: ApiClient,\n adapterName: string,\n skill: string | undefined\n): Promise<AdapterModelInfo[]> {\n const { models } = await client.getAdapterModels(adapterName, skill);\n return models;\n}\n\nexport function findModel(models: AdapterModelInfo[], query: string): AdapterModelInfo | undefined {\n const index = Number(query);\n if (Number.isInteger(index) && index >= 1 && index <= models.length) {\n return models[index - 1];\n }\n return models.find((m) => m.name?.toLowerCase() === query.toLowerCase() || m.id?.toLowerCase() === query.toLowerCase());\n}\n","import { highlight, supportsLanguage } from 'cli-highlight';\nimport { ansiEnabled } from './tty.js';\n\nconst RESET = '\\x1b[0m';\nconst BOLD = '\\x1b[1m';\nconst DIM = '\\x1b[2m';\nconst ITALIC = '\\x1b[3m';\nconst HEADING = '\\x1b[1;36m';\n\nfunction styleLine(line: string): string {\n const heading = /^(#{1,6})\\s+(.*)$/.exec(line);\n if (heading) {\n return `${HEADING}${heading[2]}${RESET}`;\n }\n\n let out = line;\n out = out.replace(/\\*\\*(.+?)\\*\\*/g, `${BOLD}$1${RESET}`);\n out = out.replace(/__(.+?)__/g, `${BOLD}$1${RESET}`);\n out = out.replace(/(?<!\\*)\\*([^*]+)\\*(?!\\*)/g, `${ITALIC}$1${RESET}`);\n out = out.replace(/`([^`]+)`/g, `${DIM}$1${RESET}`);\n out = out.replace(/^(\\s*)[-*+]\\s+/, '$1• ');\n out = out.replace(/^(\\s*>)\\s?/, `${DIM}│${RESET} `);\n return out;\n}\n\nfunction stripLine(line: string): string {\n return line\n .replace(/^#{1,6}\\s+/, '')\n .replace(/\\*\\*(.+?)\\*\\*/g, '$1')\n .replace(/__(.+?)__/g, '$1')\n .replace(/`([^`]+)`/g, '$1')\n .replace(/^(\\s*)[-*+]\\s+/, '$1- ');\n}\n\n/**\n * Line-buffered markdown -> ANSI renderer for the REPL's streaming\n * transcript: headings, emphasis, inline code, list markers, and a dimmed\n * treatment for fenced code blocks. Deliberately line-buffered rather than\n * block-buffered with a retroactive tail re-render — a completed line is\n * final and is never rewritten, which is simpler and avoids the cursor\n * bookkeeping a live-redraw renderer needs, at the cost of not syntax\n * highlighting code by language and not reflowing tables. Respects\n * `NO_COLOR` and non-TTY by falling back to the markdown stripped of its\n * syntax rather than raw asterisks/hashes.\n */\nexport class MarkdownRenderer {\n private buffer = '';\n private inFence = false;\n private fenceLang: string | undefined;\n\n push(text: string): void {\n this.buffer += text;\n let newlineIndex: number;\n while ((newlineIndex = this.buffer.indexOf('\\n')) !== -1) {\n const line = this.buffer.slice(0, newlineIndex);\n this.buffer = this.buffer.slice(newlineIndex + 1);\n this.emitLine(line);\n }\n }\n\n /** Flush any trailing partial line (the model's reply need not end in \\n). */\n flush(): void {\n if (this.buffer.length > 0) {\n this.emitLine(this.buffer);\n this.buffer = '';\n }\n }\n\n private emitLine(line: string): void {\n const color = ansiEnabled();\n\n const fenceMatch = /^```\\s*(\\S*)/.exec(line.trim());\n if (fenceMatch) {\n this.inFence = !this.inFence;\n this.fenceLang = this.inFence ? fenceMatch[1] || undefined : undefined;\n if (color) {\n process.stdout.write(`${DIM}${line}${RESET}\\n`);\n }\n // Plain-text fallback strips markdown syntax; the fence delimiter\n // itself carries no content, so it is dropped rather than printed.\n return;\n }\n\n if (this.inFence) {\n if (!color) {\n process.stdout.write(`${line}\\n`);\n return;\n }\n if (this.fenceLang && supportsLanguage(this.fenceLang)) {\n try {\n process.stdout.write(`${highlight(line, { language: this.fenceLang, ignoreIllegals: true })}\\n`);\n return;\n } catch {\n // Fall through to the flat dim treatment below.\n }\n }\n process.stdout.write(`${DIM}${line}${RESET}\\n`);\n return;\n }\n\n process.stdout.write(`${color ? styleLine(line) : stripLine(line)}\\n`);\n }\n}\n","import { ansiEnabled } from './tty.js';\n\nconst FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\nconst INTERVAL_MS = 80;\n\n/** A `\\r`-overwritten spinner shown while waiting for the first stream chunk. No-op without a color-capable TTY. */\nexport class Spinner {\n private timer: ReturnType<typeof setInterval> | null = null;\n private frame = 0;\n\n start(): void {\n if (!ansiEnabled() || this.timer) {\n return;\n }\n this.timer = setInterval(() => {\n process.stdout.write(`\\r${FRAMES[this.frame]} `);\n this.frame = (this.frame + 1) % FRAMES.length;\n }, INTERVAL_MS);\n }\n\n /** Stop and erase the spinner line so following output starts at column 0. */\n stop(): void {\n if (!this.timer) {\n return;\n }\n clearInterval(this.timer);\n this.timer = null;\n process.stdout.write('\\r\\x1b[K');\n }\n}\n","/** Read all of stdin as utf8 text, for `orbit-chat -`. */\nexport async function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n"],"mappings":"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","names":["randomUUID","randomUUID","readline","RESET","DIM","sessionId","randomUUID","message","randomUUID","code","message"]}
|