kernelbot 1.0.2 → 1.0.3
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 +20 -27
- package/bin/kernel.js +138 -154
- package/package.json +1 -1
- package/src/utils/config.js +89 -4
- package/src/utils/display.js +0 -23
package/README.md
CHANGED
|
@@ -6,10 +6,10 @@ Send a message in Telegram, and KernelBot will read files, write code, run comma
|
|
|
6
6
|
|
|
7
7
|
## How It Works
|
|
8
8
|
|
|
9
|
-
```
|
|
9
|
+
```text
|
|
10
10
|
You (Telegram) → KernelBot → Claude Sonnet (Anthropic API)
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
↕
|
|
12
|
+
OS Tools (shell, files, directories)
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
KernelBot runs a **tool-use loop**: Claude decides which tools to call, KernelBot executes them on your OS, feeds results back, and Claude continues until the task is done. One message can trigger dozens of tool calls autonomously.
|
|
@@ -29,42 +29,35 @@ KernelBot runs a **tool-use loop**: Claude decides which tools to call, KernelBo
|
|
|
29
29
|
npm install -g kernelbot
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
This installs the `kernelbot` command globally.
|
|
33
|
-
|
|
34
32
|
## Quick Start
|
|
35
33
|
|
|
36
34
|
```bash
|
|
37
|
-
|
|
38
|
-
kernelbot init
|
|
39
|
-
|
|
40
|
-
# Verify everything works
|
|
41
|
-
kernelbot check
|
|
42
|
-
|
|
43
|
-
# Launch the bot
|
|
44
|
-
kernelbot start
|
|
35
|
+
kernelbot
|
|
45
36
|
```
|
|
46
37
|
|
|
47
|
-
|
|
38
|
+
That's it. On first run, KernelBot will:
|
|
48
39
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
| `kernelbot check` | Validate config and test API connections |
|
|
54
|
-
| `kernelbot init` | Interactive setup wizard |
|
|
40
|
+
1. Detect missing credentials and prompt for them
|
|
41
|
+
2. Save them to `~/.kernelbot/.env`
|
|
42
|
+
3. Verify API connections
|
|
43
|
+
4. Launch the Telegram bot
|
|
55
44
|
|
|
56
45
|
## Configuration
|
|
57
46
|
|
|
58
|
-
KernelBot
|
|
47
|
+
KernelBot auto-detects config from the current directory or `~/.kernelbot/`. Everything works with zero config — just provide your API keys when prompted.
|
|
59
48
|
|
|
60
|
-
###
|
|
49
|
+
### Environment Variables
|
|
61
50
|
|
|
62
|
-
|
|
51
|
+
Set these in `.env` or as system environment variables:
|
|
52
|
+
|
|
53
|
+
```text
|
|
63
54
|
ANTHROPIC_API_KEY=sk-ant-...
|
|
64
55
|
TELEGRAM_BOT_TOKEN=123456:ABC-DEF...
|
|
65
56
|
```
|
|
66
57
|
|
|
67
|
-
### `config.yaml`
|
|
58
|
+
### `config.yaml` (optional)
|
|
59
|
+
|
|
60
|
+
Drop a `config.yaml` in your working directory or `~/.kernelbot/` to customize behavior:
|
|
68
61
|
|
|
69
62
|
```yaml
|
|
70
63
|
bot:
|
|
@@ -102,10 +95,10 @@ conversation:
|
|
|
102
95
|
|
|
103
96
|
## Project Structure
|
|
104
97
|
|
|
105
|
-
```
|
|
98
|
+
```text
|
|
106
99
|
KernelBot/
|
|
107
100
|
├── bin/
|
|
108
|
-
│ └── kernel.js #
|
|
101
|
+
│ └── kernel.js # Entry point
|
|
109
102
|
├── src/
|
|
110
103
|
│ ├── agent.js # Sonnet tool-use loop
|
|
111
104
|
│ ├── bot.js # Telegram bot (polling, auth, message handling)
|
|
@@ -119,7 +112,7 @@ KernelBot/
|
|
|
119
112
|
│ │ ├── os.js # OS tool definitions + handlers
|
|
120
113
|
│ │ └── index.js # Tool registry + dispatcher
|
|
121
114
|
│ └── utils/
|
|
122
|
-
│ ├── config.js # Config loading (
|
|
115
|
+
│ ├── config.js # Config loading (auto-detect + prompt)
|
|
123
116
|
│ ├── display.js # CLI display (logo, spinners, banners)
|
|
124
117
|
│ └── logger.js # Winston logger
|
|
125
118
|
├── config.example.yaml
|
package/bin/kernel.js
CHANGED
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
process.removeAllListeners('warning');
|
|
5
5
|
process.on('warning', (w) => { if (w.name !== 'DeprecationWarning' || !w.message.includes('punycode')) console.warn(w); });
|
|
6
6
|
|
|
7
|
-
import { Command } from 'commander';
|
|
8
7
|
import { createInterface } from 'readline';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
8
|
+
import { readFileSync, existsSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { homedir } from 'os';
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import { loadConfig, loadConfigInteractive } from '../src/utils/config.js';
|
|
11
13
|
import { createLogger, getLogger } from '../src/utils/logger.js';
|
|
12
14
|
import {
|
|
13
15
|
showLogo,
|
|
14
|
-
showHelp,
|
|
15
16
|
showStartupCheck,
|
|
16
17
|
showStartupComplete,
|
|
17
18
|
showError,
|
|
@@ -22,180 +23,163 @@ import { Agent } from '../src/agent.js';
|
|
|
22
23
|
import { startBot } from '../src/bot.js';
|
|
23
24
|
import Anthropic from '@anthropic-ai/sdk';
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.
|
|
29
|
-
.
|
|
30
|
-
.
|
|
31
|
-
.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
if (checks.some((c) => !c)) {
|
|
69
|
-
showError('Startup checks failed. Fix the issues above and try again.');
|
|
70
|
-
process.exit(1);
|
|
26
|
+
function showMenu() {
|
|
27
|
+
console.log('');
|
|
28
|
+
console.log(chalk.bold(' What would you like to do?\n'));
|
|
29
|
+
console.log(` ${chalk.cyan('1.')} Start bot`);
|
|
30
|
+
console.log(` ${chalk.cyan('2.')} Check connections`);
|
|
31
|
+
console.log(` ${chalk.cyan('3.')} View logs`);
|
|
32
|
+
console.log(` ${chalk.cyan('4.')} View audit logs`);
|
|
33
|
+
console.log(` ${chalk.cyan('5.')} Exit`);
|
|
34
|
+
console.log('');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function ask(rl, question) {
|
|
38
|
+
return new Promise((res) => rl.question(question, res));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function viewLog(filename) {
|
|
42
|
+
const paths = [
|
|
43
|
+
join(process.cwd(), filename),
|
|
44
|
+
join(homedir(), '.kernelbot', filename),
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
for (const p of paths) {
|
|
48
|
+
if (existsSync(p)) {
|
|
49
|
+
const content = readFileSync(p, 'utf-8');
|
|
50
|
+
const lines = content.split('\n').filter(Boolean);
|
|
51
|
+
const recent = lines.slice(-30);
|
|
52
|
+
console.log(chalk.dim(`\n Showing last ${recent.length} entries from ${p}\n`));
|
|
53
|
+
for (const line of recent) {
|
|
54
|
+
try {
|
|
55
|
+
const entry = JSON.parse(line);
|
|
56
|
+
const time = entry.timestamp || '';
|
|
57
|
+
const level = entry.level || '';
|
|
58
|
+
const msg = entry.message || '';
|
|
59
|
+
const color = level === 'error' ? chalk.red : level === 'warn' ? chalk.yellow : chalk.dim;
|
|
60
|
+
console.log(` ${chalk.dim(time)} ${color(level)} ${msg}`);
|
|
61
|
+
} catch {
|
|
62
|
+
console.log(` ${line}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
console.log('');
|
|
66
|
+
return;
|
|
71
67
|
}
|
|
68
|
+
}
|
|
69
|
+
console.log(chalk.dim(`\n No ${filename} found yet.\n`));
|
|
70
|
+
}
|
|
72
71
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
startBot(config, agent);
|
|
77
|
-
showStartupComplete();
|
|
72
|
+
async function runCheck(config) {
|
|
73
|
+
await showStartupCheck('ANTHROPIC_API_KEY', async () => {
|
|
74
|
+
if (!config.anthropic.api_key) throw new Error('Not set');
|
|
78
75
|
});
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
.description('Run a one-off prompt through the agent (no Telegram)')
|
|
84
|
-
.argument('<prompt>', 'The prompt to send')
|
|
85
|
-
.action(async (prompt) => {
|
|
86
|
-
const config = loadConfig();
|
|
87
|
-
createLogger(config);
|
|
88
|
-
createAuditLogger();
|
|
89
|
-
|
|
90
|
-
if (!config.anthropic.api_key) {
|
|
91
|
-
showError('ANTHROPIC_API_KEY not set. Run `kernelbot init` first.');
|
|
92
|
-
process.exit(1);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const conversationManager = new ConversationManager(config);
|
|
96
|
-
const agent = new Agent({ config, conversationManager });
|
|
77
|
+
await showStartupCheck('TELEGRAM_BOT_TOKEN', async () => {
|
|
78
|
+
if (!config.telegram.bot_token) throw new Error('Not set');
|
|
79
|
+
});
|
|
97
80
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
81
|
+
await showStartupCheck('Anthropic API connection', async () => {
|
|
82
|
+
const client = new Anthropic({ apiKey: config.anthropic.api_key });
|
|
83
|
+
await client.messages.create({
|
|
84
|
+
model: config.anthropic.model,
|
|
85
|
+
max_tokens: 16,
|
|
86
|
+
messages: [{ role: 'user', content: 'ping' }],
|
|
101
87
|
});
|
|
102
|
-
|
|
103
|
-
console.log('\n' + reply);
|
|
104
88
|
});
|
|
105
89
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const config = loadConfig();
|
|
114
|
-
createLogger(config);
|
|
90
|
+
await showStartupCheck('Telegram Bot API', async () => {
|
|
91
|
+
const res = await fetch(
|
|
92
|
+
`https://api.telegram.org/bot${config.telegram.bot_token}/getMe`,
|
|
93
|
+
);
|
|
94
|
+
const data = await res.json();
|
|
95
|
+
if (!data.ok) throw new Error(data.description || 'Invalid token');
|
|
96
|
+
});
|
|
115
97
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
});
|
|
98
|
+
console.log(chalk.green('\n All checks passed.\n'));
|
|
99
|
+
}
|
|
119
100
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
101
|
+
async function startBotFlow(config) {
|
|
102
|
+
createAuditLogger();
|
|
103
|
+
const logger = getLogger();
|
|
123
104
|
|
|
124
|
-
|
|
125
|
-
if (!config.telegram.bot_token) throw new Error('Not set');
|
|
126
|
-
});
|
|
105
|
+
const checks = [];
|
|
127
106
|
|
|
128
|
-
|
|
107
|
+
checks.push(
|
|
108
|
+
await showStartupCheck('Anthropic API', async () => {
|
|
129
109
|
const client = new Anthropic({ apiKey: config.anthropic.api_key });
|
|
130
110
|
await client.messages.create({
|
|
131
111
|
model: config.anthropic.model,
|
|
132
112
|
max_tokens: 16,
|
|
133
113
|
messages: [{ role: 'user', content: 'ping' }],
|
|
134
114
|
});
|
|
135
|
-
})
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
136
117
|
|
|
118
|
+
checks.push(
|
|
137
119
|
await showStartupCheck('Telegram Bot API', async () => {
|
|
138
120
|
const res = await fetch(
|
|
139
121
|
`https://api.telegram.org/bot${config.telegram.bot_token}/getMe`,
|
|
140
122
|
);
|
|
141
123
|
const data = await res.json();
|
|
142
124
|
if (!data.ok) throw new Error(data.description || 'Invalid token');
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
writeFileSync('config.yaml', configContent);
|
|
125
|
+
}),
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
if (checks.some((c) => !c)) {
|
|
129
|
+
showError('Startup failed. Fix the issues above and try again.');
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const conversationManager = new ConversationManager(config);
|
|
134
|
+
const agent = new Agent({ config, conversationManager });
|
|
135
|
+
|
|
136
|
+
startBot(config, agent);
|
|
137
|
+
showStartupComplete();
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function main() {
|
|
142
|
+
showLogo();
|
|
143
|
+
|
|
144
|
+
const config = await loadConfigInteractive();
|
|
145
|
+
createLogger(config);
|
|
146
|
+
|
|
147
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
148
|
+
|
|
149
|
+
let running = true;
|
|
150
|
+
while (running) {
|
|
151
|
+
showMenu();
|
|
152
|
+
const choice = await ask(rl, chalk.cyan(' > '));
|
|
153
|
+
|
|
154
|
+
switch (choice.trim()) {
|
|
155
|
+
case '1': {
|
|
156
|
+
rl.close();
|
|
157
|
+
const started = await startBotFlow(config);
|
|
158
|
+
if (!started) process.exit(1);
|
|
159
|
+
return; // bot is running, don't show menu again
|
|
160
|
+
}
|
|
161
|
+
case '2':
|
|
162
|
+
await runCheck(config);
|
|
163
|
+
break;
|
|
164
|
+
case '3':
|
|
165
|
+
viewLog('kernel.log');
|
|
166
|
+
break;
|
|
167
|
+
case '4':
|
|
168
|
+
viewLog('kernel-audit.log');
|
|
169
|
+
break;
|
|
170
|
+
case '5':
|
|
171
|
+
running = false;
|
|
172
|
+
break;
|
|
173
|
+
default:
|
|
174
|
+
console.log(chalk.dim(' Invalid choice.\n'));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
196
177
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
178
|
+
rl.close();
|
|
179
|
+
console.log(chalk.dim(' Goodbye.\n'));
|
|
180
|
+
}
|
|
200
181
|
|
|
201
|
-
|
|
182
|
+
main().catch((err) => {
|
|
183
|
+
showError(err.message);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
});
|
package/package.json
CHANGED
package/src/utils/config.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { readFileSync, existsSync } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
3
|
import { homedir } from 'os';
|
|
4
|
+
import { createInterface } from 'readline';
|
|
4
5
|
import yaml from 'js-yaml';
|
|
5
6
|
import dotenv from 'dotenv';
|
|
7
|
+
import chalk from 'chalk';
|
|
6
8
|
|
|
7
9
|
const DEFAULTS = {
|
|
8
10
|
bot: {
|
|
@@ -54,18 +56,96 @@ function deepMerge(target, source) {
|
|
|
54
56
|
return result;
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
function getConfigDir() {
|
|
60
|
+
return join(homedir(), '.kernelbot');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getEnvPath() {
|
|
64
|
+
const cwdEnv = join(process.cwd(), '.env');
|
|
65
|
+
if (existsSync(cwdEnv)) return cwdEnv;
|
|
66
|
+
return join(getConfigDir(), '.env');
|
|
67
|
+
}
|
|
68
|
+
|
|
57
69
|
function findConfigFile() {
|
|
58
70
|
const cwdPath = join(process.cwd(), 'config.yaml');
|
|
59
71
|
if (existsSync(cwdPath)) return cwdPath;
|
|
60
72
|
|
|
61
|
-
const homePath = join(
|
|
73
|
+
const homePath = join(getConfigDir(), 'config.yaml');
|
|
62
74
|
if (existsSync(homePath)) return homePath;
|
|
63
75
|
|
|
64
76
|
return null;
|
|
65
77
|
}
|
|
66
78
|
|
|
79
|
+
function ask(rl, question) {
|
|
80
|
+
return new Promise((res) => rl.question(question, res));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function promptForMissing(config) {
|
|
84
|
+
const missing = [];
|
|
85
|
+
if (!config.anthropic.api_key) missing.push('ANTHROPIC_API_KEY');
|
|
86
|
+
if (!config.telegram.bot_token) missing.push('TELEGRAM_BOT_TOKEN');
|
|
87
|
+
|
|
88
|
+
if (missing.length === 0) return config;
|
|
89
|
+
|
|
90
|
+
console.log(chalk.yellow('\n Missing credentials detected. Let\'s set them up.\n'));
|
|
91
|
+
|
|
92
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
93
|
+
const mutableConfig = JSON.parse(JSON.stringify(config));
|
|
94
|
+
const envLines = [];
|
|
95
|
+
|
|
96
|
+
// Read existing .env if any
|
|
97
|
+
const envPath = getEnvPath();
|
|
98
|
+
let existingEnv = '';
|
|
99
|
+
if (existsSync(envPath)) {
|
|
100
|
+
existingEnv = readFileSync(envPath, 'utf-8');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!mutableConfig.anthropic.api_key) {
|
|
104
|
+
const key = await ask(rl, chalk.cyan(' Anthropic API key: '));
|
|
105
|
+
mutableConfig.anthropic.api_key = key.trim();
|
|
106
|
+
envLines.push(`ANTHROPIC_API_KEY=${key.trim()}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!mutableConfig.telegram.bot_token) {
|
|
110
|
+
const token = await ask(rl, chalk.cyan(' Telegram Bot Token: '));
|
|
111
|
+
mutableConfig.telegram.bot_token = token.trim();
|
|
112
|
+
envLines.push(`TELEGRAM_BOT_TOKEN=${token.trim()}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
rl.close();
|
|
116
|
+
|
|
117
|
+
// Save to ~/.kernelbot/.env so it persists globally
|
|
118
|
+
if (envLines.length > 0) {
|
|
119
|
+
const configDir = getConfigDir();
|
|
120
|
+
mkdirSync(configDir, { recursive: true });
|
|
121
|
+
const savePath = join(configDir, '.env');
|
|
122
|
+
|
|
123
|
+
// Merge with existing content
|
|
124
|
+
let content = existingEnv ? existingEnv.trimEnd() + '\n' : '';
|
|
125
|
+
for (const line of envLines) {
|
|
126
|
+
const key = line.split('=')[0];
|
|
127
|
+
// Replace if exists, append if not
|
|
128
|
+
const regex = new RegExp(`^${key}=.*$`, 'm');
|
|
129
|
+
if (regex.test(content)) {
|
|
130
|
+
content = content.replace(regex, line);
|
|
131
|
+
} else {
|
|
132
|
+
content += line + '\n';
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
writeFileSync(savePath, content);
|
|
136
|
+
console.log(chalk.dim(`\n Saved to ${savePath}\n`));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return mutableConfig;
|
|
140
|
+
}
|
|
141
|
+
|
|
67
142
|
export function loadConfig() {
|
|
143
|
+
// Load .env from CWD first, then from ~/.kernelbot/
|
|
68
144
|
dotenv.config();
|
|
145
|
+
const globalEnv = join(getConfigDir(), '.env');
|
|
146
|
+
if (existsSync(globalEnv)) {
|
|
147
|
+
dotenv.config({ path: globalEnv });
|
|
148
|
+
}
|
|
69
149
|
|
|
70
150
|
let fileConfig = {};
|
|
71
151
|
const configPath = findConfigFile();
|
|
@@ -84,5 +164,10 @@ export function loadConfig() {
|
|
|
84
164
|
config.telegram.bot_token = process.env.TELEGRAM_BOT_TOKEN;
|
|
85
165
|
}
|
|
86
166
|
|
|
87
|
-
return
|
|
167
|
+
return config;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function loadConfigInteractive() {
|
|
171
|
+
const config = loadConfig();
|
|
172
|
+
return await promptForMissing(config);
|
|
88
173
|
}
|
package/src/utils/display.js
CHANGED
|
@@ -59,29 +59,6 @@ export function showError(msg) {
|
|
|
59
59
|
);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
export function showHelp() {
|
|
63
|
-
showLogo();
|
|
64
|
-
console.log(
|
|
65
|
-
boxen(
|
|
66
|
-
[
|
|
67
|
-
chalk.bold('Commands:'),
|
|
68
|
-
'',
|
|
69
|
-
` ${chalk.cyan('kernelbot start')} ${chalk.dim('Launch the Telegram bot')}`,
|
|
70
|
-
` ${chalk.cyan('kernelbot run')} ${chalk.yellow('"prompt"')} ${chalk.dim('One-off agent call (no Telegram)')}`,
|
|
71
|
-
` ${chalk.cyan('kernelbot check')} ${chalk.dim('Validate config & test APIs')}`,
|
|
72
|
-
` ${chalk.cyan('kernelbot init')} ${chalk.dim('Interactive setup wizard')}`,
|
|
73
|
-
'',
|
|
74
|
-
chalk.dim(` kernelbot --help Show all options`),
|
|
75
|
-
].join('\n'),
|
|
76
|
-
{
|
|
77
|
-
padding: 1,
|
|
78
|
-
borderStyle: 'round',
|
|
79
|
-
borderColor: 'cyan',
|
|
80
|
-
},
|
|
81
|
-
),
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
62
|
export function createSpinner(text) {
|
|
86
63
|
return ora({ text, color: 'cyan' });
|
|
87
64
|
}
|