63 lines
1.9 KiB
TypeScript
63 lines
1.9 KiB
TypeScript
/**
|
|
* Runtime feature flags. Backed by a tiny single-row table so an admin
|
|
* can flip a flag and have every Vibn pod pick it up within seconds (no
|
|
* redeploy required).
|
|
*
|
|
* Currently used for:
|
|
* - path_b_disabled : kill switch for the Path B AI dev-container
|
|
* architecture. When true, shell.exec / fs.* /
|
|
* devcontainer.* tools return 503 and the chat
|
|
* system prompt falls back to Path A guidance.
|
|
*
|
|
* See AI_PATH_B_EXECUTION_PLAN.md §7 for the rollback story.
|
|
*/
|
|
|
|
import { query, queryOne } from '@/lib/db-postgres';
|
|
|
|
let tableReady = false;
|
|
async function ensureFlagsTable(): Promise<void> {
|
|
if (tableReady) return;
|
|
await query(
|
|
`CREATE TABLE IF NOT EXISTS fs_feature_flags (
|
|
key TEXT PRIMARY KEY,
|
|
value JSONB NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);`,
|
|
[],
|
|
);
|
|
tableReady = true;
|
|
}
|
|
|
|
const TTL_MS = 10_000;
|
|
const cache = new Map<string, { value: any; expires: number }>();
|
|
|
|
export async function getFlag<T = unknown>(key: string, defaultValue: T): Promise<T> {
|
|
const cached = cache.get(key);
|
|
if (cached && cached.expires > Date.now()) return cached.value as T;
|
|
await ensureFlagsTable();
|
|
const row = await queryOne<{ value: T }>(
|
|
`SELECT value FROM fs_feature_flags WHERE key = $1 LIMIT 1`,
|
|
[key],
|
|
);
|
|
const value = row?.value ?? defaultValue;
|
|
cache.set(key, { value, expires: Date.now() + TTL_MS });
|
|
return value;
|
|
}
|
|
|
|
export async function setFlag(key: string, value: unknown): Promise<void> {
|
|
await ensureFlagsTable();
|
|
await query(
|
|
`INSERT INTO fs_feature_flags (key, value, updated_at)
|
|
VALUES ($1, $2::jsonb, now())
|
|
ON CONFLICT (key) DO UPDATE
|
|
SET value = EXCLUDED.value,
|
|
updated_at = now()`,
|
|
[key, JSON.stringify(value)],
|
|
);
|
|
cache.delete(key);
|
|
}
|
|
|
|
export async function isPathBDisabled(): Promise<boolean> {
|
|
return Boolean(await getFlag<boolean>('path_b_disabled', false));
|
|
}
|