@kinqs/brainrouter-cli 0.3.5 → 0.3.7

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.
Files changed (125) hide show
  1. package/README.md +29 -52
  2. package/agents/architect.json +18 -0
  3. package/agents/explorer.json +18 -0
  4. package/agents/reviewer.json +18 -0
  5. package/agents/verifier.json +18 -0
  6. package/agents/worker.json +18 -0
  7. package/bin/cli.cjs +71 -0
  8. package/dist/agent/agent.d.ts +224 -3
  9. package/dist/agent/agent.js +561 -55
  10. package/dist/cli/banner.d.ts +80 -0
  11. package/dist/cli/banner.js +232 -0
  12. package/dist/cli/cliPrompt.d.ts +106 -0
  13. package/dist/cli/cliPrompt.js +314 -0
  14. package/dist/cli/commands/_context.d.ts +3 -1
  15. package/dist/cli/commands/_helpers.d.ts +1 -1
  16. package/dist/cli/commands/_helpers.js +6 -6
  17. package/dist/cli/commands/config.d.ts +46 -0
  18. package/dist/cli/commands/config.js +1042 -0
  19. package/dist/cli/commands/guard.js +75 -10
  20. package/dist/cli/commands/init.d.ts +20 -0
  21. package/dist/cli/commands/init.js +64 -0
  22. package/dist/cli/commands/login.d.ts +13 -0
  23. package/dist/cli/commands/login.js +179 -0
  24. package/dist/cli/commands/mcp.d.ts +19 -0
  25. package/dist/cli/commands/mcp.js +286 -0
  26. package/dist/cli/commands/memory.js +2 -2
  27. package/dist/cli/commands/obs.js +22 -22
  28. package/dist/cli/commands/orchestration.js +18 -0
  29. package/dist/cli/commands/session.js +13 -5
  30. package/dist/cli/commands/ui.js +202 -91
  31. package/dist/cli/commands/workflow.d.ts +20 -0
  32. package/dist/cli/commands/workflow.js +368 -51
  33. package/dist/cli/ink/ChatApp.d.ts +206 -0
  34. package/dist/cli/ink/ChatApp.js +493 -0
  35. package/dist/cli/ink/Frame.d.ts +26 -0
  36. package/dist/cli/ink/Frame.js +5 -0
  37. package/dist/cli/ink/Picker.d.ts +65 -0
  38. package/dist/cli/ink/Picker.js +133 -0
  39. package/dist/cli/ink/SlashPalette.d.ts +51 -0
  40. package/dist/cli/ink/SlashPalette.js +136 -0
  41. package/dist/cli/ink/TextField.d.ts +34 -0
  42. package/dist/cli/ink/TextField.js +47 -0
  43. package/dist/cli/ink/WizardApp.d.ts +7 -0
  44. package/dist/cli/ink/WizardApp.js +422 -0
  45. package/dist/cli/ink/ambientChat.d.ts +34 -0
  46. package/dist/cli/ink/ambientChat.js +7 -0
  47. package/dist/cli/ink/consoleCapture.d.ts +11 -0
  48. package/dist/cli/ink/consoleCapture.js +33 -0
  49. package/dist/cli/ink/markdownRender.d.ts +41 -0
  50. package/dist/cli/ink/markdownRender.js +278 -0
  51. package/dist/cli/ink/renderWithResizeClear.d.ts +14 -0
  52. package/dist/cli/ink/renderWithResizeClear.js +33 -0
  53. package/dist/cli/ink/runChat.d.ts +34 -0
  54. package/dist/cli/ink/runChat.js +571 -0
  55. package/dist/cli/ink/runPicker.d.ts +31 -0
  56. package/dist/cli/ink/runPicker.js +139 -0
  57. package/dist/cli/ink/runSlashPalette.d.ts +23 -0
  58. package/dist/cli/ink/runSlashPalette.js +33 -0
  59. package/dist/cli/ink/runWizard.d.ts +22 -0
  60. package/dist/cli/ink/runWizard.js +133 -0
  61. package/dist/cli/ink/stdinHandoff.d.ts +51 -0
  62. package/dist/cli/ink/stdinHandoff.js +78 -0
  63. package/dist/cli/ink/toolFormat.d.ts +73 -0
  64. package/dist/cli/ink/toolFormat.js +180 -0
  65. package/dist/cli/ink/useTerminalSize.d.ts +35 -0
  66. package/dist/cli/ink/useTerminalSize.js +26 -0
  67. package/dist/cli/repl.d.ts +25 -3
  68. package/dist/cli/repl.js +64 -646
  69. package/dist/cli/slashSuggest.d.ts +32 -0
  70. package/dist/cli/slashSuggest.js +146 -0
  71. package/dist/cli/spinner.d.ts +34 -0
  72. package/dist/cli/spinner.js +36 -0
  73. package/dist/cli/statusline.d.ts +67 -0
  74. package/dist/cli/statusline.js +204 -0
  75. package/dist/cli/theme.d.ts +79 -0
  76. package/dist/cli/theme.js +106 -0
  77. package/dist/cli/whereView.d.ts +81 -0
  78. package/dist/cli/whereView.js +245 -0
  79. package/dist/cli/wizard/modelsApi.d.ts +72 -0
  80. package/dist/cli/wizard/modelsApi.js +166 -0
  81. package/dist/cli/wizard/picker.d.ts +202 -0
  82. package/dist/cli/wizard/picker.js +547 -0
  83. package/dist/cli/wizard/providers.d.ts +86 -0
  84. package/dist/cli/wizard/providers.js +190 -0
  85. package/dist/cli/wizard/runner.d.ts +13 -0
  86. package/dist/cli/wizard/runner.js +488 -0
  87. package/dist/cli/wizard/types.d.ts +122 -0
  88. package/dist/cli/wizard/types.js +109 -0
  89. package/dist/config/config.d.ts +52 -0
  90. package/dist/config/config.js +89 -75
  91. package/dist/index.js +215 -206
  92. package/dist/memory/briefing.d.ts +11 -1
  93. package/dist/memory/briefing.js +69 -1
  94. package/dist/memory/consolidation.d.ts +1 -1
  95. package/dist/orchestration/agentRegistry.d.ts +36 -0
  96. package/dist/orchestration/agentRegistry.js +64 -0
  97. package/dist/orchestration/orchestrator.d.ts +7 -0
  98. package/dist/orchestration/orchestrator.js +2 -0
  99. package/dist/orchestration/tools.d.ts +10 -1
  100. package/dist/orchestration/tools.js +48 -4
  101. package/dist/prompt/breadthHint.d.ts +5 -0
  102. package/dist/prompt/breadthHint.js +44 -0
  103. package/dist/prompt/skillCatalog.d.ts +11 -0
  104. package/dist/prompt/skillCatalog.js +134 -0
  105. package/dist/prompt/skillRunner.d.ts +2 -2
  106. package/dist/prompt/skillRunner.js +2 -31
  107. package/dist/prompt/systemPrompt.d.ts +34 -0
  108. package/dist/prompt/systemPrompt.js +128 -108
  109. package/dist/runtime/dangerousCommand.d.ts +53 -0
  110. package/dist/runtime/dangerousCommand.js +105 -0
  111. package/dist/runtime/mcpClient.d.ts +38 -1
  112. package/dist/runtime/mcpClient.js +104 -13
  113. package/dist/runtime/mcpPool.d.ts +162 -0
  114. package/dist/runtime/mcpPool.js +423 -0
  115. package/dist/runtime/mcpUtils.d.ts +3 -1
  116. package/dist/state/goalStore.d.ts +98 -17
  117. package/dist/state/goalStore.js +132 -42
  118. package/dist/state/preferencesStore.d.ts +67 -3
  119. package/dist/state/preferencesStore.js +84 -1
  120. package/dist/state/workflowArtifacts.d.ts +63 -2
  121. package/dist/state/workflowArtifacts.js +120 -8
  122. package/dist/tests/_helpers.d.ts +31 -0
  123. package/dist/tests/_helpers.js +91 -0
  124. package/package.json +12 -5
  125. package/.env.example +0 -109
@@ -0,0 +1,109 @@
1
+ /**
2
+ * 0.3.7 wizard — pure types + step state machine.
3
+ *
4
+ * The wizard walks the user through a small, ordered sequence of
5
+ * decisions. Each step has its own decision shape; together they fill
6
+ * in a `WizardDraft` that the Done step commits to disk.
7
+ *
8
+ * Why a typed Step enum + draft (instead of one giant async function
9
+ * with awaits in sequence)? Three reasons:
10
+ *
11
+ * 1. **Esc backs out one step at a time.** A reducer transition lets
12
+ * us model "back" cleanly (Step.Provider → Step.Theme) without
13
+ * unwinding an async stack.
14
+ * 2. **The runner is testable.** Driving the reducer with synthetic
15
+ * events (`pick`, `back`, `abort`) lets us assert the wizard ends
16
+ * in a known terminal state without simulating a real TTY.
17
+ * 3. **The shape lifts straight from peer references.**
18
+ * `openSrc/codex/codex-rs/tui/src/onboarding/onboarding_screen.rs`
19
+ * uses the same Step enum + per-step state pattern; we copy the
20
+ * pattern, not the code.
21
+ */
22
+ /** Ordered list — used by the runner to compute "next" and "previous". */
23
+ export const STEP_ORDER = [
24
+ 'welcome',
25
+ 'theme',
26
+ 'provider',
27
+ 'apiKey',
28
+ 'model',
29
+ 'mcp',
30
+ 'agentMd',
31
+ 'done',
32
+ ];
33
+ export function initWizardState() {
34
+ return {
35
+ currentStep: 'welcome',
36
+ draft: {},
37
+ warnings: [],
38
+ committed: false,
39
+ aborted: false,
40
+ };
41
+ }
42
+ /**
43
+ * Compute the next step. Pure — used by `reduceWizard` and exposed for
44
+ * tests + the runner's progress indicator ("step 3 of 7").
45
+ */
46
+ export function nextStep(current) {
47
+ const idx = STEP_ORDER.indexOf(current);
48
+ if (idx < 0 || idx === STEP_ORDER.length - 1)
49
+ return undefined;
50
+ return STEP_ORDER[idx + 1];
51
+ }
52
+ export function prevStep(current) {
53
+ const idx = STEP_ORDER.indexOf(current);
54
+ if (idx <= 0)
55
+ return undefined;
56
+ return STEP_ORDER[idx - 1];
57
+ }
58
+ /**
59
+ * Pure reducer. Every wizard transition must go through here so the
60
+ * test suite can replay the same event sequence the runner emits.
61
+ *
62
+ * Contract:
63
+ * - `advance` applies the patch into the draft and steps forward;
64
+ * a no-op when called on the Done step.
65
+ * - `back` rewinds one step; a no-op on the first step.
66
+ * - `abort` lands the wizard in a terminal state with `aborted: true`
67
+ * and the draft preserved (caller may inspect for partial intent).
68
+ * - `warn` appends an advisory; doesn't move the step pointer.
69
+ * - `commit` flips `committed: true` on the Done step only.
70
+ *
71
+ * The reducer never throws — bad inputs are silently ignored so a
72
+ * stray key event doesn't crash the wizard mid-render.
73
+ */
74
+ export function reduceWizard(state, event) {
75
+ if (state.aborted || state.committed)
76
+ return state;
77
+ switch (event.kind) {
78
+ case 'advance': {
79
+ const after = nextStep(state.currentStep);
80
+ if (!after)
81
+ return state;
82
+ return {
83
+ ...state,
84
+ currentStep: after,
85
+ draft: { ...state.draft, ...event.patch },
86
+ };
87
+ }
88
+ case 'back': {
89
+ const before = prevStep(state.currentStep);
90
+ if (!before)
91
+ return state;
92
+ return { ...state, currentStep: before };
93
+ }
94
+ case 'abort':
95
+ return { ...state, aborted: true };
96
+ case 'warn':
97
+ return {
98
+ ...state,
99
+ warnings: [
100
+ ...state.warnings,
101
+ { step: state.currentStep, message: event.message },
102
+ ],
103
+ };
104
+ case 'commit':
105
+ if (state.currentStep !== 'done')
106
+ return state;
107
+ return { ...state, committed: true };
108
+ }
109
+ }
@@ -5,6 +5,25 @@ export interface ServerConfig {
5
5
  env?: Record<string, string>;
6
6
  url?: string;
7
7
  apiKey?: string;
8
+ /**
9
+ * 0.3.6 item 10a: identity tag for distinguishing the BrainRouter cloud
10
+ * brain ("our MCP") from third-party MCPs the user might attach (GitHub,
11
+ * filesystem, Slack, etc.). Drives status surfaces (banner / statusline /
12
+ * `/where`) and the offline-mode prompt swap: when "the brain" is down
13
+ * the user gets a clear signal, not a generic "MCP offline" message.
14
+ *
15
+ * Detection priority when this field is unset:
16
+ * 1. Server profile name starts with `brainrouter` (case-insensitive).
17
+ * 2. URL hostname matches `*.brainrouter.cloud` or `*.brainrouter.dev`.
18
+ * 3. (Run-time fallback) first successful `listTools()` includes
19
+ * both `memory_recall` AND `list_skills` — the BrainRouter signature
20
+ * pair. See `detectMcpIdentity` in `runtime/mcpClient.ts`.
21
+ *
22
+ * Explicit values always win — if the user marks a third-party MCP as
23
+ * `identity: 'brainrouter'`, that's their call (e.g. they're running a
24
+ * local fork that exposes the same tool surface).
25
+ */
26
+ identity?: 'brainrouter' | 'third-party';
8
27
  }
9
28
  export interface LLMConfig {
10
29
  provider: 'openai';
@@ -18,5 +37,38 @@ export interface Config {
18
37
  llm?: LLMConfig;
19
38
  }
20
39
  export declare function getConfigPath(): string;
40
+ /**
41
+ * Read the existing config.json or exit with a clear error. The CLI owns
42
+ * READS of this file — writes are the user's job (via `brainrouter login`,
43
+ * `brainrouter config`, or direct edit). Auto-fabricating a default config
44
+ * was a holdover from the monorepo dev story; it only ever produced a
45
+ * broken stdio profile pointing at a sibling `brainrouter/` package that
46
+ * doesn't exist outside the monorepo, so npm-installed users got a config
47
+ * file they had to fix anyway.
48
+ *
49
+ * Setup commands (login / config) that need to BUILD a fresh config from
50
+ * scratch should call `loadOrInitConfig` instead — it returns an empty
51
+ * skeleton when no file exists rather than exiting.
52
+ */
21
53
  export declare function loadConfig(): Config;
54
+ /**
55
+ * Pick the best API key from the environment for a given endpoint.
56
+ * Order: provider-specific envKey (matched against `PROVIDER_CATALOG`
57
+ * by endpoint), then `OPENAI_API_KEY` (most common default), then the
58
+ * generic `BRAINROUTER_LLM_API_KEY`. Returns undefined if nothing is
59
+ * set so the caller can choose how to surface that.
60
+ *
61
+ * Kept here (vs imported from `cli/wizard/providers.ts`) so non-CLI
62
+ * callers — the MCP child env propagation, future SDK clients — can
63
+ * use it without dragging in the wizard surface.
64
+ */
65
+ export declare function backfillApiKeyFromEnv(endpoint: string | undefined): string | undefined;
66
+ /**
67
+ * Setup-wizard variant of `loadConfig`. Returns the existing config when
68
+ * one is on disk, or an empty skeleton when none exists yet. Used by
69
+ * `brainrouter login` and `brainrouter config` so a first-run user can
70
+ * BUILD their config interactively without hitting the strict
71
+ * "no config — run setup" error from `loadConfig`.
72
+ */
73
+ export declare function loadOrInitConfig(): Config;
22
74
  export declare function saveConfig(config: Config): void;
@@ -1,46 +1,110 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import os from 'node:os';
4
- import { DatabaseSync } from 'node:sqlite';
5
4
  const CONFIG_DIR = path.join(os.homedir(), '.config', 'brainrouter');
6
5
  const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
7
6
  export function getConfigPath() {
8
7
  return CONFIG_FILE;
9
8
  }
9
+ /**
10
+ * Read the existing config.json or exit with a clear error. The CLI owns
11
+ * READS of this file — writes are the user's job (via `brainrouter login`,
12
+ * `brainrouter config`, or direct edit). Auto-fabricating a default config
13
+ * was a holdover from the monorepo dev story; it only ever produced a
14
+ * broken stdio profile pointing at a sibling `brainrouter/` package that
15
+ * doesn't exist outside the monorepo, so npm-installed users got a config
16
+ * file they had to fix anyway.
17
+ *
18
+ * Setup commands (login / config) that need to BUILD a fresh config from
19
+ * scratch should call `loadOrInitConfig` instead — it returns an empty
20
+ * skeleton when no file exists rather than exiting.
21
+ */
10
22
  export function loadConfig() {
11
- let config;
12
23
  if (!fs.existsSync(CONFIG_FILE)) {
13
- config = createDefaultConfig();
24
+ console.error(`No BrainRouter config found at ${CONFIG_FILE}.`);
25
+ console.error(`Run \`brainrouter login\` to connect to a hosted MCP server, or \`brainrouter config\` to set one up.`);
26
+ process.exit(1);
14
27
  }
15
- else {
16
- try {
17
- const raw = fs.readFileSync(CONFIG_FILE, 'utf8');
18
- const parsed = JSON.parse(raw);
19
- // Backfill standard properties if missing
20
- if (!parsed.servers)
21
- parsed.servers = {};
22
- if (!parsed.activeServer)
23
- parsed.activeServer = 'default';
24
- config = parsed;
25
- }
26
- catch (error) {
27
- console.error(`Warning: Failed to parse config file at ${CONFIG_FILE}. Using default config.`);
28
- config = createDefaultConfig();
29
- }
28
+ let parsed;
29
+ try {
30
+ const raw = fs.readFileSync(CONFIG_FILE, 'utf8');
31
+ parsed = JSON.parse(raw);
32
+ }
33
+ catch (error) {
34
+ console.error(`Error: Failed to parse config file at ${CONFIG_FILE}: ${error instanceof Error ? error.message : String(error)}`);
35
+ console.error(`Fix the file by hand, or delete it and run \`brainrouter config\` to recreate.`);
36
+ process.exit(1);
30
37
  }
31
- // Auto-resolve placeholder API keys if possible
32
- resolveDefaultApiKey(config);
38
+ if (!parsed.servers)
39
+ parsed.servers = {};
40
+ if (!parsed.activeServer)
41
+ parsed.activeServer = '';
33
42
  // The default config writes `llm.apiKey: ''` so it never appears as a
34
43
  // secret in the committed file. Backfill from the standard env vars at
35
44
  // load time so every downstream consumer (callOpenAI, mcpClient env
36
45
  // propagation, the cognitive extractor LLM runner) sees a real value
37
46
  // instead of the empty string.
38
- if (config.llm && !config.llm.apiKey.trim()) {
39
- const envKey = process.env.OPENAI_API_KEY || process.env.BRAINROUTER_LLM_API_KEY;
40
- if (envKey)
41
- config.llm.apiKey = envKey;
47
+ //
48
+ // 0.3.7 provider-specific fallback. Pre-0.3.7 we only checked
49
+ // OPENAI_API_KEY / BRAINROUTER_LLM_API_KEY, which silently broke
50
+ // users with config.llm.endpoint pointing at DeepSeek / OpenRouter /
51
+ // Gemini / etc. who had the *correct* provider key in their shell
52
+ // (DEEPSEEK_API_KEY, OPENROUTER_API_KEY, GEMINI_API_KEY, …). Now we
53
+ // match the saved endpoint to a provider entry and try ITS envKey
54
+ // FIRST, then fall through to the generic vars.
55
+ if (parsed.llm && !parsed.llm.apiKey.trim()) {
56
+ parsed.llm.apiKey = backfillApiKeyFromEnv(parsed.llm.endpoint) ?? '';
57
+ }
58
+ return parsed;
59
+ }
60
+ /**
61
+ * Pick the best API key from the environment for a given endpoint.
62
+ * Order: provider-specific envKey (matched against `PROVIDER_CATALOG`
63
+ * by endpoint), then `OPENAI_API_KEY` (most common default), then the
64
+ * generic `BRAINROUTER_LLM_API_KEY`. Returns undefined if nothing is
65
+ * set so the caller can choose how to surface that.
66
+ *
67
+ * Kept here (vs imported from `cli/wizard/providers.ts`) so non-CLI
68
+ * callers — the MCP child env propagation, future SDK clients — can
69
+ * use it without dragging in the wizard surface.
70
+ */
71
+ export function backfillApiKeyFromEnv(endpoint) {
72
+ // Provider-specific env vars in order of catalog precedence. Hardcoded
73
+ // here so this function stays free of the wizard import (which pulls
74
+ // in chalk, ink picker types, etc.). Keep in lockstep with
75
+ // `cli/wizard/providers.ts → PROVIDER_CATALOG`.
76
+ const PROVIDER_ENV_BY_ENDPOINT = [
77
+ { endpoint: 'https://api.openai.com/v1', envKey: 'OPENAI_API_KEY' },
78
+ { endpoint: 'https://api.deepseek.com/v1', envKey: 'DEEPSEEK_API_KEY' },
79
+ { endpoint: 'https://openrouter.ai/api/v1', envKey: 'OPENROUTER_API_KEY' },
80
+ { endpoint: 'https://generativelanguage.googleapis.com/v1beta/openai', envKey: 'GEMINI_API_KEY' },
81
+ { endpoint: 'https://api.anthropic.com/v1', envKey: 'ANTHROPIC_API_KEY' },
82
+ { endpoint: 'http://localhost:1234/v1', envKey: 'LMSTUDIO_API_KEY' },
83
+ { endpoint: 'http://localhost:11434/v1', envKey: 'OLLAMA_API_KEY' },
84
+ ];
85
+ if (endpoint) {
86
+ const trimmed = endpoint.replace(/\/$/, '');
87
+ const match = PROVIDER_ENV_BY_ENDPOINT.find((p) => p.endpoint === trimmed);
88
+ if (match) {
89
+ const value = process.env[match.envKey];
90
+ if (value && value.trim())
91
+ return value.trim();
92
+ }
42
93
  }
43
- return config;
94
+ return process.env.OPENAI_API_KEY?.trim() || process.env.BRAINROUTER_LLM_API_KEY?.trim() || undefined;
95
+ }
96
+ /**
97
+ * Setup-wizard variant of `loadConfig`. Returns the existing config when
98
+ * one is on disk, or an empty skeleton when none exists yet. Used by
99
+ * `brainrouter login` and `brainrouter config` so a first-run user can
100
+ * BUILD their config interactively without hitting the strict
101
+ * "no config — run setup" error from `loadConfig`.
102
+ */
103
+ export function loadOrInitConfig() {
104
+ if (!fs.existsSync(CONFIG_FILE)) {
105
+ return { activeServer: '', servers: {} };
106
+ }
107
+ return loadConfig();
44
108
  }
45
109
  export function saveConfig(config) {
46
110
  try {
@@ -53,53 +117,3 @@ export function saveConfig(config) {
53
117
  console.error(`Error: Failed to save config to ${CONFIG_FILE}:`, error instanceof Error ? error.message : error);
54
118
  }
55
119
  }
56
- function createDefaultConfig() {
57
- // Derive path to the default local MCP server dist relative to this module.
58
- // After build: brainrouter-cli/dist/config/config.js → walk three levels up
59
- // to the monorepo root, then into the sibling `brainrouter/` package
60
- // (formerly `mcp/`) which is the MCP server.
61
- const defaultMcpPath = path.resolve(import.meta.dirname, '..', '..', '..', 'brainrouter', 'dist', 'index.js');
62
- const config = {
63
- activeServer: 'default',
64
- servers: {
65
- default: {
66
- type: 'stdio',
67
- command: 'node',
68
- args: [defaultMcpPath, '--root', './'],
69
- env: {
70
- BRAINROUTER_API_KEY: 'br_admin_key_placeholder'
71
- }
72
- }
73
- },
74
- llm: {
75
- provider: 'openai',
76
- apiKey: '',
77
- model: 'gpt-4o-mini',
78
- endpoint: 'https://api.openai.com/v1'
79
- }
80
- };
81
- saveConfig(config);
82
- return config;
83
- }
84
- function resolveDefaultApiKey(config) {
85
- const defaultServer = config.servers.default;
86
- if (defaultServer &&
87
- defaultServer.type === 'stdio' &&
88
- defaultServer.env &&
89
- defaultServer.env.BRAINROUTER_API_KEY === 'br_admin_key_placeholder') {
90
- const dbPath = process.env.BRAINROUTER_MEMORY_DB || path.join(os.homedir(), '.brainrouter', 'memory.db');
91
- if (fs.existsSync(dbPath)) {
92
- try {
93
- const db = new DatabaseSync(dbPath);
94
- const row = db.prepare("SELECT api_key FROM users WHERE is_admin = 1 LIMIT 1").get();
95
- if (row && row.api_key) {
96
- defaultServer.env.BRAINROUTER_API_KEY = row.api_key;
97
- saveConfig(config);
98
- }
99
- }
100
- catch (error) {
101
- // ignore errors
102
- }
103
- }
104
- }
105
- }