start.ts 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. /**
  2. * One-step host startup seam: boot core → assemble ApiProxy → assemble the
  3. * fetch handler. The returned RunningHost is shell-agnostic — node:http
  4. * (dsh web), in-process injection (dsh -p, tests), an IPC bridge (future
  5. * Electron sidecar), and front-door plugin mounting (future dsh acp) all
  6. * consume the same shape.
  7. */
  8. import type { Context } from 'cordis'
  9. import type { ApiProxy } from '@deepseek-ai/dsh-host-apiproxy/api'
  10. import { toFetchHandler } from '@deepseek-ai/dsh-host-apiproxy'
  11. import { bootHost } from './boot.ts'
  12. import type { BootHostOptions, HostDefaults } from './boot.ts'
  13. import { createApiProxy } from './api-proxy.ts'
  14. /** Options for startHost. */
  15. export interface StartHostOptions {
  16. /**
  17. * Passed through to bootHost verbatim (persistenceRoot required +
  18. * provider?/model?). Future host-level knobs (profile, log sink — any
  19. * output added to the assembly MUST be switchable off here) land as
  20. * additive fields.
  21. */
  22. boot: BootHostOptions
  23. }
  24. /** Running host handle: the contract impl plus its fetch carrier and root ctx. */
  25. export interface RunningHost {
  26. /** Contract implementation (direct calls for in-process consumers; the input of an IPC adapter). */
  27. api: ApiProxy
  28. /** WHATWG-fetch-shaped carrier (web shell bridges it to node:http; host-side endpoint of an IPC bridge). */
  29. handler: { fetch: typeof fetch }
  30. /** Host-level default routing (describe and every shell share this single source). */
  31. defaults: HostDefaults
  32. /**
  33. * Root context — a formal seam, not an escape hatch: (1) the mount point for
  34. * protocol front-door plugins (`dsh acp` = startHost() → ctx.plugin(uiAcp, config));
  35. * (2) headless session-event subscription. Discipline: consuming clients must
  36. * not bypass `api` through ctx; shells must not ctx.plugin to alter the
  37. * assembly (mounting a front door is the shell's own shape, not an assembly change).
  38. */
  39. ctx: Context
  40. /** Single shutdown exit (ctx.fiber.dispose()). Idempotent: a second call returns the same promise. */
  41. dispose(): Promise<void>
  42. }
  43. /**
  44. * Boot the host and assemble its consumption surfaces in one step.
  45. * @param options - boot passthrough (see StartHostOptions).
  46. * @returns the running host handle shared by every shell shape.
  47. */
  48. export async function startHost(options: StartHostOptions): Promise<RunningHost> {
  49. const host = await bootHost(options.boot)
  50. const api = createApiProxy(host.ctx, host.defaults)
  51. const handler = toFetchHandler(api)
  52. let disposing: Promise<void> | undefined
  53. return { api, handler, defaults: host.defaults, ctx: host.ctx, dispose: () => (disposing ??= host.dispose()) }
  54. }