opc-agent 4.1.24 → 4.2.0
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/CHANGELOG.md +59 -119
- package/COMPETITIVE-GAP.md +92 -92
- package/CONTRIBUTING.md +36 -36
- package/README.md +290 -290
- package/README.zh-CN.md +269 -269
- package/dist/channels/telegram.d.ts +0 -5
- package/dist/channels/telegram.d.ts.map +1 -1
- package/dist/channels/telegram.js +0 -108
- package/dist/channels/telegram.js.map +1 -1
- package/dist/channels/voice.d.ts +97 -71
- package/dist/channels/voice.d.ts.map +1 -1
- package/dist/channels/voice.js +347 -369
- package/dist/channels/voice.js.map +1 -1
- package/dist/channels/web.d.ts.map +1 -1
- package/dist/channels/web.js +2 -8
- package/dist/channels/web.js.map +1 -1
- package/dist/channels/wechat.js +6 -6
- package/dist/cli/chat.d.ts +1 -4
- package/dist/cli/chat.d.ts.map +1 -1
- package/dist/cli/chat.js +73 -680
- package/dist/cli/chat.js.map +1 -1
- package/dist/cli/setup.js +1 -1
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli.js +280 -373
- package/dist/cli.js.map +1 -1
- package/dist/core/agent.d.ts +0 -1
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +0 -3
- package/dist/core/agent.js.map +1 -1
- package/dist/core/runtime.d.ts.map +1 -1
- package/dist/core/runtime.js +22 -192
- package/dist/core/runtime.js.map +1 -1
- package/dist/deploy/index.js +56 -56
- package/dist/doctor.d.ts +0 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +10 -155
- package/dist/doctor.js.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -9
- package/dist/index.js.map +1 -1
- package/dist/memory/deepbrain.d.ts +1 -1
- package/dist/memory/deepbrain.d.ts.map +1 -1
- package/dist/memory/deepbrain.js +4 -95
- package/dist/memory/deepbrain.js.map +1 -1
- package/dist/memory/index.d.ts +0 -2
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +1 -3
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/user-profiler.d.ts +0 -8
- package/dist/memory/user-profiler.d.ts.map +1 -1
- package/dist/memory/user-profiler.js +0 -89
- package/dist/memory/user-profiler.js.map +1 -1
- package/dist/scheduler/cron-engine.d.ts.map +1 -1
- package/dist/scheduler/cron-engine.js +36 -3
- package/dist/scheduler/cron-engine.js.map +1 -1
- package/dist/skills/auto-learn.d.ts.map +1 -1
- package/dist/skills/auto-learn.js +11 -65
- package/dist/skills/auto-learn.js.map +1 -1
- package/dist/skills/builtin/index.d.ts.map +1 -1
- package/dist/skills/builtin/index.js +30 -163
- package/dist/skills/builtin/index.js.map +1 -1
- package/dist/skills/types.d.ts +1 -1
- package/dist/skills/types.d.ts.map +1 -1
- package/dist/skills/types.js +0 -1
- package/dist/skills/types.js.map +1 -1
- package/dist/studio/server.d.ts +0 -1
- package/dist/studio/server.d.ts.map +1 -1
- package/dist/studio/server.js +12 -142
- package/dist/studio/server.js.map +1 -1
- package/dist/studio-ui/index.html +40 -359
- package/dist/ui/components.js +105 -105
- package/examples/README.md +22 -22
- package/examples/basic-agent.ts +90 -90
- package/examples/brain-integration.ts +71 -71
- package/examples/multi-channel.ts +74 -74
- package/install.ps1 +127 -127
- package/install.sh +154 -154
- package/models.json +164 -164
- package/package.json +63 -66
- package/scripts/install.ps1 +31 -31
- package/scripts/install.sh +40 -40
- package/templates/ecommerce-assistant/README.md +45 -45
- package/templates/ecommerce-assistant/oad.yaml +47 -47
- package/templates/tech-support/README.md +43 -43
- package/templates/tech-support/oad.yaml +45 -45
- package/.opc/memory.db +0 -0
- package/dist/core/model-recommender.d.ts +0 -40
- package/dist/core/model-recommender.d.ts.map +0 -1
- package/dist/core/model-recommender.js +0 -186
- package/dist/core/model-recommender.js.map +0 -1
- package/dist/memory/evolve-engine.d.ts +0 -113
- package/dist/memory/evolve-engine.d.ts.map +0 -1
- package/dist/memory/evolve-engine.js +0 -549
- package/dist/memory/evolve-engine.js.map +0 -1
- package/dist/memory/sqlite-store.d.ts +0 -40
- package/dist/memory/sqlite-store.d.ts.map +0 -1
- package/dist/memory/sqlite-store.js +0 -269
- package/dist/memory/sqlite-store.js.map +0 -1
- package/dist/scheduler/proactive.d.ts +0 -62
- package/dist/scheduler/proactive.d.ts.map +0 -1
- package/dist/scheduler/proactive.js +0 -185
- package/dist/scheduler/proactive.js.map +0 -1
package/dist/cli/chat.js
CHANGED
|
@@ -37,705 +37,98 @@ exports.runChat = runChat;
|
|
|
37
37
|
const readline = __importStar(require("readline"));
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
|
-
const
|
|
40
|
+
const os = __importStar(require("os"));
|
|
41
41
|
const providers_1 = require("../providers");
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
underline: `${CSI}4m`,
|
|
51
|
-
inverse: `${CSI}7m`,
|
|
52
|
-
// foreground
|
|
53
|
-
black: `${CSI}30m`,
|
|
54
|
-
red: `${CSI}31m`,
|
|
55
|
-
green: `${CSI}32m`,
|
|
56
|
-
yellow: `${CSI}33m`,
|
|
57
|
-
blue: `${CSI}34m`,
|
|
58
|
-
magenta: `${CSI}35m`,
|
|
59
|
-
cyan: `${CSI}36m`,
|
|
60
|
-
white: `${CSI}37m`,
|
|
61
|
-
gray: `${CSI}90m`,
|
|
62
|
-
// background
|
|
63
|
-
bgBlack: `${CSI}40m`,
|
|
64
|
-
bgRed: `${CSI}41m`,
|
|
65
|
-
bgGreen: `${CSI}42m`,
|
|
66
|
-
bgYellow: `${CSI}43m`,
|
|
67
|
-
bgBlue: `${CSI}44m`,
|
|
68
|
-
bgWhite: `${CSI}47m`,
|
|
69
|
-
bgGray: `${CSI}100m`,
|
|
70
|
-
// cursor / screen
|
|
71
|
-
clearScreen: `${CSI}2J`,
|
|
72
|
-
clearLine: `${CSI}2K`,
|
|
73
|
-
cursorHome: `${CSI}H`,
|
|
74
|
-
cursorTo: (row, col) => `${CSI}${row};${col}H`,
|
|
75
|
-
cursorUp: (n) => `${CSI}${n}A`,
|
|
76
|
-
cursorDown: (n) => `${CSI}${n}B`,
|
|
77
|
-
cursorSave: `${ESC}7`,
|
|
78
|
-
cursorRestore: `${ESC}8`,
|
|
79
|
-
scrollRegion: (top, bottom) => `${CSI}${top};${bottom}r`,
|
|
80
|
-
hideCursor: `${CSI}?25l`,
|
|
81
|
-
showCursor: `${CSI}?25h`,
|
|
42
|
+
const OPC_HOME = path.join(os.homedir(), '.opc');
|
|
43
|
+
const CONFIG_PATH = path.join(OPC_HOME, 'config.json');
|
|
44
|
+
const c = {
|
|
45
|
+
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
46
|
+
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
47
|
+
cyan: (s) => `\x1b[36m${s}\x1b[0m`,
|
|
48
|
+
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
49
|
+
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
82
50
|
};
|
|
83
|
-
function
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
function stripAnsi(s) {
|
|
87
|
-
return s.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
|
|
88
|
-
}
|
|
89
|
-
// ── Markdown → ANSI renderer ────────────────────────────────
|
|
90
|
-
function renderMarkdown(text) {
|
|
91
|
-
const lines = text.split('\n');
|
|
92
|
-
const out = [];
|
|
93
|
-
let inCodeBlock = false;
|
|
94
|
-
let codeLang = '';
|
|
95
|
-
for (const line of lines) {
|
|
96
|
-
// Code block toggle
|
|
97
|
-
if (line.trimStart().startsWith('```')) {
|
|
98
|
-
if (!inCodeBlock) {
|
|
99
|
-
inCodeBlock = true;
|
|
100
|
-
codeLang = line.trimStart().slice(3).trim();
|
|
101
|
-
const label = codeLang ? ` ${codeLang} ` : '';
|
|
102
|
-
out.push(`${ansi.bgGray}${ansi.white}${label}${'─'.repeat(Math.max(0, 50 - label.length))}${ansi.reset}`);
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
inCodeBlock = false;
|
|
106
|
-
out.push(`${ansi.bgGray}${'─'.repeat(50)}${ansi.reset}`);
|
|
107
|
-
}
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
if (inCodeBlock) {
|
|
111
|
-
out.push(`${ansi.bgGray}${ansi.white} ${line}${ansi.reset}`);
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
let rendered = line;
|
|
115
|
-
// Headers
|
|
116
|
-
const hMatch = rendered.match(/^(#{1,3})\s+(.*)/);
|
|
117
|
-
if (hMatch) {
|
|
118
|
-
out.push(c(ansi.bold + ansi.cyan, hMatch[2]));
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
// Bullet lists
|
|
122
|
-
if (/^\s*[-*]\s/.test(rendered)) {
|
|
123
|
-
const indent = rendered.match(/^(\s*)/)?.[1] || '';
|
|
124
|
-
rendered = rendered.replace(/^(\s*)[-*]\s/, '');
|
|
125
|
-
rendered = renderInline(rendered);
|
|
126
|
-
out.push(`${indent} ${ansi.cyan}•${ansi.reset} ${rendered}`);
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
// Numbered lists
|
|
130
|
-
if (/^\s*\d+\.\s/.test(rendered)) {
|
|
131
|
-
const match = rendered.match(/^(\s*)(\d+)\.\s(.*)/);
|
|
132
|
-
if (match) {
|
|
133
|
-
out.push(`${match[1]} ${ansi.cyan}${match[2]}.${ansi.reset} ${renderInline(match[3])}`);
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
out.push(renderInline(rendered));
|
|
138
|
-
}
|
|
139
|
-
return out.join('\n');
|
|
140
|
-
}
|
|
141
|
-
function renderInline(text) {
|
|
142
|
-
// Bold **text** or __text__
|
|
143
|
-
text = text.replace(/\*\*(.+?)\*\*/g, `${ansi.bold}$1${ansi.reset}`);
|
|
144
|
-
text = text.replace(/__(.+?)__/g, `${ansi.bold}$1${ansi.reset}`);
|
|
145
|
-
// Italic *text* or _text_
|
|
146
|
-
text = text.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, `${ansi.italic}$1${ansi.reset}`);
|
|
147
|
-
// Inline code `text`
|
|
148
|
-
text = text.replace(/`([^`]+)`/g, `${ansi.bgGray}${ansi.white} $1 ${ansi.reset}`);
|
|
149
|
-
return text;
|
|
150
|
-
}
|
|
151
|
-
function loadChatConfig(oadFile = 'oad.yaml') {
|
|
152
|
-
let agentName = 'Agent';
|
|
153
|
-
let agentVersion = '1.0.0';
|
|
154
|
-
let providerName = 'auto';
|
|
155
|
-
let model = 'auto';
|
|
156
|
-
let systemPrompt = 'You are a helpful AI agent.';
|
|
157
|
-
let skillNames = [];
|
|
158
|
-
// Load SOUL.md + CONTEXT.md
|
|
159
|
-
const soulPath = path.resolve('SOUL.md');
|
|
160
|
-
const contextPath = path.resolve('CONTEXT.md');
|
|
161
|
-
const soul = fs.existsSync(soulPath) ? fs.readFileSync(soulPath, 'utf-8') : '';
|
|
162
|
-
const context = fs.existsSync(contextPath) ? fs.readFileSync(contextPath, 'utf-8') : '';
|
|
51
|
+
function loadConfig() {
|
|
52
|
+
if (!fs.existsSync(CONFIG_PATH))
|
|
53
|
+
return {};
|
|
163
54
|
try {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if (config?.spec?.model)
|
|
169
|
-
model = config.spec.model;
|
|
170
|
-
if (config?.metadata?.name)
|
|
171
|
-
agentName = config.metadata.name;
|
|
172
|
-
if (config?.metadata?.version)
|
|
173
|
-
agentVersion = config.metadata.version;
|
|
174
|
-
if (config?.spec?.provider?.default)
|
|
175
|
-
providerName = config.spec.provider.default;
|
|
176
|
-
if (config?.spec?.skills)
|
|
177
|
-
skillNames = config.spec.skills.map((s) => s.name);
|
|
55
|
+
return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return {};
|
|
178
59
|
}
|
|
179
|
-
catch { /* no config */ }
|
|
180
|
-
systemPrompt = [soul, context, systemPrompt].filter(Boolean).join('\n\n');
|
|
181
|
-
return { agentName, agentVersion, providerName, model, systemPrompt, skillNames };
|
|
182
60
|
}
|
|
183
|
-
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
61
|
+
async function runChat() {
|
|
62
|
+
const config = loadConfig();
|
|
63
|
+
if (!config.provider || !config.model) {
|
|
64
|
+
console.log(`${c.red('✘')} 未找到配置。请先运行 ${c.cyan('opc setup')} 完成初始设置。`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
const agentId = config.defaultAgent;
|
|
68
|
+
let agentConfig = {};
|
|
69
|
+
if (agentId) {
|
|
70
|
+
const agentPath = path.join(OPC_HOME, 'agents', agentId, 'config.json');
|
|
71
|
+
if (fs.existsSync(agentPath)) {
|
|
72
|
+
try {
|
|
73
|
+
agentConfig = JSON.parse(fs.readFileSync(agentPath, 'utf-8'));
|
|
74
|
+
}
|
|
75
|
+
catch { /* ignore */ }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const agentName = agentConfig.name || 'AI 助手';
|
|
79
|
+
const systemPrompt = agentConfig.description
|
|
80
|
+
? `你是「${agentName}」,${agentConfig.description}。请用简洁友好的方式回答。`
|
|
81
|
+
: `你是「${agentName}」,一个智能 AI 助手。请用简洁友好的方式回答。`;
|
|
82
|
+
console.log('');
|
|
83
|
+
console.log(c.bold(`💬 与「${agentName}」对话`));
|
|
84
|
+
console.log(c.dim(' 输入 /quit 退出'));
|
|
85
|
+
console.log('');
|
|
190
86
|
let provider;
|
|
191
87
|
try {
|
|
192
|
-
provider = (0, providers_1.createProvider)(
|
|
88
|
+
provider = (0, providers_1.createProvider)({
|
|
89
|
+
provider: config.provider,
|
|
90
|
+
model: config.model,
|
|
91
|
+
apiKey: config.apiKey,
|
|
92
|
+
baseUrl: config.baseUrl,
|
|
93
|
+
});
|
|
193
94
|
}
|
|
194
95
|
catch (err) {
|
|
195
|
-
console.
|
|
96
|
+
console.log(`${c.red('✘')} 无法初始化模型: ${err.message}`);
|
|
97
|
+
console.log(c.dim(` Provider: ${config.provider}, Model: ${config.model}`));
|
|
196
98
|
process.exit(1);
|
|
197
99
|
}
|
|
198
|
-
const messages = [
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
// ── Render status bar ───────────────────────────────────
|
|
208
|
-
function renderStatusBar() {
|
|
209
|
-
const w = cols();
|
|
210
|
-
const uptimeSec = Math.floor((Date.now() - startTime) / 1000);
|
|
211
|
-
const h = Math.floor(uptimeSec / 3600);
|
|
212
|
-
const m = Math.floor((uptimeSec % 3600) / 60);
|
|
213
|
-
const s = uptimeSec % 60;
|
|
214
|
-
const uptimeStr = h > 0 ? `${h}h${m}m` : `${m}m${s}s`;
|
|
215
|
-
const left = ` 🤖 ${config.agentName} `;
|
|
216
|
-
const mid = ` ${config.providerName}/${config.model} `;
|
|
217
|
-
const right = ` ⏱${uptimeStr} | 🔧${config.skillNames.length} skills `;
|
|
218
|
-
const padding = Math.max(0, w - stripAnsi(left).length - stripAnsi(mid).length - stripAnsi(right).length);
|
|
219
|
-
const bar = left + mid + ' '.repeat(padding) + right;
|
|
220
|
-
process.stdout.write(ansi.cursorSave);
|
|
221
|
-
process.stdout.write(ansi.cursorTo(1, 1));
|
|
222
|
-
process.stdout.write(`${ansi.bgBlue}${ansi.white}${ansi.bold}${bar.padEnd(w)}${ansi.reset}`);
|
|
223
|
-
process.stdout.write(ansi.cursorRestore);
|
|
224
|
-
}
|
|
225
|
-
// ── Render input bar ────────────────────────────────────
|
|
226
|
-
function renderInputBar(text) {
|
|
227
|
-
const w = cols();
|
|
228
|
-
const r = rows();
|
|
229
|
-
process.stdout.write(ansi.cursorSave);
|
|
230
|
-
// Separator line
|
|
231
|
-
process.stdout.write(ansi.cursorTo(r - 1, 1));
|
|
232
|
-
process.stdout.write(`${ansi.gray}${'─'.repeat(w)}${ansi.reset}`);
|
|
233
|
-
// Input line
|
|
234
|
-
process.stdout.write(ansi.cursorTo(r, 1));
|
|
235
|
-
process.stdout.write(ansi.clearLine);
|
|
236
|
-
const prompt = `${ansi.cyan}${ansi.bold}❯ ${ansi.reset}`;
|
|
237
|
-
const maxLen = w - 4;
|
|
238
|
-
const displayText = text.length > maxLen ? '…' + text.slice(-(maxLen - 1)) : text;
|
|
239
|
-
process.stdout.write(`${prompt}${displayText}`);
|
|
240
|
-
process.stdout.write(ansi.cursorRestore);
|
|
241
|
-
}
|
|
242
|
-
// ── Print message in chat area ──────────────────────────
|
|
243
|
-
let chatLine = 3; // start below status bar
|
|
244
|
-
function printToChat(text) {
|
|
245
|
-
const r = rows();
|
|
246
|
-
const maxChatLine = r - 2; // leave 2 lines for input area
|
|
247
|
-
const lines = text.split('\n');
|
|
248
|
-
for (const line of lines) {
|
|
249
|
-
if (chatLine >= maxChatLine) {
|
|
250
|
-
// Scroll: move everything up
|
|
251
|
-
process.stdout.write(ansi.cursorTo(3, 1));
|
|
252
|
-
process.stdout.write(`${CSI}1S`); // scroll up 1 line in region
|
|
253
|
-
chatLine = maxChatLine - 1;
|
|
254
|
-
}
|
|
255
|
-
process.stdout.write(ansi.cursorTo(chatLine, 1));
|
|
256
|
-
process.stdout.write(ansi.clearLine);
|
|
257
|
-
process.stdout.write(line);
|
|
258
|
-
chatLine++;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
function printSystemMsg(text) {
|
|
262
|
-
printToChat(`${ansi.gray} ${text}${ansi.reset}`);
|
|
263
|
-
}
|
|
264
|
-
function printUserMsg(text) {
|
|
265
|
-
printToChat(`${ansi.cyan}${ansi.bold} You: ${ansi.reset}${text}`);
|
|
266
|
-
printToChat('');
|
|
267
|
-
}
|
|
268
|
-
function printAssistantPrefix() {
|
|
269
|
-
printToChat(`${ansi.green}${ansi.bold} ${config.agentName}: ${ansi.reset}`);
|
|
270
|
-
}
|
|
271
|
-
// ── Clear and redraw ───────────────────────────────────
|
|
272
|
-
function fullRedraw() {
|
|
273
|
-
const r = rows();
|
|
274
|
-
process.stdout.write(ansi.clearScreen + ansi.cursorHome);
|
|
275
|
-
// Set scroll region (between status bar and input)
|
|
276
|
-
process.stdout.write(ansi.scrollRegion(3, r - 2));
|
|
277
|
-
chatLine = 3;
|
|
278
|
-
renderStatusBar();
|
|
279
|
-
// Re-render recent messages (last N that fit)
|
|
280
|
-
const maxLines = r - 5;
|
|
281
|
-
let lineCount = 0;
|
|
282
|
-
const toShow = [];
|
|
283
|
-
for (let i = messages.length - 1; i >= 0 && lineCount < maxLines; i--) {
|
|
284
|
-
const msg = messages[i];
|
|
285
|
-
const rendered = msg.role === 'user' ? msg.content : renderMarkdown(msg.content);
|
|
286
|
-
const lines = rendered.split('\n').length + 2;
|
|
287
|
-
lineCount += lines;
|
|
288
|
-
toShow.unshift(msg);
|
|
289
|
-
}
|
|
290
|
-
for (const msg of toShow) {
|
|
291
|
-
if (msg.role === 'user') {
|
|
292
|
-
printUserMsg(msg.content);
|
|
293
|
-
}
|
|
294
|
-
else if (msg.role === 'assistant') {
|
|
295
|
-
printToChat(`${ansi.green}${ansi.bold} ${config.agentName}: ${ansi.reset}`);
|
|
296
|
-
const rendered = renderMarkdown(msg.content);
|
|
297
|
-
for (const line of rendered.split('\n')) {
|
|
298
|
-
printToChat(` ${line}`);
|
|
299
|
-
}
|
|
300
|
-
printToChat('');
|
|
301
|
-
}
|
|
302
|
-
else {
|
|
303
|
-
printSystemMsg(msg.content);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
renderInputBar(currentInput);
|
|
307
|
-
}
|
|
308
|
-
// ── Handle slash commands ──────────────────────────────
|
|
309
|
-
function handleCommand(cmd) {
|
|
310
|
-
const lower = cmd.toLowerCase().trim();
|
|
311
|
-
const parts = lower.split(/\s+/);
|
|
312
|
-
switch (parts[0]) {
|
|
313
|
-
case '/help':
|
|
314
|
-
printToChat('');
|
|
315
|
-
printSystemMsg('╭─── Commands ───────────────────────╮');
|
|
316
|
-
printSystemMsg('│ /help Show this help │');
|
|
317
|
-
printSystemMsg('│ /clear Clear screen & history │');
|
|
318
|
-
printSystemMsg('│ /model Show/switch model │');
|
|
319
|
-
printSystemMsg('│ /tools List available tools │');
|
|
320
|
-
printSystemMsg('│ /skills List skills │');
|
|
321
|
-
printSystemMsg('│ /history Export chat history │');
|
|
322
|
-
printSystemMsg('│ /status Show agent status │');
|
|
323
|
-
printSystemMsg('│ /quit Exit │');
|
|
324
|
-
printSystemMsg('╰────────────────────────────────────╯');
|
|
325
|
-
printSystemMsg('Shortcuts: Ctrl+C abort | Ctrl+L clear | ↑↓ history');
|
|
326
|
-
printToChat('');
|
|
327
|
-
return true;
|
|
328
|
-
case '/clear':
|
|
329
|
-
messages.length = 0;
|
|
330
|
-
fullRedraw();
|
|
331
|
-
printSystemMsg('Chat cleared.');
|
|
332
|
-
printToChat('');
|
|
333
|
-
return true;
|
|
334
|
-
case '/model':
|
|
335
|
-
printSystemMsg(`Current model: ${config.providerName}/${config.model}`);
|
|
336
|
-
printToChat('');
|
|
337
|
-
return true;
|
|
338
|
-
case '/tools':
|
|
339
|
-
printSystemMsg('Tools: (managed by agent runtime)');
|
|
340
|
-
if (recentTools.length > 0) {
|
|
341
|
-
printSystemMsg(`Recent: ${recentTools.join(', ')}`);
|
|
342
|
-
}
|
|
343
|
-
printToChat('');
|
|
344
|
-
return true;
|
|
345
|
-
case '/skills':
|
|
346
|
-
if (config.skillNames.length === 0) {
|
|
347
|
-
printSystemMsg('No skills registered.');
|
|
348
|
-
}
|
|
349
|
-
else {
|
|
350
|
-
printSystemMsg(`Skills (${config.skillNames.length}):`);
|
|
351
|
-
for (const s of config.skillNames) {
|
|
352
|
-
printSystemMsg(` • ${s}`);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
printToChat('');
|
|
356
|
-
return true;
|
|
357
|
-
case '/history': {
|
|
358
|
-
const histFile = path.resolve(`chat-history-${Date.now()}.md`);
|
|
359
|
-
const content = messages.map(m => {
|
|
360
|
-
const prefix = m.role === 'user' ? '**You**' : m.role === 'assistant' ? `**${config.agentName}**` : '*System*';
|
|
361
|
-
return `${prefix}: ${m.content}`;
|
|
362
|
-
}).join('\n\n');
|
|
363
|
-
fs.writeFileSync(histFile, content);
|
|
364
|
-
printSystemMsg(`History exported to ${histFile}`);
|
|
365
|
-
printToChat('');
|
|
366
|
-
return true;
|
|
367
|
-
}
|
|
368
|
-
case '/status': {
|
|
369
|
-
const uptimeSec = Math.floor((Date.now() - startTime) / 1000);
|
|
370
|
-
const msgCount = messages.length;
|
|
371
|
-
const userMsgs = messages.filter(m => m.role === 'user').length;
|
|
372
|
-
printSystemMsg(`Agent: ${config.agentName} v${config.agentVersion}`);
|
|
373
|
-
printSystemMsg(`Model: ${config.providerName}/${config.model}`);
|
|
374
|
-
printSystemMsg(`Uptime: ${uptimeSec}s | Messages: ${msgCount} (${userMsgs} from you)`);
|
|
375
|
-
printSystemMsg(`Skills: ${config.skillNames.length}`);
|
|
376
|
-
printToChat('');
|
|
377
|
-
return true;
|
|
378
|
-
}
|
|
379
|
-
case '/quit':
|
|
380
|
-
case '/exit':
|
|
381
|
-
case '/q':
|
|
382
|
-
cleanup();
|
|
383
|
-
process.stdout.write(ansi.clearScreen + ansi.cursorHome + ansi.showCursor);
|
|
384
|
-
// Reset scroll region
|
|
385
|
-
process.stdout.write(`${CSI}r`);
|
|
386
|
-
console.log(`${ansi.dim}👋 Goodbye!${ansi.reset}`);
|
|
387
|
-
process.exit(0);
|
|
388
|
-
return true;
|
|
389
|
-
default:
|
|
390
|
-
printSystemMsg(`Unknown command: ${parts[0]}. Type /help for commands.`);
|
|
391
|
-
printToChat('');
|
|
392
|
-
return true;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
// ── Stream AI response ─────────────────────────────────
|
|
396
|
-
async function sendMessage(text) {
|
|
397
|
-
messages.push({ role: 'user', content: text, timestamp: Date.now() });
|
|
398
|
-
printUserMsg(text);
|
|
399
|
-
// Build message array for provider
|
|
400
|
-
const history = messages.map(m => ({
|
|
401
|
-
id: `msg_${m.timestamp}`,
|
|
402
|
-
role: m.role,
|
|
403
|
-
content: m.content,
|
|
404
|
-
timestamp: m.timestamp,
|
|
405
|
-
}));
|
|
406
|
-
printAssistantPrefix();
|
|
407
|
-
isStreaming = true;
|
|
408
|
-
abortStream = false;
|
|
409
|
-
let fullResponse = '';
|
|
410
|
-
try {
|
|
411
|
-
let lineBuffer = '';
|
|
412
|
-
for await (const chunk of provider.chatStream(history, config.systemPrompt)) {
|
|
413
|
-
if (abortStream) {
|
|
414
|
-
fullResponse += '\n[interrupted]';
|
|
415
|
-
break;
|
|
416
|
-
}
|
|
417
|
-
fullResponse += chunk;
|
|
418
|
-
lineBuffer += chunk;
|
|
419
|
-
// Flush complete lines for markdown rendering
|
|
420
|
-
const nlIdx = lineBuffer.lastIndexOf('\n');
|
|
421
|
-
if (nlIdx >= 0) {
|
|
422
|
-
const complete = lineBuffer.slice(0, nlIdx);
|
|
423
|
-
lineBuffer = lineBuffer.slice(nlIdx + 1);
|
|
424
|
-
const rendered = renderMarkdown(complete);
|
|
425
|
-
for (const line of rendered.split('\n')) {
|
|
426
|
-
printToChat(` ${line}`);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
// Flush remaining
|
|
431
|
-
if (lineBuffer.length > 0) {
|
|
432
|
-
const rendered = renderMarkdown(lineBuffer);
|
|
433
|
-
for (const line of rendered.split('\n')) {
|
|
434
|
-
printToChat(` ${line}`);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
catch (err) {
|
|
439
|
-
printToChat(` ${ansi.red}Error: ${err.message}${ansi.reset}`);
|
|
440
|
-
fullResponse = `[Error: ${err.message}]`;
|
|
441
|
-
}
|
|
442
|
-
isStreaming = false;
|
|
443
|
-
printToChat('');
|
|
444
|
-
messages.push({ role: 'assistant', content: fullResponse, timestamp: Date.now() });
|
|
445
|
-
// Trim history
|
|
446
|
-
if (messages.length > 60) {
|
|
447
|
-
messages.splice(0, messages.length - 60);
|
|
448
|
-
}
|
|
449
|
-
// Update status bar (uptime changed)
|
|
450
|
-
renderStatusBar();
|
|
451
|
-
}
|
|
452
|
-
// ── Tab completion ─────────────────────────────────────
|
|
453
|
-
function tabComplete(text) {
|
|
454
|
-
if (!text.startsWith('/'))
|
|
455
|
-
return text;
|
|
456
|
-
const matches = COMMANDS.filter(c => c.startsWith(text));
|
|
457
|
-
if (matches.length === 1)
|
|
458
|
-
return matches[0];
|
|
459
|
-
if (matches.length > 1) {
|
|
460
|
-
printSystemMsg(`Completions: ${matches.join(' ')}`);
|
|
461
|
-
}
|
|
462
|
-
return text;
|
|
463
|
-
}
|
|
464
|
-
// ── Cleanup ────────────────────────────────────────────
|
|
465
|
-
function cleanup() {
|
|
466
|
-
process.stdin.setRawMode?.(false);
|
|
467
|
-
process.stdout.write(ansi.showCursor);
|
|
468
|
-
process.stdout.write(`${CSI}r`); // reset scroll region
|
|
469
|
-
}
|
|
470
|
-
// ── Setup raw mode input ──────────────────────────────
|
|
471
|
-
process.stdout.write(ansi.hideCursor);
|
|
472
|
-
fullRedraw();
|
|
473
|
-
printSystemMsg(`💬 Chat with ${config.agentName} — type /help for commands`);
|
|
474
|
-
printToChat('');
|
|
475
|
-
renderInputBar('');
|
|
476
|
-
// Use readline for input handling — simpler and more compatible
|
|
477
|
-
const rl = readline.createInterface({
|
|
478
|
-
input: process.stdin,
|
|
479
|
-
output: process.stdout,
|
|
480
|
-
terminal: true,
|
|
481
|
-
historySize: 100,
|
|
482
|
-
prompt: '',
|
|
483
|
-
completer: (line) => {
|
|
484
|
-
if (line.startsWith('/')) {
|
|
485
|
-
const matches = COMMANDS.filter(c => c.startsWith(line));
|
|
486
|
-
return [matches, line];
|
|
487
|
-
}
|
|
488
|
-
return [[], line];
|
|
489
|
-
},
|
|
490
|
-
});
|
|
491
|
-
// Override output to avoid readline messing with our TUI
|
|
492
|
-
// We'll use a simpler approach: standard readline but render our TUI around it
|
|
493
|
-
// Reset to simpler mode — full raw TUI is complex with readline
|
|
494
|
-
// Instead, use a hybrid: ANSI decorations + readline for input
|
|
495
|
-
process.stdout.write(ansi.showCursor);
|
|
496
|
-
process.stdout.write(`${CSI}r`); // reset scroll region
|
|
497
|
-
// Simpler but polished approach
|
|
498
|
-
process.stdout.write(ansi.clearScreen + ansi.cursorHome);
|
|
499
|
-
// Print banner
|
|
500
|
-
const w = cols();
|
|
501
|
-
const bannerBg = `${ansi.bgBlue}${ansi.white}${ansi.bold}`;
|
|
502
|
-
const uptimeStr = '0m0s';
|
|
503
|
-
const bannerLeft = ` 🤖 ${config.agentName} v${config.agentVersion}`;
|
|
504
|
-
const bannerMid = ` │ ${config.providerName}/${config.model}`;
|
|
505
|
-
const bannerRight = `⏱${uptimeStr} │ 🔧${config.skillNames.length} skills `;
|
|
506
|
-
const bannerPad = Math.max(0, w - bannerLeft.length - bannerMid.length - bannerRight.length);
|
|
507
|
-
process.stdout.write(`${bannerBg}${bannerLeft}${bannerMid}${' '.repeat(bannerPad)}${bannerRight}${ansi.reset}\n`);
|
|
508
|
-
process.stdout.write(`${ansi.gray}${'─'.repeat(w)}${ansi.reset}\n`);
|
|
509
|
-
// Welcome
|
|
510
|
-
const soulLoaded = fs.existsSync(path.resolve('SOUL.md'));
|
|
511
|
-
const ctxLoaded = fs.existsSync(path.resolve('CONTEXT.md'));
|
|
512
|
-
if (soulLoaded)
|
|
513
|
-
process.stdout.write(`${ansi.gray} ✦ SOUL.md loaded${ansi.reset}\n`);
|
|
514
|
-
if (ctxLoaded)
|
|
515
|
-
process.stdout.write(`${ansi.gray} ✦ CONTEXT.md loaded${ansi.reset}\n`);
|
|
516
|
-
process.stdout.write(`${ansi.gray} Type /help for commands, /quit to exit${ansi.reset}\n`);
|
|
517
|
-
process.stdout.write(`${ansi.gray}${'─'.repeat(w)}${ansi.reset}\n\n`);
|
|
518
|
-
// Now use readline for interactive input
|
|
519
|
-
const prompt = `${ansi.cyan}${ansi.bold}❯ ${ansi.reset}`;
|
|
520
|
-
rl.setPrompt(prompt);
|
|
521
|
-
rl.prompt();
|
|
522
|
-
rl.on('line', async (input) => {
|
|
523
|
-
const text = input.trim();
|
|
524
|
-
if (!text) {
|
|
525
|
-
rl.prompt();
|
|
526
|
-
return;
|
|
527
|
-
}
|
|
528
|
-
// Save to input history
|
|
529
|
-
inputHistory.push(text);
|
|
530
|
-
// Handle slash commands
|
|
531
|
-
if (text.startsWith('/')) {
|
|
532
|
-
if (text.toLowerCase() === '/quit' || text.toLowerCase() === '/exit' || text.toLowerCase() === '/q') {
|
|
533
|
-
process.stdout.write(`\n${ansi.dim}👋 Goodbye!${ansi.reset}\n`);
|
|
534
|
-
rl.close();
|
|
535
|
-
process.exit(0);
|
|
536
|
-
}
|
|
537
|
-
if (text.toLowerCase() === '/help') {
|
|
538
|
-
console.log('');
|
|
539
|
-
console.log(`${ansi.gray} ╭─── Commands ─────────────────────────╮${ansi.reset}`);
|
|
540
|
-
console.log(`${ansi.gray} │ /help Show this help │${ansi.reset}`);
|
|
541
|
-
console.log(`${ansi.gray} │ /clear Clear screen │${ansi.reset}`);
|
|
542
|
-
console.log(`${ansi.gray} │ /model Show current model │${ansi.reset}`);
|
|
543
|
-
console.log(`${ansi.gray} │ /tools List available tools │${ansi.reset}`);
|
|
544
|
-
console.log(`${ansi.gray} │ /skills List skills │${ansi.reset}`);
|
|
545
|
-
console.log(`${ansi.gray} │ /history Export chat history │${ansi.reset}`);
|
|
546
|
-
console.log(`${ansi.gray} │ /status Show agent status │${ansi.reset}`);
|
|
547
|
-
console.log(`${ansi.gray} │ /quit Exit (/q, /exit) │${ansi.reset}`);
|
|
548
|
-
console.log(`${ansi.gray} ╰────────────────────────────────────────╯${ansi.reset}`);
|
|
549
|
-
console.log(`${ansi.gray} Shortcuts: Ctrl+C interrupt | Ctrl+L clear | ↑↓ history${ansi.reset}`);
|
|
550
|
-
console.log('');
|
|
551
|
-
rl.prompt();
|
|
100
|
+
const messages = [
|
|
101
|
+
{ role: 'system', content: systemPrompt },
|
|
102
|
+
];
|
|
103
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
104
|
+
const askQuestion = () => {
|
|
105
|
+
rl.question(c.cyan('你: '), async (input) => {
|
|
106
|
+
const trimmed = input.trim();
|
|
107
|
+
if (!trimmed) {
|
|
108
|
+
askQuestion();
|
|
552
109
|
return;
|
|
553
110
|
}
|
|
554
|
-
if (
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
const w2 = cols();
|
|
558
|
-
process.stdout.write(`${bannerBg} 🤖 ${config.agentName} v${config.agentVersion} │ ${config.providerName}/${config.model}${' '.repeat(Math.max(0, w2 - 60))}${ansi.reset}\n`);
|
|
559
|
-
process.stdout.write(`${ansi.gray}${'─'.repeat(w2)}${ansi.reset}\n`);
|
|
560
|
-
console.log(`${ansi.gray} ✦ Chat cleared${ansi.reset}\n`);
|
|
561
|
-
rl.prompt();
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
if (text.toLowerCase() === '/model') {
|
|
565
|
-
console.log(`\n${ansi.gray} Model: ${ansi.cyan}${config.providerName}/${config.model}${ansi.reset}\n`);
|
|
566
|
-
rl.prompt();
|
|
111
|
+
if (trimmed === '/quit' || trimmed === '/exit' || trimmed === '/q') {
|
|
112
|
+
console.log(c.dim('\n👋 再见!'));
|
|
113
|
+
rl.close();
|
|
567
114
|
return;
|
|
568
115
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
116
|
+
messages.push({ role: 'user', content: trimmed });
|
|
117
|
+
try {
|
|
118
|
+
process.stdout.write(c.green(`${agentName}: `));
|
|
119
|
+
const response = await provider.chat(messages);
|
|
120
|
+
const reply = typeof response === 'string' ? response : response?.content || response?.message?.content || JSON.stringify(response);
|
|
121
|
+
console.log(reply);
|
|
574
122
|
console.log('');
|
|
575
|
-
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
if (text.toLowerCase() === '/skills') {
|
|
579
|
-
if (config.skillNames.length === 0) {
|
|
580
|
-
console.log(`\n${ansi.gray} No skills registered.${ansi.reset}\n`);
|
|
581
|
-
}
|
|
582
|
-
else {
|
|
583
|
-
console.log(`\n${ansi.bold} Skills (${config.skillNames.length}):${ansi.reset}`);
|
|
584
|
-
for (const s of config.skillNames) {
|
|
585
|
-
console.log(`${ansi.gray} • ${ansi.cyan}${s}${ansi.reset}`);
|
|
586
|
-
}
|
|
587
|
-
console.log('');
|
|
588
|
-
}
|
|
589
|
-
rl.prompt();
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
if (text.toLowerCase() === '/history') {
|
|
593
|
-
const histFile = path.resolve(`chat-history-${Date.now()}.md`);
|
|
594
|
-
const content = messages.map(m => {
|
|
595
|
-
const prefix = m.role === 'user' ? '**You**' : m.role === 'assistant' ? `**${config.agentName}**` : '*System*';
|
|
596
|
-
return `${prefix}: ${m.content}`;
|
|
597
|
-
}).join('\n\n');
|
|
598
|
-
fs.writeFileSync(histFile, content);
|
|
599
|
-
console.log(`\n${ansi.gray} ✦ History exported to ${histFile}${ansi.reset}\n`);
|
|
600
|
-
rl.prompt();
|
|
601
|
-
return;
|
|
123
|
+
messages.push({ role: 'assistant', content: reply });
|
|
602
124
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
const h = Math.floor(uptimeSec / 3600);
|
|
606
|
-
const m = Math.floor((uptimeSec % 3600) / 60);
|
|
607
|
-
const s = uptimeSec % 60;
|
|
608
|
-
const upStr = h > 0 ? `${h}h ${m}m ${s}s` : `${m}m ${s}s`;
|
|
609
|
-
const userMsgs = messages.filter(m => m.role === 'user').length;
|
|
125
|
+
catch (err) {
|
|
126
|
+
console.log(c.red(`\n 错误: ${err.message}`));
|
|
610
127
|
console.log('');
|
|
611
|
-
console.log(`${ansi.gray} ╭─── Agent Status ─────────────────────╮${ansi.reset}`);
|
|
612
|
-
console.log(`${ansi.gray} │ Agent: ${ansi.cyan}${config.agentName} v${config.agentVersion}${ansi.reset}${ansi.gray}${' '.repeat(Math.max(0, 28 - config.agentName.length - config.agentVersion.length))}│${ansi.reset}`);
|
|
613
|
-
console.log(`${ansi.gray} │ Model: ${ansi.cyan}${config.providerName}/${config.model}${ansi.reset}${ansi.gray}${' '.repeat(Math.max(0, 28 - config.providerName.length - config.model.length))}│${ansi.reset}`);
|
|
614
|
-
console.log(`${ansi.gray} │ Uptime: ${upStr}${' '.repeat(Math.max(0, 28 - upStr.length))}│${ansi.reset}`);
|
|
615
|
-
console.log(`${ansi.gray} │ Messages: ${userMsgs} sent, ${messages.length} total${' '.repeat(Math.max(0, 14 - String(userMsgs).length - String(messages.length).length))}│${ansi.reset}`);
|
|
616
|
-
console.log(`${ansi.gray} │ Skills: ${config.skillNames.length}${' '.repeat(Math.max(0, 27 - String(config.skillNames.length).length))}│${ansi.reset}`);
|
|
617
|
-
console.log(`${ansi.gray} ╰────────────────────────────────────────╯${ansi.reset}`);
|
|
618
|
-
console.log('');
|
|
619
|
-
rl.prompt();
|
|
620
|
-
return;
|
|
621
|
-
}
|
|
622
|
-
console.log(`\n${ansi.gray} Unknown command: ${text}. Type /help${ansi.reset}\n`);
|
|
623
|
-
rl.prompt();
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
// Regular message
|
|
627
|
-
messages.push({ role: 'user', content: text, timestamp: Date.now() });
|
|
628
|
-
console.log('');
|
|
629
|
-
// Build message array for provider
|
|
630
|
-
const history = messages.map(m => ({
|
|
631
|
-
id: `msg_${m.timestamp}`,
|
|
632
|
-
role: m.role,
|
|
633
|
-
content: m.content,
|
|
634
|
-
timestamp: m.timestamp,
|
|
635
|
-
}));
|
|
636
|
-
process.stdout.write(`${ansi.green}${ansi.bold} ${config.agentName}: ${ansi.reset}`);
|
|
637
|
-
isStreaming = true;
|
|
638
|
-
abortStream = false;
|
|
639
|
-
let fullResponse = '';
|
|
640
|
-
// Handle Ctrl+C during streaming
|
|
641
|
-
const sigintHandler = () => {
|
|
642
|
-
if (isStreaming) {
|
|
643
|
-
abortStream = true;
|
|
644
|
-
}
|
|
645
|
-
};
|
|
646
|
-
process.on('SIGINT', sigintHandler);
|
|
647
|
-
try {
|
|
648
|
-
let lineBuffer = '';
|
|
649
|
-
let firstChunk = true;
|
|
650
|
-
for await (const chunk of provider.chatStream(history, config.systemPrompt)) {
|
|
651
|
-
if (abortStream) {
|
|
652
|
-
process.stdout.write(`\n${ansi.yellow} [interrupted]${ansi.reset}`);
|
|
653
|
-
fullResponse += '\n[interrupted]';
|
|
654
|
-
break;
|
|
655
|
-
}
|
|
656
|
-
fullResponse += chunk;
|
|
657
|
-
// Stream output with markdown rendering for complete lines
|
|
658
|
-
lineBuffer += chunk;
|
|
659
|
-
const nlIdx = lineBuffer.lastIndexOf('\n');
|
|
660
|
-
if (nlIdx >= 0) {
|
|
661
|
-
const complete = lineBuffer.slice(0, nlIdx);
|
|
662
|
-
lineBuffer = lineBuffer.slice(nlIdx + 1);
|
|
663
|
-
if (firstChunk) {
|
|
664
|
-
// First line continues after prefix
|
|
665
|
-
const lines = complete.split('\n');
|
|
666
|
-
const renderedFirst = renderMarkdown(lines[0]);
|
|
667
|
-
process.stdout.write(renderedFirst);
|
|
668
|
-
for (let i = 1; i < lines.length; i++) {
|
|
669
|
-
const rendered = renderMarkdown(lines[i]);
|
|
670
|
-
process.stdout.write(`\n ${rendered}`);
|
|
671
|
-
}
|
|
672
|
-
process.stdout.write('\n');
|
|
673
|
-
firstChunk = false;
|
|
674
|
-
}
|
|
675
|
-
else {
|
|
676
|
-
const rendered = renderMarkdown(complete);
|
|
677
|
-
for (const line of rendered.split('\n')) {
|
|
678
|
-
process.stdout.write(` ${line}\n`);
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
// Flush remaining
|
|
684
|
-
if (lineBuffer.length > 0) {
|
|
685
|
-
const rendered = renderMarkdown(lineBuffer);
|
|
686
|
-
if (firstChunk) {
|
|
687
|
-
process.stdout.write(rendered);
|
|
688
|
-
}
|
|
689
|
-
else {
|
|
690
|
-
for (const line of rendered.split('\n')) {
|
|
691
|
-
process.stdout.write(` ${line}\n`);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
catch (err) {
|
|
697
|
-
process.stdout.write(`\n${ansi.red} Error: ${err.message}${ansi.reset}`);
|
|
698
|
-
fullResponse = `[Error: ${err.message}]`;
|
|
699
|
-
}
|
|
700
|
-
process.removeListener('SIGINT', sigintHandler);
|
|
701
|
-
isStreaming = false;
|
|
702
|
-
console.log('\n');
|
|
703
|
-
messages.push({ role: 'assistant', content: fullResponse, timestamp: Date.now() });
|
|
704
|
-
// Trim history
|
|
705
|
-
if (messages.length > 60) {
|
|
706
|
-
messages.splice(0, messages.length - 60);
|
|
707
|
-
}
|
|
708
|
-
rl.prompt();
|
|
709
|
-
});
|
|
710
|
-
// Ctrl+L to clear
|
|
711
|
-
rl.on('SIGCONT', () => {
|
|
712
|
-
// not standard, handled below
|
|
713
|
-
});
|
|
714
|
-
// Handle Ctrl+C when not streaming
|
|
715
|
-
rl.on('SIGINT', () => {
|
|
716
|
-
if (isStreaming) {
|
|
717
|
-
abortStream = true;
|
|
718
|
-
}
|
|
719
|
-
else {
|
|
720
|
-
console.log(`\n${ansi.dim} (Ctrl+C again or /quit to exit)${ansi.reset}\n`);
|
|
721
|
-
rl.prompt();
|
|
722
|
-
}
|
|
723
|
-
});
|
|
724
|
-
rl.on('close', () => {
|
|
725
|
-
process.stdout.write(`\n${ansi.dim}👋 Goodbye!${ansi.reset}\n`);
|
|
726
|
-
process.exit(0);
|
|
727
|
-
});
|
|
728
|
-
// Ctrl+L clear screen
|
|
729
|
-
if (process.stdin.isTTY) {
|
|
730
|
-
process.stdin.on('keypress', (_ch, key) => {
|
|
731
|
-
if (key && key.ctrl && key.name === 'l') {
|
|
732
|
-
process.stdout.write(ansi.clearScreen + ansi.cursorHome);
|
|
733
|
-
const w2 = cols();
|
|
734
|
-
process.stdout.write(`${bannerBg} 🤖 ${config.agentName} v${config.agentVersion} │ ${config.providerName}/${config.model}${' '.repeat(Math.max(0, w2 - 60))}${ansi.reset}\n`);
|
|
735
|
-
process.stdout.write(`${ansi.gray}${'─'.repeat(w2)}${ansi.reset}\n\n`);
|
|
736
|
-
rl.prompt();
|
|
737
128
|
}
|
|
129
|
+
askQuestion();
|
|
738
130
|
});
|
|
739
|
-
}
|
|
131
|
+
};
|
|
132
|
+
askQuestion();
|
|
740
133
|
}
|
|
741
134
|
//# sourceMappingURL=chat.js.map
|