151 lines
3.6 KiB
JavaScript
Executable File
151 lines
3.6 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Simple terminal chat client for Didactyl HTTP API.
|
|
*
|
|
* Usage:
|
|
* node didactyl-chat-cli.js
|
|
*
|
|
* Optional env vars:
|
|
* DIDACTYL_API_BASE_URL=http://127.0.0.1:8484
|
|
* DIDACTYL_MODEL=claude-haiku-4.5
|
|
* DIDACTYL_MAX_TURNS=4
|
|
*/
|
|
|
|
const readline = require("node:readline/promises");
|
|
const { stdin, stdout } = require("node:process");
|
|
const http = require("node:http");
|
|
const https = require("node:https");
|
|
|
|
const API_BASE_URL = process.env.DIDACTYL_API_BASE_URL || "https://127.0.0.1:8484";
|
|
const MODEL = process.env.DIDACTYL_MODEL || "";
|
|
const MAX_TURNS = Number.parseInt(process.env.DIDACTYL_MAX_TURNS || "4", 10);
|
|
const INSECURE_TLS = !["0", "false", "False", "FALSE"].includes(
|
|
String(process.env.DIDACTYL_INSECURE_TLS || "1")
|
|
);
|
|
|
|
function printMessage(role, content) {
|
|
const who = role === "user" ? "You" : role === "assistant" ? "Didactyl" : role;
|
|
console.log(`${who}>`);
|
|
console.log(content);
|
|
console.log("");
|
|
}
|
|
|
|
function postJson(urlString, payload) {
|
|
const url = new URL(urlString);
|
|
const isHttps = url.protocol === "https:";
|
|
const data = JSON.stringify(payload);
|
|
|
|
const options = {
|
|
method: "POST",
|
|
hostname: url.hostname,
|
|
port: url.port,
|
|
path: url.pathname + url.search,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"Content-Length": Buffer.byteLength(data),
|
|
},
|
|
};
|
|
|
|
if (isHttps) {
|
|
options.rejectUnauthorized = !INSECURE_TLS;
|
|
}
|
|
|
|
const client = isHttps ? https : http;
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const req = client.request(options, (res) => {
|
|
let body = "";
|
|
res.setEncoding("utf8");
|
|
res.on("data", (chunk) => {
|
|
body += chunk;
|
|
});
|
|
res.on("end", () => {
|
|
resolve({
|
|
statusCode: res.statusCode || 0,
|
|
body,
|
|
});
|
|
});
|
|
});
|
|
|
|
req.on("error", reject);
|
|
req.write(data);
|
|
req.end();
|
|
});
|
|
}
|
|
|
|
async function callDidactyl(message) {
|
|
const body = {
|
|
message,
|
|
max_turns: Number.isFinite(MAX_TURNS) ? MAX_TURNS : 4,
|
|
};
|
|
|
|
if (MODEL.trim()) {
|
|
body.model = MODEL.trim();
|
|
}
|
|
|
|
const { statusCode, body: responseBody } = await postJson(`${API_BASE_URL}/api/prompt/agent`, body);
|
|
|
|
if (statusCode < 200 || statusCode >= 300) {
|
|
throw new Error(`HTTP ${statusCode}: ${responseBody}`);
|
|
}
|
|
|
|
let data;
|
|
try {
|
|
data = JSON.parse(responseBody);
|
|
} catch {
|
|
throw new Error(`Invalid JSON from API: ${responseBody}`);
|
|
}
|
|
|
|
if (!data.success) {
|
|
throw new Error(data.error || "Didactyl API returned success=false");
|
|
}
|
|
|
|
return String(data.final_response || "");
|
|
}
|
|
|
|
async function main() {
|
|
console.log("Didactyl CLI chat");
|
|
console.log(`API: ${API_BASE_URL}`);
|
|
console.log(`TLS verify: ${INSECURE_TLS ? "disabled (local dev)" : "enabled"}`);
|
|
console.log("Type /exit to quit.\n");
|
|
|
|
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
|
|
try {
|
|
while (true) {
|
|
const input = (await rl.question("You> ")).trim();
|
|
|
|
if (!input) {
|
|
console.log("");
|
|
continue;
|
|
}
|
|
|
|
if (input === "/exit" || input === "/quit") {
|
|
console.log("Exiting.");
|
|
break;
|
|
}
|
|
|
|
console.log("");
|
|
|
|
try {
|
|
const reply = await callDidactyl(input);
|
|
printMessage("assistant", reply);
|
|
console.log("");
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : String(err);
|
|
console.error(`Didactyl error: ${message}`);
|
|
console.error("");
|
|
}
|
|
}
|
|
} finally {
|
|
rl.close();
|
|
}
|
|
}
|
|
|
|
main().catch((err) => {
|
|
const message = err instanceof Error ? err.message : String(err);
|
|
console.error(`Fatal error: ${message}`);
|
|
process.exit(1);
|
|
});
|