index.ts 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. /** Read-only projection of the current Cordis Loader plugin entries. */
  2. import type { Context, FiberState } from '@deepseek-ai/cordis'
  3. import type {} from '@deepseek-ai/cordis-plugin-loader'
  4. // Type-only: the optional agent-preset roster resolved through `ctx.get`.
  5. import type {} from '@deepseek-ai/dsh-agent-presets'
  6. import { TypertRemoteService, Remote } from '@deepseek-ai/dsh-typert-protocol'
  7. // Typert-generated ./typert and ./remote artifacts import Zod at runtime.
  8. import type {} from 'zod'
  9. import type {
  10. AgentPresetPluginGroup,
  11. PluginEntryId,
  12. PluginFiberPhase,
  13. PluginInventoryEntry,
  14. PluginInventorySnapshot,
  15. } from './types.ts'
  16. export type * from './types.ts'
  17. /** Brand an existing Loader-tree entry id at the owning boundary. */
  18. function pluginEntryId(value: string): PluginEntryId {
  19. return value as PluginEntryId
  20. }
  21. /** Runtime mirror: FiberState is a cross-package const enum. */
  22. const FIBER_STATE = {
  23. PENDING: 0 as FiberState.PENDING,
  24. LOADING: 1 as FiberState.LOADING,
  25. ACTIVE: 2 as FiberState.ACTIVE,
  26. FAILED: 3 as FiberState.FAILED,
  27. DISPOSED: 4 as FiberState.DISPOSED,
  28. UNLOADING: 5 as FiberState.UNLOADING,
  29. } as const
  30. /** Complete public projection of Cordis Fiber states. */
  31. const FIBER_PHASE = {
  32. [FIBER_STATE.PENDING]: 'pending',
  33. [FIBER_STATE.LOADING]: 'loading',
  34. [FIBER_STATE.ACTIVE]: 'active',
  35. [FIBER_STATE.FAILED]: 'failed',
  36. [FIBER_STATE.DISPOSED]: null,
  37. [FIBER_STATE.UNLOADING]: 'unloading',
  38. } as const satisfies Record<FiberState, PluginFiberPhase>
  39. /** Remote-only service exposing the Loader's current non-group entry state. */
  40. export class PluginInventoryGateway extends TypertRemoteService {
  41. static inject = ['loader']
  42. constructor(ctx: Context) {
  43. super(ctx, 'pluginInventory')
  44. }
  45. /**
  46. * Read the Loader directly on every call. Cordis's internal plugin/status
  47. * events already maintain Entry.fiber and Fiber.state, so a second cache
  48. * would only add another lifecycle truth to keep synchronized.
  49. *
  50. * When an agent-preset roster is composed, the snapshot also carries each
  51. * preset's composition rows, because those rows — not the Loader's own
  52. * entries — are where a deployment that mounts the roster runs its
  53. * model-facing plugins.
  54. * @returns Current non-group Loader entries in Loader order, with per-preset
  55. * compositions when a roster is composed.
  56. */
  57. @Remote('list')
  58. async list(): Promise<PluginInventorySnapshot> {
  59. const entries: PluginInventoryEntry[] = []
  60. for (const entry of this.ctx.loader.entries()) {
  61. if (entry.options.group) continue
  62. entries.push({
  63. entryId: pluginEntryId(entry.id),
  64. moduleName: entry.options.name,
  65. enabled: !entry.disabled,
  66. fiberPhase: entry.fiber === undefined ? null : FIBER_PHASE[entry.fiber.state],
  67. })
  68. }
  69. const presets = this.ctx.get('agentPresets')
  70. if (presets === undefined) return { entries }
  71. const agentPresets: AgentPresetPluginGroup[] = (await presets.compositionInventory()).map(
  72. composition => ({
  73. ...composition,
  74. rows: composition.rows.map(({ fiberState, ...row }) => ({
  75. ...row,
  76. fiberPhase: fiberState === undefined ? null : FIBER_PHASE[fiberState],
  77. })),
  78. }),
  79. )
  80. return { entries, agentPresets }
  81. }
  82. }
  83. export default PluginInventoryGateway