import CLIController, { OpenCodeCLI } from './cli.js';
* CLIController 接口调用测试
*
* 使用真实运行的 CLI 服务逐个调用 CLIController 暴露的全部接口,
* 模拟真实使用流程:
*
* checkHealth → listSessions → startSession → sendMessage →
* checkSessionStatus → getSessionMessages → getSession →
* getArchivedMessages → getArchiveMeta → listArchivedSessions →
* abortSession → deleteSession → getType / getAdapter
*
* 默认连接 opencode 服务(localhost:4096),可通过环境变量切换:
*
* CLI_TYPE=deveco-code node test.js
*
* deveco-code 服务需先启动:deveco serve(默认 4096,与 opencode 一致)
*/
const SEP = '═'.repeat(72);
const SUB = '─'.repeat(72);
function header(title) {
console.log(`\n${SEP}\n ${title}\n${SEP}`);
}
function sub(title) {
console.log(`\n── ${title} ──────────────────────────────────────────`);
}
function safeStringify(obj, indent = 2) {
const seen = new WeakSet();
return JSON.stringify(obj, (_key, value) => {
if (typeof value === 'bigint') return value.toString();
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) return '[Circular]';
seen.add(value);
}
return value;
}, indent);
}
function printResult(label, result) {
console.log(`\n✅ ${label}`);
console.log(safeStringify(result));
}
function printError(label, err) {
console.log(`\n❌ ${label}`);
console.error(err);
}
* 等待 opencode session 完成(轮询 checkSessionStatus)
* @param {CLIController} cli
* @param {string} sessionId
* @param {object} [opts]
* @param {number} [opts.maxWaitMs=60000] - 最大等待时长
* @param {number} [opts.intervalMs=2000] - 轮询间隔
* @param {(status:string)=>boolean} [opts.isDone] - 自定义完成判断
*/
async function waitForStatus(cli, sessionId, opts = {}) {
const maxWaitMs = opts.maxWaitMs ?? 60_000;
const intervalMs = opts.intervalMs ?? 2000;
const isDone = opts.isDone ?? (s => s === 'completed' || s === 'error' || s === 'unknown');
const start = Date.now();
let lastStatus = 'unknown';
while (Date.now() - start < maxWaitMs) {
const status = await cli.checkSessionStatus(sessionId).catch(e => {
console.warn(` ⚠️ 轮询失败: ${e.message}`);
return { status: 'unknown' };
});
lastStatus = status.status;
if (isDone(lastStatus)) return status;
await sleep(intervalMs);
}
console.warn(` ⚠️ 等待超时 (${maxWaitMs}ms),最后状态: ${lastStatus}`);
return { status: lastStatus };
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function main() {
header('CLIController 接口调用测试');
const cliType = process.env.CLI_TYPE || 'deveco-code';
const cliHost = process.env.CLI_HOST || 'localhost';
const cliPort = process.env.CLI_PORT || '4096';
sub(`1. 创建 CLIController (${cliType})`);
const cli = new CLIController(cliType, {
host: cliHost,
port: cliPort
});
sub('2. checkHealth()');
const health = await cli.checkHealth();
if (health.status !== 'ok') {
console.error(`\n🛑 ${cliType} 服务不可用,终止测试`);
process.exit(1);
}
sub('3. listSessions()');
const sessionsBefore = await cli.listSessions();
sub('4. startSession(title, prompt)');
const title = `测试 Session ${new Date().toLocaleString('zh-CN')}`;
const prompt = '请用一句话介绍你自己。';
const startResult = await cli.startSession(title, prompt);
const sessionId = startResult.sessionId;
sub('5. checkSessionStatus() — 等待初始 prompt 完成');
const status1 = await waitForStatus(cli, sessionId, {
maxWaitMs: 90_000,
intervalMs: 3000
});
sub('6. sendMessage(sessionId, message)');
const followUp = '请再说一句关于天气的话。';
const sendResult = await cli.sendMessage(sessionId, followUp);
sub('6a. injectPrompt(sessionId, message) — 注入上下文');
const injectText = '【补充规则】后续回复请始终以"好的,"开头。';
try {
const injectResult = await cli.injectPrompt(sessionId, injectText);
printResult('injectPrompt', injectResult);
} catch (err) {
printError('injectPrompt', err);
}
sub('6b. getLiveMessages(sessionId) — 对话运行中实时查看');
const liveMsgs = await cli.getLiveMessages(sessionId);
console.log(`\n 实时消息 (${liveMsgs.length} 条)`);
for (const msg of liveMsgs) {
console.log(` [${msg.role}] ${msg.content}`);
}
sub('7. checkSessionStatus() — 等待跟进消息完成');
const status2 = await waitForStatus(cli, sessionId, {
maxWaitMs: 90_000,
intervalMs: 3000
});
sub('7a. sendAndWaitMessage(sessionId, message) — 同步等待 AI 回复');
const syncQuestion = '请用一句话介绍鸿蒙系统。';
try {
const syncResult = await cli.sendAndWaitMessage(sessionId, syncQuestion);
console.log(`\n ✅ AI 回复 (同步等待):`);
console.log(` ${syncResult.content}`);
console.log(` finish: ${syncResult.response?.info?.finish ?? 'N/A'}`);
} catch (err) {
printError('sendAndWaitMessage', err);
}
sub('8. getSessionMessages(sessionId)');
const messages = await cli.getSessionMessages(sessionId);
sub('9. getSession(sessionId)');
const sessionInfo = await cli.getSession(sessionId);
sub('10. getArchivedMessages(sessionId)');
const archived = await cli.getArchivedMessages(sessionId);
sub('11. getArchiveMeta(sessionId)');
const meta = await cli.getArchiveMeta(sessionId);
sub('11b. exportSession(sessionId, { format: "json" })');
const exportedJson = await cli.exportSession(sessionId, { format: 'json' });
printResult(`导出对话 JSON (${exportedJson.messages.length} 条消息)`, exportedJson);
sub('11c. exportSession(sessionId, { format: "markdown" })');
const exportedMd = await cli.exportSession(sessionId, { format: 'markdown' });;
sub('12. listArchivedSessions()');
const archivedSessions = await cli.listArchivedSessions();
sub('13. abortSession(sessionId)');
const abortResult = await cli.abortSession(sessionId);
sub('14. 不支持的 CLI 类型');
try {
new CLIController('unknown-type');
console.log(' ⚠️ 未抛出异常');
} catch (err) {
printResult('预期异常捕获', { message: err.message });
}
header('✅ 全部接口调用完成');
}
main().catch(err => {
console.error('\n🛑 测试执行失败:', err);
process.exit(1);
});