fix(core): harden env loading, OpenAI compatibility, and runtime recovery
This commit is contained in:
@@ -484,6 +484,52 @@ models:
|
||||
}
|
||||
});
|
||||
|
||||
it('loads env vars from FLYNN_ENV_FILE before env-var checks', async () => {
|
||||
const originalEnvFile = process.env.FLYNN_ENV_FILE;
|
||||
const originalOpenAIKey = process.env.OPENAI_API_KEY;
|
||||
delete process.env.OPENAI_API_KEY;
|
||||
|
||||
try {
|
||||
mkdirSync(testDir, { recursive: true });
|
||||
const envPath = join(testDir, 'cloud.env');
|
||||
writeFileSync(envPath, 'OPENAI_API_KEY=sk-test-from-env-file\n');
|
||||
process.env.FLYNN_ENV_FILE = envPath;
|
||||
|
||||
const configPath = join(testDir, 'openai-env-file.yaml');
|
||||
writeFileSync(configPath, `
|
||||
telegram:
|
||||
bot_token: "test-token"
|
||||
allowed_chat_ids: [123]
|
||||
models:
|
||||
default:
|
||||
provider: openai
|
||||
model: gpt-5.2
|
||||
auth_mode: api_key
|
||||
api_key: \${OPENAI_API_KEY}
|
||||
`);
|
||||
|
||||
const ctx: DoctorContext = { configPath, dataDir: testDir };
|
||||
const results = await runChecks(ctx);
|
||||
const envCheck = results.find((r) => r.label.includes('Env vars resolved')) as CheckResult | undefined;
|
||||
const modelCheck = results.find((r) => r.label.includes('Model connectivity')) as CheckResult | undefined;
|
||||
|
||||
expect(envCheck?.status).toBe('pass');
|
||||
expect(modelCheck?.status).toBe('pass');
|
||||
expect(modelCheck?.detail).toContain('api_key=config+env');
|
||||
} finally {
|
||||
if (originalEnvFile !== undefined) {
|
||||
process.env.FLYNN_ENV_FILE = originalEnvFile;
|
||||
} else {
|
||||
delete process.env.FLYNN_ENV_FILE;
|
||||
}
|
||||
if (originalOpenAIKey !== undefined) {
|
||||
process.env.OPENAI_API_KEY = originalOpenAIKey;
|
||||
} else {
|
||||
delete process.env.OPENAI_API_KEY;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('reports WARN when Vercel AI Gateway has no available API key sources', async () => {
|
||||
const originalKey = process.env.AI_GATEWAY_API_KEY;
|
||||
delete process.env.AI_GATEWAY_API_KEY;
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
import type { Command } from 'commander';
|
||||
import type { Config } from '../config/index.js';
|
||||
import { getConfigPath, getDataDir, formatStatus, resolveOverlayPath } from './shared.js';
|
||||
import { getConfigPath, getDataDir, formatStatus, resolveOverlayPath, loadEnvFileIfPresent } from './shared.js';
|
||||
import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'fs';
|
||||
import { homedir } from 'os';
|
||||
import { resolve, join } from 'path';
|
||||
@@ -643,6 +643,8 @@ const allChecks: Check[] = [
|
||||
|
||||
/** Run all doctor checks in order. Exported for testing. */
|
||||
export async function runChecks(ctx: DoctorContext): Promise<CheckResult[]> {
|
||||
// Keep doctor behavior aligned with other CLI commands that load cloud.env.
|
||||
loadEnvFileIfPresent();
|
||||
const results: CheckResult[] = [];
|
||||
for (const check of allChecks) {
|
||||
const result = await check(ctx);
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import { resolve, dirname, join } from 'path';
|
||||
import { homedir } from 'os';
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
|
||||
function loadEnvFileIfPresent(): void {
|
||||
export function loadEnvFileIfPresent(): void {
|
||||
const envFile = process.env.FLYNN_ENV_FILE ?? resolve(homedir(), '.config/flynn/cloud.env');
|
||||
if (!existsSync(envFile)) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user