kernelbot 1.0.35 → 1.0.37

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 CHANGED
@@ -179,26 +179,20 @@ async function runCheck(config) {
179
179
  const orchProviderKey = config.orchestrator.provider || 'anthropic';
180
180
  const orchProviderDef = PROVIDERS[orchProviderKey];
181
181
  const orchLabel = orchProviderDef ? orchProviderDef.name : orchProviderKey;
182
- const orchEnvKey = orchProviderDef ? orchProviderDef.envKey : 'ANTHROPIC_API_KEY';
182
+ const orchEnvKey = orchProviderDef ? orchProviderDef.envKey : 'API_KEY';
183
183
 
184
184
  await showStartupCheck(`Orchestrator ${orchEnvKey}`, async () => {
185
- const orchestratorKey = config.orchestrator.api_key
186
- || (orchProviderDef && process.env[orchProviderDef.envKey])
187
- || process.env.ANTHROPIC_API_KEY;
188
- if (!orchestratorKey) throw new Error('Not set');
185
+ if (!config.orchestrator.api_key) throw new Error('Not set');
189
186
  });
190
187
 
191
188
  await showStartupCheck(`Orchestrator (${orchLabel}) API connection`, async () => {
192
- const orchestratorKey = config.orchestrator.api_key
193
- || (orchProviderDef && process.env[orchProviderDef.envKey])
194
- || process.env.ANTHROPIC_API_KEY;
195
189
  const provider = createProvider({
196
190
  brain: {
197
191
  provider: orchProviderKey,
198
192
  model: config.orchestrator.model,
199
193
  max_tokens: config.orchestrator.max_tokens,
200
194
  temperature: config.orchestrator.temperature,
201
- api_key: orchestratorKey,
195
+ api_key: config.orchestrator.api_key,
202
196
  },
203
197
  });
204
198
  await provider.ping();
@@ -246,12 +240,11 @@ async function startBotFlow(config) {
246
240
  const orchProviderKey = config.orchestrator.provider || 'anthropic';
247
241
  const orchProviderDef = PROVIDERS[orchProviderKey];
248
242
  const orchLabel = orchProviderDef ? orchProviderDef.name : orchProviderKey;
243
+ const orchEnvKey = orchProviderDef?.envKey || 'API_KEY';
249
244
  checks.push(
250
245
  await showStartupCheck(`Orchestrator (${orchLabel}) API`, async () => {
251
- const orchestratorKey = config.orchestrator.api_key
252
- || (orchProviderDef && process.env[orchProviderDef.envKey])
253
- || process.env.ANTHROPIC_API_KEY;
254
- if (!orchestratorKey) throw new Error(`${orchProviderDef?.envKey || 'ANTHROPIC_API_KEY'} is required for the orchestrator`);
246
+ const orchestratorKey = config.orchestrator.api_key;
247
+ if (!orchestratorKey) throw new Error(`${orchEnvKey} is required for the orchestrator (${orchLabel})`);
255
248
  const provider = createProvider({
256
249
  brain: {
257
250
  provider: orchProviderKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kernelbot",
3
- "version": "1.0.35",
3
+ "version": "1.0.37",
4
4
  "description": "KernelBot — AI engineering agent with full OS control",
5
5
  "type": "module",
6
6
  "author": "Abdullah Al-Taheri <abdullah@altaheri.me>",
package/src/agent.js CHANGED
@@ -27,7 +27,7 @@ export class OrchestratorAgent {
27
27
  // Orchestrator provider (30s timeout — lean dispatch/summarize calls)
28
28
  const orchProviderKey = config.orchestrator.provider || 'anthropic';
29
29
  const orchProviderDef = PROVIDERS[orchProviderKey];
30
- const orchApiKey = config.orchestrator.api_key || (orchProviderDef && process.env[orchProviderDef.envKey]) || process.env.ANTHROPIC_API_KEY;
30
+ const orchApiKey = config.orchestrator.api_key || (orchProviderDef && process.env[orchProviderDef.envKey]);
31
31
  this.orchestratorProvider = createProvider({
32
32
  brain: {
33
33
  provider: orchProviderKey,
@@ -1,9 +1,50 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { homedir } from 'os';
4
+ import { getLogger } from '../utils/logger.js';
5
+
1
6
  export function isAllowedUser(userId, config) {
2
7
  const allowed = config.telegram.allowed_users;
3
- if (!allowed || allowed.length === 0) return false;
8
+
9
+ // Auto-register the first user as owner when no allowed users exist
10
+ if (!allowed || allowed.length === 0) {
11
+ config.telegram.allowed_users = [userId];
12
+ _persistOwner(userId);
13
+ const logger = getLogger();
14
+ logger.info(`[Auth] Auto-registered first user ${userId} as owner`);
15
+ return true;
16
+ }
17
+
4
18
  return allowed.includes(userId);
5
19
  }
6
20
 
21
+ /**
22
+ * Persist the auto-registered owner ID to ~/.kernelbot/.env
23
+ */
24
+ function _persistOwner(userId) {
25
+ try {
26
+ const configDir = join(homedir(), '.kernelbot');
27
+ mkdirSync(configDir, { recursive: true });
28
+ const envPath = join(configDir, '.env');
29
+
30
+ let content = '';
31
+ if (existsSync(envPath)) {
32
+ content = readFileSync(envPath, 'utf-8').trimEnd() + '\n';
33
+ }
34
+
35
+ const regex = /^OWNER_TELEGRAM_ID=.*$/m;
36
+ const line = `OWNER_TELEGRAM_ID=${userId}`;
37
+ if (regex.test(content)) {
38
+ content = content.replace(regex, line);
39
+ } else {
40
+ content += line + '\n';
41
+ }
42
+ writeFileSync(envPath, content);
43
+ } catch {
44
+ // Non-fatal — owner is still in memory for this session
45
+ }
46
+ }
47
+
7
48
  export function getUnauthorizedMessage() {
8
49
  return 'Access denied. You are not authorized to use this bot.';
9
50
  }
@@ -401,7 +401,8 @@ async function promptForMissing(config) {
401
401
  }
402
402
 
403
403
  if (!mutableConfig.brain.api_key) {
404
- // Run provider selection flow
404
+ // Run brain provider selection flow
405
+ console.log(chalk.bold('\n 🧠 Worker Brain'));
405
406
  const { providerKey, modelId } = await promptProviderSelection(rl);
406
407
  mutableConfig.brain.provider = providerKey;
407
408
  mutableConfig.brain.model = modelId;
@@ -413,6 +414,38 @@ async function promptForMissing(config) {
413
414
  const key = await ask(rl, chalk.cyan(`\n ${providerDef.name} API key: `));
414
415
  mutableConfig.brain.api_key = key.trim();
415
416
  envLines.push(`${envKey}=${key.trim()}`);
417
+
418
+ // Orchestrator provider selection
419
+ console.log(chalk.bold('\n 🎛️ Orchestrator'));
420
+ const sameChoice = await ask(rl, chalk.cyan(` Use same provider (${providerDef.name} / ${modelId}) for orchestrator? [Y/n]: `));
421
+ if (!sameChoice.trim() || sameChoice.trim().toLowerCase() === 'y') {
422
+ mutableConfig.orchestrator.provider = providerKey;
423
+ mutableConfig.orchestrator.model = modelId;
424
+ mutableConfig.orchestrator.api_key = key.trim();
425
+ saveOrchestratorToYaml(providerKey, modelId);
426
+ } else {
427
+ const orch = await promptProviderSelection(rl);
428
+ mutableConfig.orchestrator.provider = orch.providerKey;
429
+ mutableConfig.orchestrator.model = orch.modelId;
430
+ saveOrchestratorToYaml(orch.providerKey, orch.modelId);
431
+
432
+ const orchProviderDef = PROVIDERS[orch.providerKey];
433
+ if (orch.providerKey === providerKey) {
434
+ // Same provider — reuse the API key
435
+ mutableConfig.orchestrator.api_key = key.trim();
436
+ } else {
437
+ // Different provider — need a separate key
438
+ const orchEnvKey = orchProviderDef.envKey;
439
+ const orchExisting = process.env[orchEnvKey];
440
+ if (orchExisting) {
441
+ mutableConfig.orchestrator.api_key = orchExisting;
442
+ } else {
443
+ const orchKey = await ask(rl, chalk.cyan(`\n ${orchProviderDef.name} API key: `));
444
+ mutableConfig.orchestrator.api_key = orchKey.trim();
445
+ envLines.push(`${orchEnvKey}=${orchKey.trim()}`);
446
+ }
447
+ }
448
+ }
416
449
  }
417
450
 
418
451
  if (!mutableConfig.telegram.bot_token) {
@@ -468,24 +501,20 @@ export function loadConfig() {
468
501
 
469
502
  const config = deepMerge(DEFAULTS, fileConfig);
470
503
 
504
+ // Brain — resolve API key from env based on configured provider
505
+ const providerDef = PROVIDERS[config.brain.provider];
506
+ if (providerDef && process.env[providerDef.envKey]) {
507
+ config.brain.api_key = process.env[providerDef.envKey];
508
+ }
509
+
471
510
  // Orchestrator — resolve API key based on configured provider
472
511
  const orchProvider = PROVIDERS[config.orchestrator.provider];
473
512
  if (orchProvider && process.env[orchProvider.envKey]) {
474
513
  config.orchestrator.api_key = process.env[orchProvider.envKey];
475
514
  }
476
- // Legacy fallback: ANTHROPIC_API_KEY for anthropic orchestrator
477
- if (config.orchestrator.provider === 'anthropic' && !config.orchestrator.api_key && process.env.ANTHROPIC_API_KEY) {
478
- config.orchestrator.api_key = process.env.ANTHROPIC_API_KEY;
479
- }
480
-
481
- // Overlay env vars for brain API key based on provider
482
- const providerDef = PROVIDERS[config.brain.provider];
483
- if (providerDef && process.env[providerDef.envKey]) {
484
- config.brain.api_key = process.env[providerDef.envKey];
485
- }
486
- // Legacy fallback: ANTHROPIC_API_KEY for anthropic provider
487
- if (config.brain.provider === 'anthropic' && !config.brain.api_key && process.env.ANTHROPIC_API_KEY) {
488
- config.brain.api_key = process.env.ANTHROPIC_API_KEY;
515
+ // If orchestrator uses the same provider as brain, share the key
516
+ if (!config.orchestrator.api_key && config.orchestrator.provider === config.brain.provider && config.brain.api_key) {
517
+ config.orchestrator.api_key = config.brain.api_key;
489
518
  }
490
519
 
491
520
  if (process.env.TELEGRAM_BOT_TOKEN) {