fix(config): persist runtime patches to active overlay config path
This commit is contained in:
@@ -5669,6 +5669,20 @@
|
||||
"docs/plans/state.json"
|
||||
],
|
||||
"test_status": "pnpm typecheck passing"
|
||||
},
|
||||
"overlay-aware-runtime-config-persistence": {
|
||||
"status": "completed",
|
||||
"date": "2026-02-19",
|
||||
"updated": "2026-02-19",
|
||||
"summary": "Made runtime config persistence overlay-aware: when `FLYNN_ENV` overlay file is active, daemon/gateway config.patch now persists to the effective overlay config path instead of always writing base config.yaml.",
|
||||
"files_modified": [
|
||||
"src/cli/shared.ts",
|
||||
"src/cli/start.ts",
|
||||
"src/cli/setup.ts",
|
||||
"src/daemon/index.ts",
|
||||
"docs/plans/state.json"
|
||||
],
|
||||
"test_status": "pnpm typecheck passing"
|
||||
}
|
||||
},
|
||||
"overall_progress": {
|
||||
|
||||
+3
-2
@@ -3,7 +3,7 @@ import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
||||
import { dirname } from 'path';
|
||||
import { createInterface } from 'readline/promises';
|
||||
import { parse } from 'yaml';
|
||||
import { getConfigPath } from './shared.js';
|
||||
import { getConfigPath, resolveEffectiveConfigPath } from './shared.js';
|
||||
import { createPrompter } from './setup/prompts.js';
|
||||
import { ConfigBuilder } from './setup/config.js';
|
||||
import { runFirstRunWizard, runMenu } from './setup/orchestrator.js';
|
||||
@@ -47,7 +47,8 @@ export async function runSetup(configPath: string): Promise<void> {
|
||||
const { startDaemon } = await import('../daemon/index.js');
|
||||
const { loadConfig } = await import('../config/index.js');
|
||||
const config = loadConfig(configPath);
|
||||
const daemon = await startDaemon(config, { configPath });
|
||||
const persistConfigPath = resolveEffectiveConfigPath(configPath);
|
||||
const daemon = await startDaemon(config, { configPath, persistConfigPath });
|
||||
await new Promise<void>(resolve => daemon.lifecycle.onShutdown(async () => resolve()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,19 @@ export function resolveOverlayPath(basePath: string): string | undefined {
|
||||
return join(configDir, `${env}.yaml`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the effective config source path for runtime persistence.
|
||||
* When FLYNN_ENV is set and overlay file exists, persist to the overlay file.
|
||||
* Otherwise persist to the base config path.
|
||||
*/
|
||||
export function resolveEffectiveConfigPath(basePath: string): string {
|
||||
const overlayPath = resolveOverlayPath(basePath);
|
||||
if (overlayPath && existsSync(overlayPath)) {
|
||||
return overlayPath;
|
||||
}
|
||||
return basePath;
|
||||
}
|
||||
|
||||
/** Load config without throwing. Returns { config } or { error }. */
|
||||
export function loadConfigSafe(configPath?: string): { config?: Config; error?: string } {
|
||||
const path = configPath ?? getConfigPath();
|
||||
|
||||
+6
-2
@@ -1,5 +1,5 @@
|
||||
import type { Command } from 'commander';
|
||||
import { loadConfigSafe, getConfigPath } from './shared.js';
|
||||
import { loadConfigSafe, getConfigPath, resolveEffectiveConfigPath } from './shared.js';
|
||||
import { existsSync } from 'fs';
|
||||
|
||||
export function registerStartCommand(program: Command): void {
|
||||
@@ -9,6 +9,7 @@ export function registerStartCommand(program: Command): void {
|
||||
.option('-c, --config <path>', 'Config file path')
|
||||
.action(async (opts: { config?: string }) => {
|
||||
const configPath = opts.config ?? getConfigPath();
|
||||
const persistConfigPath = resolveEffectiveConfigPath(configPath);
|
||||
|
||||
if (!existsSync(configPath)) {
|
||||
// Offer setup wizard
|
||||
@@ -35,6 +36,9 @@ export function registerStartCommand(program: Command): void {
|
||||
|
||||
console.log('Flynn starting...');
|
||||
console.log(`Loading config from: ${configPath}`);
|
||||
if (persistConfigPath !== configPath) {
|
||||
console.log(`Runtime config persistence target: ${persistConfigPath}`);
|
||||
}
|
||||
|
||||
const { config, error } = loadConfigSafe(configPath);
|
||||
if (!config) {
|
||||
@@ -44,7 +48,7 @@ export function registerStartCommand(program: Command): void {
|
||||
|
||||
// Dynamic import to avoid loading daemon code for other commands
|
||||
const { startDaemon } = await import('../daemon/index.js');
|
||||
const daemon = await startDaemon(config, { configPath });
|
||||
const daemon = await startDaemon(config, { configPath, persistConfigPath });
|
||||
|
||||
if (config.telegram) {
|
||||
console.log(`Allowed Telegram chat IDs: ${config.telegram.allowed_chat_ids.join(', ')}`);
|
||||
|
||||
+2
-1
@@ -108,6 +108,7 @@ export interface DaemonContext {
|
||||
|
||||
export interface StartDaemonOptions {
|
||||
configPath?: string;
|
||||
persistConfigPath?: string;
|
||||
}
|
||||
|
||||
export async function startDaemon(config: Config, options?: StartDaemonOptions): Promise<DaemonContext> {
|
||||
@@ -206,7 +207,7 @@ export async function startDaemon(config: Config, options?: StartDaemonOptions):
|
||||
let channelAgents: ReturnType<typeof createMessageRouter>['agents'] | null = null;
|
||||
|
||||
const gateway = createGateway({
|
||||
config, configPath: options?.configPath, sessionManager, modelRouter, systemPrompt, toolRegistry, toolExecutor,
|
||||
config, configPath: options?.persistConfigPath ?? options?.configPath, sessionManager, modelRouter, systemPrompt, toolRegistry, toolExecutor,
|
||||
channelRegistry, pairingManager, lifecycle, memoryStore,
|
||||
getChannelAgents: () => channelAgents, commandRegistry, intentRegistry, routingPolicy, hookEngine,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user