provider-snapshot no longer returns null for uninstalled/disabled providers: it emits one entry per registered provider with a lifecycle status (loading|ready|unavailable|error), an enabled flag, and a two-tier probe. Tier-1 is a fast which-style check (command-availability.ts, execFile/no-shell); tier-2 (cold ACP probe) is skipped unless forced, last_probed_at is older than PROVIDER_PROBE_TTL_MS (24h), or DB models are empty — the snapshot-latency win. Cache miss returns status:'loading' synchronously while the build settles via the existing inflight promise. ProviderSnapshotStatus/Entry regain loading/unavailable + gain enabled/description?/fetchedAt? in both coder and web copies, guarded by a runtime parity test (provider-types-parity.test.ts; compile-time cross-project check was blocked by TS6307). Also tracks the data/coder-providers.json seed via a .gitignore exception, completing the Phase 1 config file. No dispatch/route/UI changes (Phase 3+); AgentComposerBar filtering unchanged. 13 snapshot tests (+6) + 6 parity tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
55 lines
2.5 KiB
TypeScript
55 lines
2.5 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
// BooCoder's config is a superset of the server's Config type so it can be
|
|
// passed directly into the inference runner's InferenceContext. Fields the
|
|
// inference loop reads: LLAMA_SWAP_URL, PROJECT_ROOT_WHITELIST. The rest
|
|
// default to values that satisfy the server's Zod schema without BooCoder
|
|
// needing to supply them in its environment.
|
|
const ConfigSchema = z.object({
|
|
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
|
PORT: z.coerce.number().int().positive().default(3000),
|
|
HOST: z.string().default('0.0.0.0'),
|
|
DATABASE_URL: z.string().url(),
|
|
LLAMA_SWAP_URL: z.string().url(),
|
|
PROJECT_ROOT_WHITELIST: z.string().default('/opt'),
|
|
BOOTSTRAP_ROOT: z.string().default('/opt/projects'),
|
|
DEFAULT_MODEL: z.string().default('qwen3.6-35b-a3b-mxfp4'),
|
|
LOG_LEVEL: z.string().default('info'),
|
|
CONTAINER_GUIDANCE_FILE: z.string().optional(),
|
|
// Fields needed to satisfy the server's Config type but unused by BooCoder:
|
|
SEARXNG_URL: z.string().url().default('http://100.114.205.53:8888'),
|
|
GITEA_BASE_URL: z.string().url().default('https://git.indifferentketchup.com'),
|
|
GITEA_USER: z.string().default('indifferentketchup'),
|
|
GITEA_TOKEN: z.string().optional(),
|
|
GITEA_SSH_HOST: z.string().default('100.114.205.53:2222'),
|
|
MCP_CONFIG_PATH: z.string().optional(),
|
|
// v2.3: config-backed provider overrides/custom-ACP entries merged over the
|
|
// hardcoded built-ins. Missing file = built-ins only (see provider-config.ts).
|
|
CODER_PROVIDERS_PATH: z.string().default('/data/coder-providers.json'),
|
|
// v2.3 phase 2: tier-2 (cold ACP probe) is skipped when available_agents was
|
|
// probed more recently than this. 24h default — stale model lists self-heal
|
|
// on the next snapshot; an explicit /refresh always re-probes.
|
|
PROVIDER_PROBE_TTL_MS: z.coerce.number().int().positive().default(86_400_000),
|
|
// v2.0.5: cheaper model for titles, summaries, labeling.
|
|
FAST_MODEL: z.string().optional(),
|
|
// SSH access to the host for external agent dispatch (Phase 5)
|
|
BOOCODER_SSH_HOST: z.string().default('100.114.205.53'),
|
|
BOOCODER_SSH_USER: z.string().default('samkintop'),
|
|
});
|
|
|
|
export type Config = z.infer<typeof ConfigSchema>;
|
|
|
|
let cached: Config | null = null;
|
|
|
|
export function loadConfig(): Config {
|
|
if (cached) return cached;
|
|
const parsed = ConfigSchema.safeParse(process.env);
|
|
if (!parsed.success) {
|
|
console.error('Invalid environment configuration:');
|
|
console.error(parsed.error.flatten().fieldErrors);
|
|
process.exit(1);
|
|
}
|
|
cached = parsed.data;
|
|
return cached;
|
|
}
|