86 lines
3.0 KiB
TypeScript
86 lines
3.0 KiB
TypeScript
/**
|
|
* Server-side singleton for core services.
|
|
*
|
|
* Lazily initializes config, plugin registry, and session manager.
|
|
* Cached in globalThis to survive Next.js HMR reloads in development.
|
|
*
|
|
* NOTE: Plugins are explicitly imported here because Next.js webpack
|
|
* cannot resolve dynamic `import(variable)` expressions used by the
|
|
* core plugin registry's loadBuiltins(). Static imports let webpack
|
|
* bundle them correctly.
|
|
*/
|
|
|
|
import {
|
|
loadConfig,
|
|
createPluginRegistry,
|
|
createSessionManager,
|
|
type OrchestratorConfig,
|
|
type PluginRegistry,
|
|
type OpenCodeSessionManager,
|
|
type SCM,
|
|
type ProjectConfig,
|
|
} from "@composio/ao-core";
|
|
|
|
// Static plugin imports — webpack needs these to be string literals
|
|
import pluginRuntimeTmux from "@composio/ao-plugin-runtime-tmux";
|
|
import pluginAgentClaudeCode from "@composio/ao-plugin-agent-claude-code";
|
|
import pluginAgentOpencode from "@composio/ao-plugin-agent-opencode";
|
|
import pluginWorkspaceWorktree from "@composio/ao-plugin-workspace-worktree";
|
|
import pluginScmGithub from "@composio/ao-plugin-scm-github";
|
|
import pluginTrackerGithub from "@composio/ao-plugin-tracker-github";
|
|
import pluginTrackerLinear from "@composio/ao-plugin-tracker-linear";
|
|
|
|
export interface Services {
|
|
config: OrchestratorConfig;
|
|
registry: PluginRegistry;
|
|
sessionManager: OpenCodeSessionManager;
|
|
}
|
|
|
|
// Cache in globalThis for Next.js HMR stability
|
|
const globalForServices = globalThis as typeof globalThis & {
|
|
_aoServices?: Services;
|
|
_aoServicesInit?: Promise<Services>;
|
|
};
|
|
|
|
/** Get (or lazily initialize) the core services singleton. */
|
|
export function getServices(): Promise<Services> {
|
|
if (globalForServices._aoServices) {
|
|
return Promise.resolve(globalForServices._aoServices);
|
|
}
|
|
if (!globalForServices._aoServicesInit) {
|
|
globalForServices._aoServicesInit = initServices().catch((err) => {
|
|
// Clear the cached promise so the next call retries instead of
|
|
// permanently returning a rejected promise.
|
|
globalForServices._aoServicesInit = undefined;
|
|
throw err;
|
|
});
|
|
}
|
|
return globalForServices._aoServicesInit;
|
|
}
|
|
|
|
async function initServices(): Promise<Services> {
|
|
const config = loadConfig();
|
|
const registry = createPluginRegistry();
|
|
|
|
// Register plugins explicitly (webpack can't handle dynamic import() in core)
|
|
registry.register(pluginRuntimeTmux);
|
|
registry.register(pluginAgentClaudeCode);
|
|
registry.register(pluginAgentOpencode);
|
|
registry.register(pluginWorkspaceWorktree);
|
|
registry.register(pluginScmGithub);
|
|
registry.register(pluginTrackerGithub);
|
|
registry.register(pluginTrackerLinear);
|
|
|
|
const sessionManager = createSessionManager({ config, registry });
|
|
|
|
const services = { config, registry, sessionManager };
|
|
globalForServices._aoServices = services;
|
|
return services;
|
|
}
|
|
|
|
/** Resolve the SCM plugin for a project. Returns null if not configured. */
|
|
export function getSCM(registry: PluginRegistry, project: ProjectConfig | undefined): SCM | null {
|
|
if (!project?.scm) return null;
|
|
return registry.get<SCM>("scm", project.scm.plugin);
|
|
}
|