manager.ts 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. import type { PluginInput } from '@opencode-ai/plugin';
  2. import type { PluginConfig } from '../config';
  3. import { createInterviewServer } from './server';
  4. import { createInterviewService } from './service';
  5. /**
  6. * Interview Manager - Composition root wiring the lean service ↔ server flow.
  7. *
  8. * Architecture:
  9. * - Service: in-memory interview runtime + markdown document updates
  10. * - Server: localhost UI + JSON API
  11. * - Manager: small adapter exposing plugin hooks
  12. *
  13. * Dependency injection pattern:
  14. * - Server depends on service.getState and service.submitAnswers
  15. * - Service depends on server.ensureStarted (via setBaseUrlResolver)
  16. * - Circular dependency resolved by lazy resolution
  17. *
  18. * Plugin integration:
  19. * - registerCommand: injects /interview into OpenCode config
  20. * - handleCommandExecuteBefore: intercepts /interview execution
  21. * - handleEvent: listens to session.status and session.deleted events
  22. */
  23. export function createInterviewManager(
  24. ctx: PluginInput,
  25. config: PluginConfig,
  26. ): {
  27. registerCommand: (config: Record<string, unknown>) => void;
  28. handleCommandExecuteBefore: (
  29. input: { command: string; sessionID: string; arguments: string },
  30. output: { parts: Array<{ type: string; text?: string }> },
  31. ) => Promise<void>;
  32. handleEvent: (input: {
  33. event: { type: string; properties?: Record<string, unknown> };
  34. }) => Promise<void>;
  35. } {
  36. const service = createInterviewService(ctx, config.interview);
  37. const server = createInterviewServer({
  38. getState: async (interviewId) => service.getInterviewState(interviewId),
  39. submitAnswers: async (interviewId, answers) =>
  40. service.submitAnswers(interviewId, answers),
  41. port: config.interview?.port ?? 0,
  42. });
  43. // Inject server URL resolver into service (lazy: server starts on first request)
  44. service.setBaseUrlResolver(() => server.ensureStarted());
  45. return {
  46. registerCommand: (config) => service.registerCommand(config),
  47. handleCommandExecuteBefore: async (input, output) =>
  48. service.handleCommandExecuteBefore(input, output),
  49. handleEvent: async (input) => service.handleEvent(input),
  50. };
  51. }