@stevederico/dotbot 0.24.0 → 0.26.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 +17 -0
- package/README.md +15 -2
- package/bin/dotbot.js +577 -90
- package/dotbot.db +0 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
0.26
|
|
2
|
+
|
|
3
|
+
Update REPL prompt style
|
|
4
|
+
Add visible thinking output
|
|
5
|
+
Add /help, /show, /bye commands
|
|
6
|
+
Add multi-line input mode
|
|
7
|
+
|
|
8
|
+
0.25
|
|
9
|
+
|
|
10
|
+
Add delete subcommands for memory, jobs, tasks, sessions
|
|
11
|
+
Add --json flag for machine-readable output
|
|
12
|
+
Add doctor command for environment check
|
|
13
|
+
Add ~/.dotbotrc config file support
|
|
14
|
+
Add --openai flag for OpenAI-compatible API
|
|
15
|
+
Add pipe support (stdin input)
|
|
16
|
+
Add --session flag to resume conversations
|
|
17
|
+
|
|
1
18
|
0.24
|
|
2
19
|
|
|
3
20
|
Add --system flag for custom prompts
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<img src="https://img.shields.io/github/stars/stevederico/dotbot?style=social" alt="GitHub stars">
|
|
14
14
|
</a>
|
|
15
15
|
<a href="https://github.com/stevederico/dotbot">
|
|
16
|
-
<img src="https://img.shields.io/badge/version-0.
|
|
16
|
+
<img src="https://img.shields.io/badge/version-0.25-green" alt="version">
|
|
17
17
|
</a>
|
|
18
18
|
<img src="https://img.shields.io/badge/LOC-11k-orange" alt="Lines of Code">
|
|
19
19
|
</p>
|
|
@@ -154,28 +154,38 @@ for await (const event of agent.chat({
|
|
|
154
154
|
## CLI Reference
|
|
155
155
|
|
|
156
156
|
```
|
|
157
|
-
dotbot v0.
|
|
157
|
+
dotbot v0.25 — AI agent CLI
|
|
158
158
|
|
|
159
159
|
Usage:
|
|
160
160
|
dotbot "message" One-shot query
|
|
161
161
|
dotbot Interactive chat
|
|
162
162
|
dotbot serve [--port N] Start HTTP server (default: 3000)
|
|
163
|
+
dotbot serve --openai Start OpenAI-compatible API server
|
|
164
|
+
echo "msg" | dotbot Pipe input from stdin
|
|
163
165
|
|
|
164
166
|
Commands:
|
|
167
|
+
doctor Check environment and configuration
|
|
165
168
|
tools List all available tools
|
|
166
169
|
stats Show database statistics
|
|
167
170
|
memory [list|search <q>] Manage saved memories
|
|
171
|
+
memory delete <key> Delete a memory by key
|
|
168
172
|
jobs List scheduled jobs
|
|
173
|
+
jobs delete <id> Delete a scheduled job
|
|
169
174
|
tasks List active tasks
|
|
175
|
+
tasks delete <id> Delete a task
|
|
170
176
|
sessions List chat sessions
|
|
177
|
+
sessions delete <id> Delete a session
|
|
171
178
|
events [--summary] View audit log
|
|
172
179
|
|
|
173
180
|
Options:
|
|
174
181
|
--provider, -p AI provider: xai, anthropic, openai, ollama (default: xai)
|
|
175
182
|
--model, -m Model name (default: grok-4-1-fast-reasoning)
|
|
176
183
|
--system, -s Custom system prompt (prepended to default)
|
|
184
|
+
--session Resume a specific session by ID
|
|
177
185
|
--db SQLite database path (default: ./dotbot.db)
|
|
178
186
|
--port Server port for 'serve' command
|
|
187
|
+
--openai Enable OpenAI-compatible API endpoints
|
|
188
|
+
--json Output as JSON (for inspection commands)
|
|
179
189
|
--verbose Show initialization logs
|
|
180
190
|
--help, -h Show help
|
|
181
191
|
--version, -v Show version
|
|
@@ -185,6 +195,9 @@ Environment Variables:
|
|
|
185
195
|
ANTHROPIC_API_KEY API key for Anthropic
|
|
186
196
|
OPENAI_API_KEY API key for OpenAI
|
|
187
197
|
OLLAMA_BASE_URL Base URL for Ollama (default: http://localhost:11434)
|
|
198
|
+
|
|
199
|
+
Config File:
|
|
200
|
+
~/.dotbotrc JSON config for defaults (provider, model, db)
|
|
188
201
|
```
|
|
189
202
|
|
|
190
203
|
<br />
|
package/bin/dotbot.js
CHANGED
|
@@ -22,11 +22,13 @@ process.emit = function (event, error) {
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import { parseArgs } from 'node:util';
|
|
25
|
-
import { readFileSync } from 'node:fs';
|
|
25
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
26
26
|
import { fileURLToPath } from 'node:url';
|
|
27
27
|
import { dirname, join } from 'node:path';
|
|
28
|
+
import { homedir } from 'node:os';
|
|
28
29
|
import * as readline from 'node:readline';
|
|
29
30
|
import { createServer } from 'node:http';
|
|
31
|
+
import { randomUUID } from 'node:crypto';
|
|
30
32
|
|
|
31
33
|
// Read version from package.json
|
|
32
34
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -59,6 +61,7 @@ async function loadModules() {
|
|
|
59
61
|
}
|
|
60
62
|
const DEFAULT_PORT = 3000;
|
|
61
63
|
const DEFAULT_DB = './dotbot.db';
|
|
64
|
+
const CONFIG_PATH = join(homedir(), '.dotbotrc');
|
|
62
65
|
|
|
63
66
|
// Spinner for tool execution feedback
|
|
64
67
|
let spinnerInterval = null;
|
|
@@ -93,22 +96,32 @@ Usage:
|
|
|
93
96
|
dotbot "message" One-shot query
|
|
94
97
|
dotbot Interactive chat
|
|
95
98
|
dotbot serve [--port N] Start HTTP server (default: ${DEFAULT_PORT})
|
|
99
|
+
dotbot serve --openai Start OpenAI-compatible API server
|
|
100
|
+
echo "msg" | dotbot Pipe input from stdin
|
|
96
101
|
|
|
97
102
|
Commands:
|
|
103
|
+
doctor Check environment and configuration
|
|
98
104
|
tools List all available tools
|
|
99
105
|
stats Show database statistics
|
|
100
106
|
memory [list|search <q>] Manage saved memories
|
|
107
|
+
memory delete <key> Delete a memory by key
|
|
101
108
|
jobs List scheduled jobs
|
|
109
|
+
jobs delete <id> Delete a scheduled job
|
|
102
110
|
tasks List active tasks
|
|
111
|
+
tasks delete <id> Delete a task
|
|
103
112
|
sessions List chat sessions
|
|
113
|
+
sessions delete <id> Delete a session
|
|
104
114
|
events [--summary] View audit log
|
|
105
115
|
|
|
106
116
|
Options:
|
|
107
117
|
--provider, -p AI provider: xai, anthropic, openai, ollama (default: xai)
|
|
108
118
|
--model, -m Model name (default: grok-4-1-fast-reasoning)
|
|
109
119
|
--system, -s Custom system prompt (prepended to default)
|
|
120
|
+
--session Resume a specific session by ID
|
|
110
121
|
--db SQLite database path (default: ${DEFAULT_DB})
|
|
111
122
|
--port Server port for 'serve' command (default: ${DEFAULT_PORT})
|
|
123
|
+
--openai Enable OpenAI-compatible API endpoints (/v1/chat/completions, /v1/models)
|
|
124
|
+
--json Output as JSON (for inspection commands)
|
|
112
125
|
--verbose Show initialization logs
|
|
113
126
|
--help, -h Show this help
|
|
114
127
|
--version, -v Show version
|
|
@@ -119,20 +132,51 @@ Environment Variables:
|
|
|
119
132
|
OPENAI_API_KEY API key for OpenAI
|
|
120
133
|
OLLAMA_BASE_URL Base URL for Ollama (default: http://localhost:11434)
|
|
121
134
|
|
|
135
|
+
Config File:
|
|
136
|
+
~/.dotbotrc JSON config for defaults (provider, model, db)
|
|
137
|
+
|
|
122
138
|
Examples:
|
|
123
139
|
dotbot "What's the weather in SF?"
|
|
124
140
|
dotbot
|
|
125
141
|
dotbot serve --port 8080
|
|
142
|
+
dotbot doctor
|
|
126
143
|
dotbot tools
|
|
127
144
|
dotbot memory search "preferences"
|
|
145
|
+
dotbot memory delete user_pref
|
|
146
|
+
dotbot stats --json
|
|
128
147
|
dotbot --system "You are a pirate" "Hello"
|
|
148
|
+
dotbot --session abc-123 "follow up question"
|
|
149
|
+
echo "What is 2+2?" | dotbot
|
|
150
|
+
cat question.txt | dotbot
|
|
129
151
|
`);
|
|
130
152
|
}
|
|
131
153
|
|
|
132
154
|
/**
|
|
133
|
-
*
|
|
155
|
+
* Load config from ~/.dotbotrc if it exists.
|
|
156
|
+
*
|
|
157
|
+
* @returns {Object} Config object or empty object if not found
|
|
158
|
+
*/
|
|
159
|
+
function loadConfig() {
|
|
160
|
+
if (!existsSync(CONFIG_PATH)) {
|
|
161
|
+
return {};
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const content = readFileSync(CONFIG_PATH, 'utf8');
|
|
165
|
+
return JSON.parse(content);
|
|
166
|
+
} catch (err) {
|
|
167
|
+
console.error(`Warning: Invalid config file at ${CONFIG_PATH}: ${err.message}`);
|
|
168
|
+
return {};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Parse CLI arguments with config file fallback.
|
|
174
|
+
*
|
|
175
|
+
* @returns {Object} Merged CLI args and config values
|
|
134
176
|
*/
|
|
135
177
|
function parseCliArgs() {
|
|
178
|
+
const config = loadConfig();
|
|
179
|
+
|
|
136
180
|
try {
|
|
137
181
|
const { values, positionals } = parseArgs({
|
|
138
182
|
allowPositionals: true,
|
|
@@ -140,15 +184,29 @@ function parseCliArgs() {
|
|
|
140
184
|
help: { type: 'boolean', short: 'h', default: false },
|
|
141
185
|
version: { type: 'boolean', short: 'v', default: false },
|
|
142
186
|
verbose: { type: 'boolean', default: false },
|
|
143
|
-
provider: { type: 'string', short: 'p'
|
|
144
|
-
model: { type: 'string', short: 'm'
|
|
145
|
-
system: { type: 'string', short: 's'
|
|
187
|
+
provider: { type: 'string', short: 'p' },
|
|
188
|
+
model: { type: 'string', short: 'm' },
|
|
189
|
+
system: { type: 'string', short: 's' },
|
|
146
190
|
summary: { type: 'boolean', default: false },
|
|
147
|
-
|
|
148
|
-
|
|
191
|
+
json: { type: 'boolean', default: false },
|
|
192
|
+
db: { type: 'string' },
|
|
193
|
+
port: { type: 'string' },
|
|
194
|
+
openai: { type: 'boolean', default: false },
|
|
195
|
+
session: { type: 'string', default: '' },
|
|
149
196
|
},
|
|
150
197
|
});
|
|
151
|
-
|
|
198
|
+
|
|
199
|
+
// Merge: CLI args > config file > hardcoded defaults
|
|
200
|
+
return {
|
|
201
|
+
...values,
|
|
202
|
+
provider: values.provider ?? config.provider ?? 'xai',
|
|
203
|
+
model: values.model ?? config.model ?? 'grok-4-1-fast-reasoning',
|
|
204
|
+
system: values.system ?? config.system ?? '',
|
|
205
|
+
db: values.db ?? config.db ?? DEFAULT_DB,
|
|
206
|
+
port: values.port ?? config.port ?? String(DEFAULT_PORT),
|
|
207
|
+
session: values.session ?? '',
|
|
208
|
+
positionals,
|
|
209
|
+
};
|
|
152
210
|
} catch (err) {
|
|
153
211
|
console.error(`Error: ${err.message}`);
|
|
154
212
|
process.exit(1);
|
|
@@ -251,7 +309,20 @@ async function runChat(message, options) {
|
|
|
251
309
|
const storesObj = await initStores(options.db, options.verbose, options.system);
|
|
252
310
|
const provider = await getProviderConfig(options.provider);
|
|
253
311
|
|
|
254
|
-
|
|
312
|
+
let session;
|
|
313
|
+
let messages;
|
|
314
|
+
|
|
315
|
+
if (options.session) {
|
|
316
|
+
session = await storesObj.sessionStore.getSession(options.session, 'cli-user');
|
|
317
|
+
if (!session) {
|
|
318
|
+
console.error(`Error: Session not found: ${options.session}`);
|
|
319
|
+
process.exit(1);
|
|
320
|
+
}
|
|
321
|
+
messages = [...(session.messages || []), { role: 'user', content: message }];
|
|
322
|
+
} else {
|
|
323
|
+
session = await storesObj.sessionStore.createSession('cli-user', options.model, options.provider);
|
|
324
|
+
messages = [{ role: 'user', content: message }];
|
|
325
|
+
}
|
|
255
326
|
|
|
256
327
|
const context = {
|
|
257
328
|
userID: 'cli-user',
|
|
@@ -260,10 +331,8 @@ async function runChat(message, options) {
|
|
|
260
331
|
...storesObj,
|
|
261
332
|
};
|
|
262
333
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
process.stdout.write('\n[thinking] ');
|
|
266
|
-
startSpinner();
|
|
334
|
+
let isThinking = false;
|
|
335
|
+
let thinkingDone = false;
|
|
267
336
|
|
|
268
337
|
for await (const event of agentLoop({
|
|
269
338
|
model: options.model,
|
|
@@ -274,14 +343,28 @@ async function runChat(message, options) {
|
|
|
274
343
|
})) {
|
|
275
344
|
switch (event.type) {
|
|
276
345
|
case 'thinking':
|
|
277
|
-
|
|
346
|
+
if (!isThinking) {
|
|
347
|
+
process.stdout.write('Thinking...\n');
|
|
348
|
+
isThinking = true;
|
|
349
|
+
}
|
|
350
|
+
if (event.text) {
|
|
351
|
+
process.stdout.write(event.text);
|
|
352
|
+
}
|
|
278
353
|
break;
|
|
279
354
|
case 'text_delta':
|
|
280
|
-
|
|
355
|
+
if (isThinking && !thinkingDone) {
|
|
356
|
+
stopSpinner('');
|
|
357
|
+
process.stdout.write('\n...done thinking.\n\n');
|
|
358
|
+
thinkingDone = true;
|
|
359
|
+
}
|
|
281
360
|
process.stdout.write(event.text);
|
|
282
361
|
break;
|
|
283
362
|
case 'tool_start':
|
|
284
|
-
|
|
363
|
+
if (isThinking && !thinkingDone) {
|
|
364
|
+
stopSpinner('');
|
|
365
|
+
process.stdout.write('\n...done thinking.\n\n');
|
|
366
|
+
thinkingDone = true;
|
|
367
|
+
}
|
|
285
368
|
process.stdout.write(`[${event.name}] `);
|
|
286
369
|
startSpinner();
|
|
287
370
|
break;
|
|
@@ -310,8 +393,20 @@ async function runRepl(options) {
|
|
|
310
393
|
const storesObj = await initStores(options.db, options.verbose, options.system);
|
|
311
394
|
const provider = await getProviderConfig(options.provider);
|
|
312
395
|
|
|
313
|
-
|
|
314
|
-
|
|
396
|
+
let session;
|
|
397
|
+
let messages;
|
|
398
|
+
|
|
399
|
+
if (options.session) {
|
|
400
|
+
session = await storesObj.sessionStore.getSession(options.session, 'cli-user');
|
|
401
|
+
if (!session) {
|
|
402
|
+
console.error(`Error: Session not found: ${options.session}`);
|
|
403
|
+
process.exit(1);
|
|
404
|
+
}
|
|
405
|
+
messages = [...(session.messages || [])];
|
|
406
|
+
} else {
|
|
407
|
+
session = await storesObj.sessionStore.createSession('cli-user', options.model, options.provider);
|
|
408
|
+
messages = [];
|
|
409
|
+
}
|
|
315
410
|
|
|
316
411
|
const context = {
|
|
317
412
|
userID: 'cli-user',
|
|
@@ -326,18 +421,58 @@ async function runRepl(options) {
|
|
|
326
421
|
});
|
|
327
422
|
|
|
328
423
|
console.log(`\ndotbot v${VERSION} — ${options.provider}/${options.model}`);
|
|
329
|
-
|
|
424
|
+
if (options.session) {
|
|
425
|
+
console.log(`Resuming session: ${session.id}`);
|
|
426
|
+
}
|
|
427
|
+
console.log('Type /? for help\n');
|
|
428
|
+
|
|
429
|
+
const showHelp = () => {
|
|
430
|
+
console.log('Available Commands:');
|
|
431
|
+
console.log(' /show Show model information');
|
|
432
|
+
console.log(' /clear Clear session context');
|
|
433
|
+
console.log(' /bye Exit');
|
|
434
|
+
console.log(' /?, /help Help for a command');
|
|
435
|
+
console.log('');
|
|
436
|
+
console.log('Use """ to begin a multi-line message.\n');
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const showModel = () => {
|
|
440
|
+
console.log(` Model: ${options.model}`);
|
|
441
|
+
console.log(` Provider: ${options.provider}`);
|
|
442
|
+
console.log(` Session: ${session.id}\n`);
|
|
443
|
+
};
|
|
330
444
|
|
|
331
|
-
const
|
|
332
|
-
rl.question('
|
|
445
|
+
const promptUser = () => {
|
|
446
|
+
rl.question('>>> ', async (input) => {
|
|
333
447
|
const trimmed = input.trim();
|
|
334
448
|
|
|
335
449
|
if (!trimmed) {
|
|
336
|
-
|
|
450
|
+
promptUser();
|
|
337
451
|
return;
|
|
338
452
|
}
|
|
339
453
|
|
|
340
|
-
|
|
454
|
+
// Multi-line input mode
|
|
455
|
+
if (trimmed === '"""') {
|
|
456
|
+
let multiLine = '';
|
|
457
|
+
const collectLines = () => {
|
|
458
|
+
rl.question('... ', (line) => {
|
|
459
|
+
if (line.trim() === '"""') {
|
|
460
|
+
if (multiLine.trim()) {
|
|
461
|
+
handleMessage(multiLine.trim());
|
|
462
|
+
} else {
|
|
463
|
+
promptUser();
|
|
464
|
+
}
|
|
465
|
+
} else {
|
|
466
|
+
multiLine += (multiLine ? '\n' : '') + line;
|
|
467
|
+
collectLines();
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
};
|
|
471
|
+
collectLines();
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (trimmed === '/bye' || trimmed === '/quit' || trimmed === '/exit') {
|
|
341
476
|
console.log('Goodbye!');
|
|
342
477
|
rl.close();
|
|
343
478
|
process.exit(0);
|
|
@@ -346,64 +481,94 @@ async function runRepl(options) {
|
|
|
346
481
|
if (trimmed === '/clear') {
|
|
347
482
|
messages.length = 0;
|
|
348
483
|
console.log('Conversation cleared.\n');
|
|
349
|
-
|
|
484
|
+
promptUser();
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (trimmed === '/?' || trimmed === '/help') {
|
|
489
|
+
showHelp();
|
|
490
|
+
promptUser();
|
|
350
491
|
return;
|
|
351
492
|
}
|
|
352
493
|
|
|
353
|
-
|
|
494
|
+
if (trimmed === '/show') {
|
|
495
|
+
showModel();
|
|
496
|
+
promptUser();
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
354
499
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
500
|
+
await handleMessage(trimmed);
|
|
501
|
+
});
|
|
502
|
+
};
|
|
358
503
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
504
|
+
const handleMessage = async (text) => {
|
|
505
|
+
messages.push({ role: 'user', content: text });
|
|
506
|
+
|
|
507
|
+
let isThinking = false;
|
|
508
|
+
let thinkingDone = false;
|
|
509
|
+
let assistantContent = '';
|
|
510
|
+
|
|
511
|
+
try {
|
|
512
|
+
for await (const event of agentLoop({
|
|
513
|
+
model: options.model,
|
|
514
|
+
messages: [...messages],
|
|
515
|
+
tools: coreTools,
|
|
516
|
+
provider,
|
|
517
|
+
context,
|
|
518
|
+
})) {
|
|
519
|
+
switch (event.type) {
|
|
520
|
+
case 'thinking':
|
|
521
|
+
if (!isThinking) {
|
|
522
|
+
process.stdout.write('Thinking...\n');
|
|
523
|
+
isThinking = true;
|
|
524
|
+
}
|
|
525
|
+
if (event.text) {
|
|
373
526
|
process.stdout.write(event.text);
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
527
|
+
}
|
|
528
|
+
break;
|
|
529
|
+
case 'text_delta':
|
|
530
|
+
if (isThinking && !thinkingDone) {
|
|
531
|
+
stopSpinner('');
|
|
532
|
+
process.stdout.write('\n...done thinking.\n\n');
|
|
533
|
+
thinkingDone = true;
|
|
534
|
+
}
|
|
535
|
+
process.stdout.write(event.text);
|
|
536
|
+
assistantContent += event.text;
|
|
537
|
+
break;
|
|
538
|
+
case 'tool_start':
|
|
539
|
+
if (isThinking && !thinkingDone) {
|
|
540
|
+
stopSpinner('');
|
|
541
|
+
process.stdout.write('\n...done thinking.\n\n');
|
|
542
|
+
thinkingDone = true;
|
|
543
|
+
}
|
|
544
|
+
process.stdout.write(`[${event.name}] `);
|
|
545
|
+
startSpinner();
|
|
546
|
+
break;
|
|
547
|
+
case 'tool_result':
|
|
548
|
+
stopSpinner('done');
|
|
549
|
+
break;
|
|
550
|
+
case 'tool_error':
|
|
551
|
+
stopSpinner('error');
|
|
552
|
+
break;
|
|
553
|
+
case 'error':
|
|
554
|
+
stopSpinner();
|
|
555
|
+
console.error(`\nError: ${event.error}`);
|
|
556
|
+
break;
|
|
392
557
|
}
|
|
558
|
+
}
|
|
393
559
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
} catch (err) {
|
|
398
|
-
console.error(`\nError: ${err.message}`);
|
|
560
|
+
if (assistantContent) {
|
|
561
|
+
messages.push({ role: 'assistant', content: assistantContent });
|
|
399
562
|
}
|
|
563
|
+
} catch (err) {
|
|
564
|
+
console.error(`\nError: ${err.message}`);
|
|
565
|
+
}
|
|
400
566
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
});
|
|
567
|
+
process.stdout.write('\n\n');
|
|
568
|
+
promptUser();
|
|
404
569
|
};
|
|
405
570
|
|
|
406
|
-
|
|
571
|
+
promptUser();
|
|
407
572
|
}
|
|
408
573
|
|
|
409
574
|
/**
|
|
@@ -436,6 +601,140 @@ async function runServer(options) {
|
|
|
436
601
|
return;
|
|
437
602
|
}
|
|
438
603
|
|
|
604
|
+
// OpenAI-compatible endpoints (when --openai flag is set)
|
|
605
|
+
if (options.openai) {
|
|
606
|
+
// GET /v1/models - list available models
|
|
607
|
+
if (req.method === 'GET' && url.pathname === '/v1/models') {
|
|
608
|
+
const models = [
|
|
609
|
+
{ id: 'grok-3', object: 'model', owned_by: 'xai' },
|
|
610
|
+
{ id: 'grok-4-1-fast-reasoning', object: 'model', owned_by: 'xai' },
|
|
611
|
+
{ id: 'claude-sonnet-4-5', object: 'model', owned_by: 'anthropic' },
|
|
612
|
+
{ id: 'claude-opus-4', object: 'model', owned_by: 'anthropic' },
|
|
613
|
+
{ id: 'gpt-4o', object: 'model', owned_by: 'openai' },
|
|
614
|
+
];
|
|
615
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
616
|
+
res.end(JSON.stringify({ object: 'list', data: models }));
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// POST /v1/chat/completions - OpenAI-compatible chat endpoint
|
|
621
|
+
if (req.method === 'POST' && url.pathname === '/v1/chat/completions') {
|
|
622
|
+
let body = '';
|
|
623
|
+
for await (const chunk of req) body += chunk;
|
|
624
|
+
|
|
625
|
+
try {
|
|
626
|
+
const { model = 'grok-4-1-fast-reasoning', messages: reqMessages, stream = true } = JSON.parse(body);
|
|
627
|
+
|
|
628
|
+
if (!reqMessages || !Array.isArray(reqMessages)) {
|
|
629
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
630
|
+
res.end(JSON.stringify({ error: { message: 'messages array required', type: 'invalid_request_error' } }));
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Determine provider from model name
|
|
635
|
+
let providerId = 'xai';
|
|
636
|
+
if (model.startsWith('claude')) providerId = 'anthropic';
|
|
637
|
+
else if (model.startsWith('gpt')) providerId = 'openai';
|
|
638
|
+
else if (model.startsWith('llama') || model.startsWith('mistral')) providerId = 'ollama';
|
|
639
|
+
|
|
640
|
+
const provider = await getProviderConfig(providerId);
|
|
641
|
+
const session = await storesObj.sessionStore.createSession('api-user', model, providerId);
|
|
642
|
+
|
|
643
|
+
const context = {
|
|
644
|
+
userID: 'api-user',
|
|
645
|
+
sessionId: session.id,
|
|
646
|
+
providers: { [providerId]: { apiKey: process.env[AI_PROVIDERS[providerId]?.envKey] } },
|
|
647
|
+
...storesObj,
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
const completionId = `chatcmpl-${randomUUID()}`;
|
|
651
|
+
const created = Math.floor(Date.now() / 1000);
|
|
652
|
+
|
|
653
|
+
if (stream) {
|
|
654
|
+
res.writeHead(200, {
|
|
655
|
+
'Content-Type': 'text/event-stream',
|
|
656
|
+
'Cache-Control': 'no-cache',
|
|
657
|
+
'Connection': 'keep-alive',
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
// Send initial role chunk
|
|
661
|
+
const roleChunk = {
|
|
662
|
+
id: completionId,
|
|
663
|
+
object: 'chat.completion.chunk',
|
|
664
|
+
created,
|
|
665
|
+
model,
|
|
666
|
+
choices: [{ index: 0, delta: { role: 'assistant' }, finish_reason: null }],
|
|
667
|
+
};
|
|
668
|
+
res.write(`data: ${JSON.stringify(roleChunk)}\n\n`);
|
|
669
|
+
|
|
670
|
+
for await (const event of agentLoop({
|
|
671
|
+
model,
|
|
672
|
+
messages: reqMessages,
|
|
673
|
+
tools: coreTools,
|
|
674
|
+
provider,
|
|
675
|
+
context,
|
|
676
|
+
})) {
|
|
677
|
+
if (event.type === 'text_delta') {
|
|
678
|
+
const chunk = {
|
|
679
|
+
id: completionId,
|
|
680
|
+
object: 'chat.completion.chunk',
|
|
681
|
+
created,
|
|
682
|
+
model,
|
|
683
|
+
choices: [{ index: 0, delta: { content: event.text }, finish_reason: null }],
|
|
684
|
+
};
|
|
685
|
+
res.write(`data: ${JSON.stringify(chunk)}\n\n`);
|
|
686
|
+
} else if (event.type === 'done') {
|
|
687
|
+
const finalChunk = {
|
|
688
|
+
id: completionId,
|
|
689
|
+
object: 'chat.completion.chunk',
|
|
690
|
+
created,
|
|
691
|
+
model,
|
|
692
|
+
choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
|
|
693
|
+
};
|
|
694
|
+
res.write(`data: ${JSON.stringify(finalChunk)}\n\n`);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
res.write('data: [DONE]\n\n');
|
|
699
|
+
res.end();
|
|
700
|
+
} else {
|
|
701
|
+
// Non-streaming response
|
|
702
|
+
let fullContent = '';
|
|
703
|
+
for await (const event of agentLoop({
|
|
704
|
+
model,
|
|
705
|
+
messages: reqMessages,
|
|
706
|
+
tools: coreTools,
|
|
707
|
+
provider,
|
|
708
|
+
context,
|
|
709
|
+
})) {
|
|
710
|
+
if (event.type === 'text_delta') {
|
|
711
|
+
fullContent += event.text;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const response = {
|
|
716
|
+
id: completionId,
|
|
717
|
+
object: 'chat.completion',
|
|
718
|
+
created,
|
|
719
|
+
model,
|
|
720
|
+
choices: [{
|
|
721
|
+
index: 0,
|
|
722
|
+
message: { role: 'assistant', content: fullContent },
|
|
723
|
+
finish_reason: 'stop',
|
|
724
|
+
}],
|
|
725
|
+
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
|
|
726
|
+
};
|
|
727
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
728
|
+
res.end(JSON.stringify(response));
|
|
729
|
+
}
|
|
730
|
+
} catch (err) {
|
|
731
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
732
|
+
res.end(JSON.stringify({ error: { message: err.message, type: 'server_error' } }));
|
|
733
|
+
}
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
439
738
|
// Chat endpoint
|
|
440
739
|
if (req.method === 'POST' && url.pathname === '/chat') {
|
|
441
740
|
let body = '';
|
|
@@ -503,15 +802,30 @@ async function runServer(options) {
|
|
|
503
802
|
console.log(`Listening on http://localhost:${port}`);
|
|
504
803
|
console.log(`\nEndpoints:`);
|
|
505
804
|
console.log(` GET /health Health check`);
|
|
506
|
-
console.log(` POST /chat Send message (SSE stream)
|
|
805
|
+
console.log(` POST /chat Send message (SSE stream)`);
|
|
806
|
+
if (options.openai) {
|
|
807
|
+
console.log(`\nOpenAI-compatible API:`);
|
|
808
|
+
console.log(` GET /v1/models List available models`);
|
|
809
|
+
console.log(` POST /v1/chat/completions Chat completions (SSE stream)`);
|
|
810
|
+
}
|
|
811
|
+
console.log();
|
|
507
812
|
});
|
|
508
813
|
}
|
|
509
814
|
|
|
510
815
|
/**
|
|
511
816
|
* List all available tools.
|
|
817
|
+
*
|
|
818
|
+
* @param {Object} options - CLI options
|
|
512
819
|
*/
|
|
513
|
-
async function runTools() {
|
|
820
|
+
async function runTools(options) {
|
|
514
821
|
await loadModules();
|
|
822
|
+
|
|
823
|
+
if (options.json) {
|
|
824
|
+
const toolList = coreTools.map((t) => ({ name: t.name, description: t.description }));
|
|
825
|
+
console.log(JSON.stringify(toolList));
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
|
|
515
829
|
console.log(`\ndotbot tools (${coreTools.length})\n`);
|
|
516
830
|
|
|
517
831
|
// Group tools by category based on name prefix
|
|
@@ -539,27 +853,39 @@ async function runTools() {
|
|
|
539
853
|
async function runStats(options) {
|
|
540
854
|
const storesObj = await initStores(options.db, options.verbose, options.system);
|
|
541
855
|
|
|
542
|
-
console.log(`\ndotbot stats\n`);
|
|
543
|
-
console.log(` Database: ${options.db}`);
|
|
544
|
-
|
|
545
856
|
// Sessions
|
|
546
857
|
const sessions = await storesObj.sessionStore.listSessions('cli-user');
|
|
547
|
-
console.log(` Sessions: ${sessions.length}`);
|
|
548
858
|
|
|
549
859
|
// Memory
|
|
550
860
|
const memories = await storesObj.memoryStore.getAllMemories('cli-user');
|
|
551
|
-
console.log(` Memories: ${memories.length}`);
|
|
552
861
|
|
|
553
862
|
// Jobs (need to get session IDs first)
|
|
554
863
|
const jobs = await storesObj.cronStore.listTasksBySessionIds(['default'], 'cli-user');
|
|
555
|
-
console.log(` Jobs: ${jobs.length}`);
|
|
556
864
|
|
|
557
865
|
// Tasks
|
|
558
866
|
const tasks = await storesObj.taskStore.getTasks('cli-user');
|
|
559
|
-
console.log(` Tasks: ${tasks.length}`);
|
|
560
867
|
|
|
561
868
|
// Triggers
|
|
562
869
|
const triggers = await storesObj.triggerStore.listTriggers('cli-user');
|
|
870
|
+
|
|
871
|
+
if (options.json) {
|
|
872
|
+
console.log(JSON.stringify({
|
|
873
|
+
database: options.db,
|
|
874
|
+
sessions: sessions.length,
|
|
875
|
+
memories: memories.length,
|
|
876
|
+
jobs: jobs.length,
|
|
877
|
+
tasks: tasks.length,
|
|
878
|
+
triggers: triggers.length,
|
|
879
|
+
}));
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
console.log(`\ndotbot stats\n`);
|
|
884
|
+
console.log(` Database: ${options.db}`);
|
|
885
|
+
console.log(` Sessions: ${sessions.length}`);
|
|
886
|
+
console.log(` Memories: ${memories.length}`);
|
|
887
|
+
console.log(` Jobs: ${jobs.length}`);
|
|
888
|
+
console.log(` Tasks: ${tasks.length}`);
|
|
563
889
|
console.log(` Triggers: ${triggers.length}`);
|
|
564
890
|
|
|
565
891
|
console.log();
|
|
@@ -569,14 +895,28 @@ async function runStats(options) {
|
|
|
569
895
|
* Manage memories.
|
|
570
896
|
*
|
|
571
897
|
* @param {Object} options - CLI options
|
|
572
|
-
* @param {string} subcommand - list or
|
|
573
|
-
* @param {string} query - Search query
|
|
898
|
+
* @param {string} subcommand - list, search, or delete
|
|
899
|
+
* @param {string} query - Search query or key to delete
|
|
574
900
|
*/
|
|
575
901
|
async function runMemory(options, subcommand, query) {
|
|
576
902
|
const storesObj = await initStores(options.db, options.verbose, options.system);
|
|
577
903
|
|
|
904
|
+
if (subcommand === 'delete' && query) {
|
|
905
|
+
const result = await storesObj.memoryStore.deleteMemory('cli-user', query);
|
|
906
|
+
if (options.json) {
|
|
907
|
+
console.log(JSON.stringify({ deleted: result, key: query }));
|
|
908
|
+
} else {
|
|
909
|
+
console.log(result ? `\nDeleted memory: ${query}\n` : `\nMemory not found: ${query}\n`);
|
|
910
|
+
}
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
|
|
578
914
|
if (subcommand === 'search' && query) {
|
|
579
915
|
const results = await storesObj.memoryStore.readMemoryPattern('cli-user', `%${query}%`);
|
|
916
|
+
if (options.json) {
|
|
917
|
+
console.log(JSON.stringify(results));
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
580
920
|
console.log(`\nMemory search: "${query}" (${results.length} results)\n`);
|
|
581
921
|
for (const mem of results) {
|
|
582
922
|
const val = typeof mem.value === 'string' ? mem.value : JSON.stringify(mem.value);
|
|
@@ -584,6 +924,10 @@ async function runMemory(options, subcommand, query) {
|
|
|
584
924
|
}
|
|
585
925
|
} else {
|
|
586
926
|
const memories = await storesObj.memoryStore.getAllMemories('cli-user');
|
|
927
|
+
if (options.json) {
|
|
928
|
+
console.log(JSON.stringify(memories));
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
587
931
|
console.log(`\nMemories (${memories.length})\n`);
|
|
588
932
|
for (const mem of memories) {
|
|
589
933
|
const val = typeof mem.value === 'string' ? mem.value : JSON.stringify(mem.value);
|
|
@@ -594,14 +938,32 @@ async function runMemory(options, subcommand, query) {
|
|
|
594
938
|
}
|
|
595
939
|
|
|
596
940
|
/**
|
|
597
|
-
*
|
|
941
|
+
* Manage scheduled jobs.
|
|
598
942
|
*
|
|
599
943
|
* @param {Object} options - CLI options
|
|
944
|
+
* @param {string} subcommand - list or delete
|
|
945
|
+
* @param {string} jobId - Job ID to delete
|
|
600
946
|
*/
|
|
601
|
-
async function runJobs(options) {
|
|
947
|
+
async function runJobs(options, subcommand, jobId) {
|
|
602
948
|
const storesObj = await initStores(options.db, options.verbose, options.system);
|
|
603
949
|
|
|
950
|
+
if (subcommand === 'delete' && jobId) {
|
|
951
|
+
const result = await storesObj.cronStore.deleteTask(jobId);
|
|
952
|
+
if (options.json) {
|
|
953
|
+
console.log(JSON.stringify({ deleted: result, id: jobId }));
|
|
954
|
+
} else {
|
|
955
|
+
console.log(result ? `\nDeleted job: ${jobId}\n` : `\nJob not found: ${jobId}\n`);
|
|
956
|
+
}
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
|
|
604
960
|
const jobs = await storesObj.cronStore.listTasksBySessionIds(['default'], 'cli-user');
|
|
961
|
+
|
|
962
|
+
if (options.json) {
|
|
963
|
+
console.log(JSON.stringify(jobs));
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
|
|
605
967
|
console.log(`\nScheduled jobs (${jobs.length})\n`);
|
|
606
968
|
|
|
607
969
|
for (const job of jobs) {
|
|
@@ -617,14 +979,32 @@ async function runJobs(options) {
|
|
|
617
979
|
}
|
|
618
980
|
|
|
619
981
|
/**
|
|
620
|
-
*
|
|
982
|
+
* Manage active tasks.
|
|
621
983
|
*
|
|
622
984
|
* @param {Object} options - CLI options
|
|
985
|
+
* @param {string} subcommand - list or delete
|
|
986
|
+
* @param {string} taskId - Task ID to delete
|
|
623
987
|
*/
|
|
624
|
-
async function runTasks(options) {
|
|
988
|
+
async function runTasks(options, subcommand, taskId) {
|
|
625
989
|
const storesObj = await initStores(options.db, options.verbose, options.system);
|
|
626
990
|
|
|
991
|
+
if (subcommand === 'delete' && taskId) {
|
|
992
|
+
const result = await storesObj.taskStore.deleteTask('cli-user', taskId);
|
|
993
|
+
if (options.json) {
|
|
994
|
+
console.log(JSON.stringify({ deleted: result, id: taskId }));
|
|
995
|
+
} else {
|
|
996
|
+
console.log(result ? `\nDeleted task: ${taskId}\n` : `\nTask not found: ${taskId}\n`);
|
|
997
|
+
}
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
|
|
627
1001
|
const tasks = await storesObj.taskStore.getTasks('cli-user');
|
|
1002
|
+
|
|
1003
|
+
if (options.json) {
|
|
1004
|
+
console.log(JSON.stringify(tasks));
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
628
1008
|
console.log(`\nTasks (${tasks.length})\n`);
|
|
629
1009
|
|
|
630
1010
|
for (const task of tasks) {
|
|
@@ -639,14 +1019,32 @@ async function runTasks(options) {
|
|
|
639
1019
|
}
|
|
640
1020
|
|
|
641
1021
|
/**
|
|
642
|
-
*
|
|
1022
|
+
* Manage chat sessions.
|
|
643
1023
|
*
|
|
644
1024
|
* @param {Object} options - CLI options
|
|
1025
|
+
* @param {string} subcommand - list or delete
|
|
1026
|
+
* @param {string} sessionId - Session ID to delete
|
|
645
1027
|
*/
|
|
646
|
-
async function runSessions(options) {
|
|
1028
|
+
async function runSessions(options, subcommand, sessionId) {
|
|
647
1029
|
const storesObj = await initStores(options.db, options.verbose, options.system);
|
|
648
1030
|
|
|
1031
|
+
if (subcommand === 'delete' && sessionId) {
|
|
1032
|
+
const result = await storesObj.sessionStore.deleteSession(sessionId, 'cli-user');
|
|
1033
|
+
if (options.json) {
|
|
1034
|
+
console.log(JSON.stringify({ deleted: result, id: sessionId }));
|
|
1035
|
+
} else {
|
|
1036
|
+
console.log(result ? `\nDeleted session: ${sessionId}\n` : `\nSession not found: ${sessionId}\n`);
|
|
1037
|
+
}
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
649
1041
|
const sessions = await storesObj.sessionStore.listSessions('cli-user');
|
|
1042
|
+
|
|
1043
|
+
if (options.json) {
|
|
1044
|
+
console.log(JSON.stringify(sessions));
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
650
1048
|
console.log(`\nSessions (${sessions.length})\n`);
|
|
651
1049
|
|
|
652
1050
|
for (const session of sessions) {
|
|
@@ -670,6 +1068,10 @@ async function runEvents(options) {
|
|
|
670
1068
|
|
|
671
1069
|
if (options.summary) {
|
|
672
1070
|
const summary = await storesObj.eventStore.summary({ userId: 'cli-user' });
|
|
1071
|
+
if (options.json) {
|
|
1072
|
+
console.log(JSON.stringify(summary));
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
673
1075
|
console.log(`\nEvent summary\n`);
|
|
674
1076
|
console.log(` Total events: ${summary.total || 0}`);
|
|
675
1077
|
if (summary.breakdown) {
|
|
@@ -679,6 +1081,10 @@ async function runEvents(options) {
|
|
|
679
1081
|
}
|
|
680
1082
|
} else {
|
|
681
1083
|
const events = await storesObj.eventStore.query({ userId: 'cli-user', limit: 20 });
|
|
1084
|
+
if (options.json) {
|
|
1085
|
+
console.log(JSON.stringify(events));
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
682
1088
|
console.log(`\nRecent events (${events.length})\n`);
|
|
683
1089
|
|
|
684
1090
|
for (const event of events) {
|
|
@@ -693,6 +1099,71 @@ async function runEvents(options) {
|
|
|
693
1099
|
console.log();
|
|
694
1100
|
}
|
|
695
1101
|
|
|
1102
|
+
/**
|
|
1103
|
+
* Check environment and configuration.
|
|
1104
|
+
*
|
|
1105
|
+
* @param {Object} options - CLI options
|
|
1106
|
+
*/
|
|
1107
|
+
async function runDoctor(options) {
|
|
1108
|
+
console.log(`\ndotbot doctor\n`);
|
|
1109
|
+
|
|
1110
|
+
// Node.js version
|
|
1111
|
+
const nodeVersion = process.version;
|
|
1112
|
+
const nodeMajor = parseInt(nodeVersion.slice(1).split('.')[0], 10);
|
|
1113
|
+
const nodeOk = nodeMajor >= 22;
|
|
1114
|
+
console.log(` Node.js: ${nodeVersion} ${nodeOk ? '\u2713' : '\u2717 (requires >= 22.0.0)'}`);
|
|
1115
|
+
|
|
1116
|
+
// Database check
|
|
1117
|
+
const dbPath = options.db;
|
|
1118
|
+
let dbOk = false;
|
|
1119
|
+
try {
|
|
1120
|
+
if (existsSync(dbPath)) {
|
|
1121
|
+
// Try to open database
|
|
1122
|
+
const { DatabaseSync } = await import('node:sqlite');
|
|
1123
|
+
const db = new DatabaseSync(dbPath, { open: true });
|
|
1124
|
+
db.close();
|
|
1125
|
+
dbOk = true;
|
|
1126
|
+
} else {
|
|
1127
|
+
dbOk = true; // Will be created on first use
|
|
1128
|
+
}
|
|
1129
|
+
} catch {
|
|
1130
|
+
dbOk = false;
|
|
1131
|
+
}
|
|
1132
|
+
console.log(` Database: ${dbPath} ${dbOk ? '\u2713' : '\u2717 not accessible'}`);
|
|
1133
|
+
|
|
1134
|
+
// Config file check
|
|
1135
|
+
let configOk = false;
|
|
1136
|
+
let configMsg = '';
|
|
1137
|
+
if (existsSync(CONFIG_PATH)) {
|
|
1138
|
+
try {
|
|
1139
|
+
const content = readFileSync(CONFIG_PATH, 'utf8');
|
|
1140
|
+
JSON.parse(content);
|
|
1141
|
+
configOk = true;
|
|
1142
|
+
configMsg = `${CONFIG_PATH} \u2713`;
|
|
1143
|
+
} catch (err) {
|
|
1144
|
+
configMsg = `${CONFIG_PATH} \u2717 invalid JSON`;
|
|
1145
|
+
}
|
|
1146
|
+
} else {
|
|
1147
|
+
configMsg = `${CONFIG_PATH} (not found)`;
|
|
1148
|
+
}
|
|
1149
|
+
console.log(` Config: ${configMsg}`);
|
|
1150
|
+
|
|
1151
|
+
// API Keys
|
|
1152
|
+
console.log(`\n API Keys:`);
|
|
1153
|
+
const apiKeys = [
|
|
1154
|
+
{ name: 'XAI_API_KEY', env: process.env.XAI_API_KEY },
|
|
1155
|
+
{ name: 'ANTHROPIC_API_KEY', env: process.env.ANTHROPIC_API_KEY },
|
|
1156
|
+
{ name: 'OPENAI_API_KEY', env: process.env.OPENAI_API_KEY },
|
|
1157
|
+
];
|
|
1158
|
+
|
|
1159
|
+
for (const key of apiKeys) {
|
|
1160
|
+
const isSet = Boolean(key.env);
|
|
1161
|
+
console.log(` ${key.name}: ${isSet ? '\u2713 set' : '\u2717 not set'}`);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
console.log();
|
|
1165
|
+
}
|
|
1166
|
+
|
|
696
1167
|
/**
|
|
697
1168
|
* Main entry point.
|
|
698
1169
|
*/
|
|
@@ -711,12 +1182,28 @@ async function main() {
|
|
|
711
1182
|
|
|
712
1183
|
const command = args.positionals[0];
|
|
713
1184
|
|
|
1185
|
+
// Handle piped input from stdin
|
|
1186
|
+
if (!process.stdin.isTTY && !command) {
|
|
1187
|
+
let input = '';
|
|
1188
|
+
for await (const chunk of process.stdin) {
|
|
1189
|
+
input += chunk;
|
|
1190
|
+
}
|
|
1191
|
+
const message = input.trim();
|
|
1192
|
+
if (message) {
|
|
1193
|
+
await runChat(message, args);
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
|
|
714
1198
|
switch (command) {
|
|
1199
|
+
case 'doctor':
|
|
1200
|
+
await runDoctor(args);
|
|
1201
|
+
break;
|
|
715
1202
|
case 'serve':
|
|
716
1203
|
await runServer(args);
|
|
717
1204
|
break;
|
|
718
1205
|
case 'tools':
|
|
719
|
-
await runTools();
|
|
1206
|
+
await runTools(args);
|
|
720
1207
|
break;
|
|
721
1208
|
case 'stats':
|
|
722
1209
|
await runStats(args);
|
|
@@ -725,13 +1212,13 @@ async function main() {
|
|
|
725
1212
|
await runMemory(args, args.positionals[1], args.positionals.slice(2).join(' '));
|
|
726
1213
|
break;
|
|
727
1214
|
case 'jobs':
|
|
728
|
-
await runJobs(args);
|
|
1215
|
+
await runJobs(args, args.positionals[1], args.positionals[2]);
|
|
729
1216
|
break;
|
|
730
1217
|
case 'tasks':
|
|
731
|
-
await runTasks(args);
|
|
1218
|
+
await runTasks(args, args.positionals[1], args.positionals[2]);
|
|
732
1219
|
break;
|
|
733
1220
|
case 'sessions':
|
|
734
|
-
await runSessions(args);
|
|
1221
|
+
await runSessions(args, args.positionals[1], args.positionals[2]);
|
|
735
1222
|
break;
|
|
736
1223
|
case 'events':
|
|
737
1224
|
await runEvents(args);
|
package/dotbot.db
CHANGED
|
Binary file
|
package/package.json
CHANGED