kernelbot 1.0.38 → 1.0.39
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/bin/kernel.js +331 -447
- package/knowledge_base/active_inference_foraging.md +126 -0
- package/knowledge_base/index.md +1 -1
- package/package.json +2 -1
- package/src/dashboard/shared.js +10 -1
- package/src/life/engine.js +87 -6
- package/src/utils/config.js +86 -82
- package/src/utils/display.js +118 -66
package/src/utils/display.js
CHANGED
|
@@ -1,19 +1,34 @@
|
|
|
1
|
-
import { readFileSync } from
|
|
2
|
-
import { join, dirname } from
|
|
3
|
-
import { fileURLToPath } from
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { networkInterfaces } from "os";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import ora from "ora";
|
|
7
|
+
import boxen from "boxen";
|
|
8
|
+
import gradient from "gradient-string";
|
|
9
|
+
import * as p from "@clack/prompts";
|
|
10
|
+
import { PROVIDERS } from "../providers/models.js";
|
|
11
|
+
|
|
12
|
+
export { p };
|
|
13
|
+
|
|
14
|
+
function getLocalIp() {
|
|
15
|
+
const nets = networkInterfaces();
|
|
16
|
+
for (const iface of Object.values(nets)) {
|
|
17
|
+
for (const info of iface) {
|
|
18
|
+
if (info.family === "IPv4" && !info.internal) return info.address;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return "localhost";
|
|
22
|
+
}
|
|
8
23
|
|
|
9
24
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
25
|
|
|
11
26
|
function getVersion() {
|
|
12
27
|
try {
|
|
13
|
-
const pkg = JSON.parse(readFileSync(join(__dirname,
|
|
28
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, "../../package.json"), "utf-8"));
|
|
14
29
|
return pkg.version;
|
|
15
30
|
} catch {
|
|
16
|
-
return
|
|
31
|
+
return "unknown";
|
|
17
32
|
}
|
|
18
33
|
}
|
|
19
34
|
|
|
@@ -38,38 +53,14 @@ const LOGO = `
|
|
|
38
53
|
`;
|
|
39
54
|
|
|
40
55
|
// Green terminal gradient
|
|
41
|
-
const monoGradient = gradient([
|
|
42
|
-
'#00ff41',
|
|
43
|
-
'#00cc33',
|
|
44
|
-
'#009926',
|
|
45
|
-
'#006619',
|
|
46
|
-
]);
|
|
56
|
+
const monoGradient = gradient(["#00ff41", "#00cc33", "#009926", "#006619"]);
|
|
47
57
|
|
|
48
58
|
export function showLogo() {
|
|
49
59
|
console.log(monoGradient.multiline(LOGO));
|
|
50
|
-
console.log(chalk.dim(` AI Engineering Agent — v${getVersion()}\n`));
|
|
51
|
-
console.log(
|
|
52
|
-
boxen(
|
|
53
|
-
chalk.yellow.bold('WARNING') +
|
|
54
|
-
chalk.yellow(
|
|
55
|
-
'\n\nKernelBot has full access to your operating system.\n' +
|
|
56
|
-
'It can execute commands, read/write files, manage processes,\n' +
|
|
57
|
-
'and interact with external services on your behalf.\n\n' +
|
|
58
|
-
'Only run this on machines you control.\n' +
|
|
59
|
-
'Set OWNER_TELEGRAM_ID in .env or allowed_users in config.yaml.',
|
|
60
|
-
),
|
|
61
|
-
{
|
|
62
|
-
padding: 1,
|
|
63
|
-
borderStyle: 'round',
|
|
64
|
-
borderColor: 'yellow',
|
|
65
|
-
},
|
|
66
|
-
),
|
|
67
|
-
);
|
|
68
|
-
console.log('');
|
|
69
60
|
}
|
|
70
61
|
|
|
71
62
|
export async function showStartupCheck(label, checkFn) {
|
|
72
|
-
const spinner = ora({ text: label, color:
|
|
63
|
+
const spinner = ora({ text: label, color: "cyan" }).start();
|
|
73
64
|
try {
|
|
74
65
|
await checkFn();
|
|
75
66
|
spinner.succeed(chalk.green(label));
|
|
@@ -82,11 +73,11 @@ export async function showStartupCheck(label, checkFn) {
|
|
|
82
73
|
|
|
83
74
|
export function showStartupComplete() {
|
|
84
75
|
console.log(
|
|
85
|
-
boxen(chalk.green.bold(
|
|
76
|
+
boxen(chalk.green.bold("KernelBot is live"), {
|
|
86
77
|
padding: 1,
|
|
87
78
|
margin: { top: 1 },
|
|
88
|
-
borderStyle:
|
|
89
|
-
borderColor:
|
|
79
|
+
borderStyle: "round",
|
|
80
|
+
borderColor: "green",
|
|
90
81
|
}),
|
|
91
82
|
);
|
|
92
83
|
}
|
|
@@ -95,8 +86,8 @@ export function showSuccess(msg) {
|
|
|
95
86
|
console.log(
|
|
96
87
|
boxen(chalk.green(msg), {
|
|
97
88
|
padding: 1,
|
|
98
|
-
borderStyle:
|
|
99
|
-
borderColor:
|
|
89
|
+
borderStyle: "round",
|
|
90
|
+
borderColor: "green",
|
|
100
91
|
}),
|
|
101
92
|
);
|
|
102
93
|
}
|
|
@@ -105,14 +96,14 @@ export function showError(msg) {
|
|
|
105
96
|
console.log(
|
|
106
97
|
boxen(chalk.red(msg), {
|
|
107
98
|
padding: 1,
|
|
108
|
-
borderStyle:
|
|
109
|
-
borderColor:
|
|
99
|
+
borderStyle: "round",
|
|
100
|
+
borderColor: "red",
|
|
110
101
|
}),
|
|
111
102
|
);
|
|
112
103
|
}
|
|
113
104
|
|
|
114
105
|
export function createSpinner(text) {
|
|
115
|
-
return ora({ text, color:
|
|
106
|
+
return ora({ text, color: "cyan" });
|
|
116
107
|
}
|
|
117
108
|
|
|
118
109
|
/**
|
|
@@ -121,47 +112,108 @@ export function createSpinner(text) {
|
|
|
121
112
|
* @param {boolean} isActive — whether this is the currently active character
|
|
122
113
|
*/
|
|
123
114
|
export function showCharacterCard(character, isActive = false) {
|
|
124
|
-
const art = character.asciiArt ||
|
|
125
|
-
const activeTag = isActive ? chalk.green(
|
|
115
|
+
const art = character.asciiArt || "";
|
|
116
|
+
const activeTag = isActive ? chalk.green(" (active)") : "";
|
|
126
117
|
const content = [
|
|
127
118
|
`${character.emoji} ${chalk.bold(character.name)}${activeTag}`,
|
|
128
119
|
chalk.dim(`"${character.tagline}"`),
|
|
129
|
-
|
|
130
|
-
...(art ? art.split(
|
|
131
|
-
|
|
132
|
-
chalk.dim(`Origin: ${character.origin ||
|
|
133
|
-
chalk.dim(`Style: ${character.age ||
|
|
134
|
-
].join(
|
|
120
|
+
"",
|
|
121
|
+
...(art ? art.split("\n").map((line) => chalk.cyan(line)) : []),
|
|
122
|
+
"",
|
|
123
|
+
chalk.dim(`Origin: ${character.origin || "Unknown"}`),
|
|
124
|
+
chalk.dim(`Style: ${character.age || "Unknown"}`),
|
|
125
|
+
].join("\n");
|
|
135
126
|
|
|
136
127
|
console.log(
|
|
137
128
|
boxen(content, {
|
|
138
129
|
padding: 1,
|
|
139
|
-
borderStyle:
|
|
140
|
-
borderColor: isActive ?
|
|
130
|
+
borderStyle: "round",
|
|
131
|
+
borderColor: isActive ? "green" : "cyan",
|
|
141
132
|
}),
|
|
142
133
|
);
|
|
143
134
|
}
|
|
144
135
|
|
|
145
136
|
/**
|
|
146
|
-
*
|
|
147
|
-
* @param {object
|
|
148
|
-
* @param {
|
|
137
|
+
* Format "Provider / model" label for a config section.
|
|
138
|
+
* @param {object} config — full config
|
|
139
|
+
* @param {'brain'|'orchestrator'} section
|
|
140
|
+
*/
|
|
141
|
+
export function formatProviderLabel(config, section) {
|
|
142
|
+
const sec = config[section];
|
|
143
|
+
const providerDef = PROVIDERS[sec.provider];
|
|
144
|
+
const name = providerDef ? providerDef.name : sec.provider;
|
|
145
|
+
return `${name} / ${sec.model}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Centralized cancel handler for @clack/prompts.
|
|
150
|
+
* Call after every prompt — exits gracefully on Ctrl+C.
|
|
151
|
+
*/
|
|
152
|
+
export function handleCancel(value) {
|
|
153
|
+
if (p.isCancel(value)) {
|
|
154
|
+
p.cancel("Cancelled.");
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Claude Code-inspired info box shown after the logo.
|
|
149
162
|
*/
|
|
163
|
+
export function showWelcomeScreen(config, characterManager) {
|
|
164
|
+
const version = getVersion();
|
|
165
|
+
|
|
166
|
+
const orchLabel = formatProviderLabel(config, "orchestrator");
|
|
167
|
+
const brainLabel = formatProviderLabel(config, "brain");
|
|
168
|
+
|
|
169
|
+
let charLabel = "None";
|
|
170
|
+
if (characterManager) {
|
|
171
|
+
const activeId = characterManager.getActiveCharacterId();
|
|
172
|
+
const active = characterManager.getCharacter(activeId);
|
|
173
|
+
if (active) charLabel = `${active.emoji} ${active.name}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const lifeEnabled = config.life?.enabled !== false;
|
|
177
|
+
const dashPort = config.dashboard?.port || 3000;
|
|
178
|
+
const dashEnabled = config.dashboard?.enabled;
|
|
179
|
+
|
|
180
|
+
const pad = (label, width = 18) => label.padEnd(width);
|
|
181
|
+
|
|
182
|
+
const lines = [
|
|
183
|
+
"",
|
|
184
|
+
` ${chalk.dim(pad("Orchestrator"))}${orchLabel}`,
|
|
185
|
+
` ${chalk.dim(pad("Brain"))}${brainLabel}`,
|
|
186
|
+
` ${chalk.dim(pad("Character"))}${charLabel}`,
|
|
187
|
+
` ${chalk.dim(pad("Life Engine"))}${lifeEnabled ? chalk.green("enabled") : chalk.yellow("disabled")}`,
|
|
188
|
+
` ${chalk.dim(pad("Dashboard"))}${dashEnabled ? chalk.green(`http://${getLocalIp()}:${dashPort}/`) : chalk.yellow("off")}`,
|
|
189
|
+
"",
|
|
190
|
+
chalk.dim(" ↑↓ Navigate · Enter Select · Ctrl+C Cancel"),
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
console.log(
|
|
194
|
+
boxen(lines.join("\n"), {
|
|
195
|
+
title: `KERNEL Bot v${version}`,
|
|
196
|
+
titleAlignment: "left",
|
|
197
|
+
padding: { top: 0, bottom: 0, left: 0, right: 2 },
|
|
198
|
+
borderStyle: "round",
|
|
199
|
+
borderColor: "green",
|
|
200
|
+
}),
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
150
204
|
export function showCharacterGallery(characters, activeId = null) {
|
|
151
|
-
console.log(
|
|
205
|
+
console.log("");
|
|
152
206
|
console.log(
|
|
153
|
-
gradient([
|
|
154
|
-
|
|
155
|
-
' CHOOSE YOUR CHARACTER\n' +
|
|
156
|
-
' ═══════════════════════════════',
|
|
207
|
+
gradient(["#ff6b6b", "#feca57", "#48dbfb", "#ff9ff3"]).multiline(
|
|
208
|
+
" ═══════════════════════════════\n" + " CHOOSE YOUR CHARACTER\n" + " ═══════════════════════════════",
|
|
157
209
|
),
|
|
158
210
|
);
|
|
159
|
-
console.log(
|
|
160
|
-
console.log(chalk.dim(
|
|
161
|
-
console.log(chalk.dim(
|
|
162
|
-
console.log(
|
|
211
|
+
console.log("");
|
|
212
|
+
console.log(chalk.dim(" Each character has their own personality,"));
|
|
213
|
+
console.log(chalk.dim(" memories, and story that evolves with you."));
|
|
214
|
+
console.log("");
|
|
163
215
|
|
|
164
216
|
for (const c of characters) {
|
|
165
217
|
showCharacterCard(c, c.id === activeId);
|
|
166
218
|
}
|
|
167
|
-
}
|
|
219
|
+
}
|