diff --git a/apps/x/packages/core/src/config/initConfigs.ts b/apps/x/packages/core/src/config/initConfigs.ts index adfb8b24..76017229 100644 --- a/apps/x/packages/core/src/config/initConfigs.ts +++ b/apps/x/packages/core/src/config/initConfigs.ts @@ -3,6 +3,7 @@ import type { IModelConfigRepo } from "../models/repo.js"; import type { IMcpConfigRepo } from "../mcp/repo.js"; import type { IAgentScheduleRepo } from "../agent-schedule/repo.js"; import type { IAgentScheduleStateRepo } from "../agent-schedule/state-repo.js"; +import type { IConfigRepo } from "./repo.js"; import { ensureSecurityConfig } from "./security.js"; /** @@ -15,12 +16,14 @@ export async function initConfigs(): Promise { const mcpConfigRepo = container.resolve("mcpConfigRepo"); const agentScheduleRepo = container.resolve("agentScheduleRepo"); const agentScheduleStateRepo = container.resolve("agentScheduleStateRepo"); + const configRepo = container.resolve("configRepo"); await Promise.all([ modelConfigRepo.ensureConfig(), mcpConfigRepo.ensureConfig(), agentScheduleRepo.ensureConfig(), agentScheduleStateRepo.ensureState(), + configRepo.ensureConfig(), ensureSecurityConfig(), ]); } diff --git a/apps/x/packages/core/src/config/repo.ts b/apps/x/packages/core/src/config/repo.ts new file mode 100644 index 00000000..58ab5533 --- /dev/null +++ b/apps/x/packages/core/src/config/repo.ts @@ -0,0 +1,40 @@ +import fs from "fs"; +import fsp from "fs/promises"; +import path from "path"; +import { AppConfig } from "@x/shared/dist/config.js"; +import { WorkDir } from "./config.js"; +import type z from "zod"; + +export interface IConfigRepo { + ensureConfig(): Promise; + getConfigSync(): z.infer; + setConfig(config: z.infer): Promise; +} + +const defaultConfig: z.infer = { + executionProfile: { mode: "local" }, +}; + +export class FSConfigRepo implements IConfigRepo { + private readonly configPath = path.join(WorkDir, "config", "config.json"); + + async ensureConfig(): Promise { + try { + await fsp.access(this.configPath); + } catch { + await fsp.writeFile(this.configPath, JSON.stringify(defaultConfig, null, 2)); + } + } + + getConfigSync(): z.infer { + if (!fs.existsSync(this.configPath)) { + fs.writeFileSync(this.configPath, JSON.stringify(defaultConfig, null, 2)); + } + const raw = fs.readFileSync(this.configPath, "utf8"); + return AppConfig.parse(JSON.parse(raw)); + } + + async setConfig(config: z.infer): Promise { + await fsp.writeFile(this.configPath, JSON.stringify(config, null, 2)); + } +} diff --git a/apps/x/packages/core/src/di/container.ts b/apps/x/packages/core/src/di/container.ts index 2d756fb7..22c27220 100644 --- a/apps/x/packages/core/src/di/container.ts +++ b/apps/x/packages/core/src/di/container.ts @@ -14,6 +14,7 @@ import { FSGranolaConfigRepo, IGranolaConfigRepo } from "../knowledge/granola/re import { IAbortRegistry, InMemoryAbortRegistry } from "../runs/abort-registry.js"; import { FSAgentScheduleRepo, IAgentScheduleRepo } from "../agent-schedule/repo.js"; import { FSAgentScheduleStateRepo, IAgentScheduleStateRepo } from "../agent-schedule/state-repo.js"; +import { FSConfigRepo, IConfigRepo } from "../config/repo.js"; import type { ILlmService } from "../execution/llm-service.js"; import type { IGmailService } from "../execution/gmail-service.js"; import type { ISttService } from "../execution/stt-service.js"; @@ -46,10 +47,24 @@ container.register({ agentScheduleRepo: asClass(FSAgentScheduleRepo).singleton(), agentScheduleStateRepo: asClass(FSAgentScheduleStateRepo).singleton(), - llmService: asClass(LocalLlmService).singleton(), - gmailService: asClass(LocalGmailService).singleton(), - sttService: asClass(LocalSttService).singleton(), - composioService: asClass(LocalComposioService).singleton(), + configRepo: asClass(FSConfigRepo).singleton(), }); -export default container; \ No newline at end of file +const appConfig = container.resolve("configRepo").getConfigSync(); + +switch (appConfig.executionProfile.mode) { + case "local": + container.register({ + llmService: asClass(LocalLlmService).singleton(), + gmailService: asClass(LocalGmailService).singleton(), + sttService: asClass(LocalSttService).singleton(), + composioService: asClass(LocalComposioService).singleton(), + }); + break; + case "cloud": + throw new Error("Cloud execution profile not supported yet. Configure {\"executionProfile\":{\"mode\":\"local\"}}."); + default: + throw new Error(`Unsupported execution profile mode: ${(appConfig.executionProfile as { mode: string }).mode}`); +} + +export default container; diff --git a/apps/x/packages/core/src/execution/factory.ts b/apps/x/packages/core/src/execution/factory.ts deleted file mode 100644 index a50a9ccd..00000000 --- a/apps/x/packages/core/src/execution/factory.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { ExecutionProfile } from "@x/shared/dist/execution-profile.js"; -import { ILlmService } from "./llm-service.js"; -import { IGmailService } from "./gmail-service.js"; -import { ISttService } from "./stt-service.js"; -import { IComposioService } from "./composio-service.js"; -import { LocalLlmService } from "./local/local-llm-service.js"; -import { LocalGmailService } from "./local/local-gmail-service.js"; -import { LocalSttService } from "./local/local-stt-service.js"; -import { LocalComposioService } from "./local/local-composio-service.js"; - -export interface ExecutionServices { - llm: ILlmService; - gmail: IGmailService; - stt: ISttService; - composio: IComposioService; -} - -export function createServices(profile: ExecutionProfile): ExecutionServices { - switch (profile.mode) { - case "local": - return { - llm: new LocalLlmService(), - gmail: new LocalGmailService(), - stt: new LocalSttService(), - composio: new LocalComposioService(), - }; - default: - throw new Error(`Unsupported execution profile mode: ${(profile as { mode: string }).mode}`); - } -} diff --git a/apps/x/packages/core/src/execution/index.ts b/apps/x/packages/core/src/execution/index.ts index cb7d6f8d..4981476e 100644 --- a/apps/x/packages/core/src/execution/index.ts +++ b/apps/x/packages/core/src/execution/index.ts @@ -2,8 +2,6 @@ export type { ILlmService } from "./llm-service.js"; export type { IGmailService } from "./gmail-service.js"; export type { ISttService } from "./stt-service.js"; export type { IComposioService } from "./composio-service.js"; -export { createServices } from "./factory.js"; -export type { ExecutionServices } from "./factory.js"; // Local implementations export { LocalLlmService } from "./local/local-llm-service.js"; diff --git a/apps/x/packages/shared/src/config.ts b/apps/x/packages/shared/src/config.ts new file mode 100644 index 00000000..0668c3b8 --- /dev/null +++ b/apps/x/packages/shared/src/config.ts @@ -0,0 +1,10 @@ +import { z } from "zod"; +import { ExecutionProfile } from "./execution-profile.js"; + +export const AppConfig = z + .object({ + executionProfile: ExecutionProfile, + }) + .passthrough(); + +export type AppConfig = z.infer; diff --git a/apps/x/packages/shared/src/index.ts b/apps/x/packages/shared/src/index.ts index f84804c1..f68bdc45 100644 --- a/apps/x/packages/shared/src/index.ts +++ b/apps/x/packages/shared/src/index.ts @@ -8,4 +8,5 @@ export * as agentSchedule from './agent-schedule.js'; export * as agentScheduleState from './agent-schedule-state.js'; export * as serviceEvents from './service-events.js'; export * as executionProfile from './execution-profile.js'; +export * as config from './config.js'; export { PrefixLogger };