{"version":3,"file":"mcpBundle.mjs","names":[],"sources":["../../../src/builders/server/mcpBundle.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport {\n  DOOM_MCP_BUNDLE_FILE,\n  DOOM_MCP_BUNDLE_VERSION,\n  type DoomMcpBundle,\n  type DoomMcpBundleEntry,\n  parseDoomMcpBundle,\n} from '@agimon-ai/doompi-core/mcp-facet';\nimport { writeFileAtomic } from '@agimon-ai/doompi-core/runtime-json';\nimport type { DoomServerBundleOwner } from '@agimon-ai/doompi-core/server-facet';\n\nimport { compileExtensionModule, extensionModuleManifestPath } from '../../compiler';\nimport type { ExtensionComposition } from '../cli/extensionAssembler';\n\ninterface DeclaredMcpPlugin {\n  readonly packageName: string;\n  readonly packageDir: string;\n  readonly entry: string;\n  readonly dist: string;\n}\n\nexport interface McpBundleSyncInput {\n  readonly generation: string;\n  readonly fingerprint: string;\n  readonly repositoryRoot: string;\n  readonly compositions: readonly ExtensionComposition[];\n  readonly outputDirectory: string;\n  readonly cacheDirectory: string;\n  readonly sharedCacheDirectory?: string;\n}\n\nexport interface McpBundleSyncResult {\n  readonly descriptor: DoomMcpBundle;\n  readonly compilerManifests: Record<string, string>;\n}\n\nfunction packageRootOf(entry: string): string {\n  let directory = path.dirname(fs.realpathSync(entry));\n  for (;;) {\n    if (fs.existsSync(path.join(directory, 'package.json'))) return directory;\n    const parent = path.dirname(directory);\n    if (parent === directory) throw new Error(`No package manifest owns ${entry}`);\n    directory = parent;\n  }\n}\n\nfunction packagePath(packageDir: string, field: string, value: unknown): string {\n  if (typeof value !== 'string' || !value.startsWith('./') || value.includes('..'))\n    throw new Error(`doompiMcp ${field} in ${packageDir} must be a package-relative ./path with no '..'.`);\n  return value;\n}\n\nfunction declaredMcpPlugin(packageDir: string, manifest: Record<string, unknown>): DeclaredMcpPlugin | undefined {\n  const value = manifest.doompiMcp;\n  if (value === undefined) return undefined;\n  if (typeof value !== 'object' || value === null || Array.isArray(value))\n    throw new Error(`doompiMcp manifest in ${packageDir} must be an object.`);\n  const declaration = value as Record<string, unknown>;\n  if (!Array.isArray(declaration.scopes) || declaration.scopes.length !== 1 || declaration.scopes[0] !== 'session')\n    throw new Error(`doompiMcp scopes in ${packageDir} must be exactly ['session'].`);\n  const packageName = manifest.name;\n  if (typeof packageName !== 'string' || packageName === '') throw new Error(`Package in ${packageDir} has no name.`);\n  return {\n    packageName,\n    packageDir,\n    entry: packagePath(packageDir, 'entry', declaration.entry),\n    dist: packagePath(packageDir, 'dist', declaration.dist),\n  };\n}\n\n/** Stages explicit package MCP entries into a generation-owned descriptor. */\nexport async function syncMcpBundle(input: McpBundleSyncInput): Promise<McpBundleSyncResult> {\n  const descriptorPath = path.join(input.outputDirectory, DOOM_MCP_BUNDLE_FILE);\n  if (fs.existsSync(descriptorPath)) throw new Error('An MCP bundle generation cannot be overwritten');\n  const packages = new Map<string, { declaration: DeclaredMcpPlugin; owners: DoomServerBundleOwner[] }>();\n  for (const composition of input.compositions) {\n    const majorMode = composition.majorMode?.name;\n    if (!majorMode) throw new Error('An MCP bundle composition needs its major mode');\n    for (const root of new Set(composition.parentActivation.map(packageRootOf))) {\n      const manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as Record<string, unknown>;\n      const declaration = declaredMcpPlugin(root, manifest);\n      if (declaration === undefined) continue;\n      const previous = packages.get(declaration.packageName);\n      if (previous && previous.declaration.packageDir !== root)\n        throw new Error(`Ambiguous MCP bundle package: ${declaration.packageName}`);\n      const candidate = previous ?? { declaration, owners: [] };\n      const layers = composition.selections\n        .filter(\n          (selection) => selection.outcome === 'resolved' && selection.path && packageRootOf(selection.path) === root,\n        )\n        .map((selection) => selection.layer);\n      for (const layer of new Set(layers.length > 0 ? layers : ['default'])) {\n        if (!candidate.owners.some((owner) => owner.majorMode === majorMode && owner.layer === layer))\n          candidate.owners.push({ majorMode, layer });\n      }\n      packages.set(declaration.packageName, candidate);\n    }\n  }\n  fs.mkdirSync(input.outputDirectory, { recursive: true });\n  const entries: DoomMcpBundleEntry[] = [];\n  const compilerManifests: Record<string, string> = {};\n  for (const [index, { declaration, owners }] of [...packages.values()].entries()) {\n    const builtEntry = fs.realpathSync(path.resolve(declaration.packageDir, declaration.dist));\n    const relative = path.relative(declaration.packageDir, builtEntry);\n    if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))\n      throw new Error(`MCP entry escapes package: ${declaration.packageName}`);\n    const compileOptions = {\n      repositoryRoot: input.repositoryRoot,\n      sharedCacheDirectory: input.sharedCacheDirectory,\n      outputDirectory: path.join(input.outputDirectory, 'modules', String(index)),\n      outputName: 'plugin',\n    };\n    const module = await compileExtensionModule(builtEntry, input.cacheDirectory, compileOptions);\n    compilerManifests[declaration.packageName] = extensionModuleManifestPath(\n      builtEntry,\n      input.cacheDirectory,\n      compileOptions,\n    );\n    entries.push({\n      packageName: declaration.packageName,\n      entry: declaration.entry,\n      module: `./${path.relative(input.outputDirectory, module).split(path.sep).join('/')}`,\n      sha256: crypto.createHash('sha256').update(fs.readFileSync(module)).digest('hex'),\n      owners,\n    });\n  }\n  const descriptor = parseDoomMcpBundle({\n    version: DOOM_MCP_BUNDLE_VERSION,\n    generation: input.generation,\n    fingerprint: input.fingerprint,\n    entries,\n  });\n  writeFileAtomic(descriptorPath, `${JSON.stringify(descriptor, null, 2)}\\n`);\n  return { descriptor, compilerManifests };\n}\n"],"mappings":"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"}