{"version":3,"file":"platform-sdk.mjs","names":[],"sources":["../../src/llm-export.ts","../../src/bin/platform-sdk.ts"],"sourcesContent":["// Consumer-facing agent-docs injector — the SDK-bin home of\n// `llm-export`. Writes a managed block into <cwd>/AGENTS.md that POINTS\n// at this package's shipped `.agent/` corpus (predictable npm path), and\n// ensures <cwd>/CLAUDE.md imports AGENTS.md.\n//\n// This writer is DUPLICATED in the CLI (packages/cli/src/cli/llm-export.ts)\n// for the brew `alvera llm-export` path. The two copies must emit a\n// byte-identical block — `buildManagedBlock()` below is kept verbatim in\n// both, and each package snapshot-tests its own output (architecture-\n// decisions §2.10.13). They are not shared via import: the CLI's tsconfig\n// rootDir forbids importing SDK source, a public SDK export was rejected\n// to keep the surface lean (CLAUDE.md #6), and the sharing would be\n// throwaway under a future Go CLI anyway.\n//\n// Unlike the CLI copy, this one is a LIBRARY module: it throws on damaged\n// markers rather than calling process.exit — the bin entry catches and\n// exits.\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nconst MARKER_BEGIN = '<!-- @alvera-ai/platform-sdk:BEGIN -->'\nconst MARKER_END = '<!-- @alvera-ai/platform-sdk:END -->'\nconst CLAUDE_IMPORT_LINE = '@AGENTS.md'\nconst SDK_PACKAGE_NAME = '@alvera-ai/platform-sdk'\n\nexport interface LlmExportResult {\n  agentsMdPath: string\n  claudeMdPath: string\n  managedBlockBytes: number\n  claudeImportWritten: boolean\n}\n\n// Pure + deterministic. MUST stay byte-identical to the CLI copy's\n// buildManagedBlock (§2.10.13 drift guard).\nexport function buildManagedBlock(): string {\n  const corpus = `node_modules/${SDK_PACKAGE_NAME}/.agent`\n  const lines: string[] = [\n    MARKER_BEGIN,\n    '',\n    '<!-- Managed by `alvera llm-export` — do not edit by hand. -->',\n    '<!-- Re-running the command replaces only this block. -->',\n    '',\n    `## \\`${SDK_PACKAGE_NAME}\\` corpus`,\n    '',\n    'The SDK ships an agent-docs corpus next to its npm install. Read the',\n    'corpus index first (Claude Code inlines this import):',\n    '',\n    `@${corpus}/AGENTS.md`,\n    '',\n    `Cookbook recipes live under \\`${corpus}/cookbook/\\`, indexed below —`,\n    'each recipe is anchored to a green end-to-end test and carries the',\n    'verified call sequence for one scenario. **Cookbook-first:** before',\n    'composing API calls for a scenario from primitives, start from the',\n    'closest recipe and diverge only with a stated reason.',\n    '',\n    '### The four cookbooks',\n    '',\n    'One per use case. Each stands up its own tenant, datalake and',\n    'resources and shares nothing with the others — read one end to end',\n    'and you need no second file. Duplication between them is deliberate.',\n    '',\n    `- \\`${corpus}/cookbook/organic-marketing.md\\` — score inbound leads`,\n    '  into four bands with an LLM agent, schedule a birthday greeting a',\n    '  year out with a pure-Liquid trigger, and run an A/B campaign whose',\n    '  suppression gates and split live in the workflow. Ends with',\n    '  delivery reconciliation and a natural-language read of the lake.',\n    '  One raw datalake.',\n    `- \\`${corpus}/cookbook/payments-compliance.md\\` — disambiguate`,\n    '  gray-zone sanctions screenings with an LLM agent that never sees',\n    '  who was screened, and fire a KYC notification on account',\n    '  activation. Ends by reading one row through raw, tokenized and',\n    '  redacted to show what each reader gets. Three datalakes.',\n    `- \\`${corpus}/cookbook/subscription-saas.md\\` — one customers table`,\n    '  and three workflows over it: a welcome SMS gated on reachability,',\n    '  a dunning reminder gated on reachability AND KYC, and an LLM agent',\n    '  banding accounts by priority. Then the same table filled three',\n    '  more ways — inline, bulk CSV, REST fetch — and a teammate invited',\n    '  onto the tenant. One raw datalake.',\n    `- \\`${corpus}/cookbook/primary-care.md\\` — a review request that only`,\n    '  reaches patients whose visit actually happened, an LLM agent',\n    '  triaging inbound messages off the tokenized projection, and a',\n    '  vision agent reading a scanned document. Three datalakes.',\n    '',\n    '**Capabilities are inside the cookbooks, not beside them.** Bulk',\n    'upload, REST fetch, team invitations, action status updaters,',\n    'paginated pollers, text-to-SQL and agent file-invocation each live in',\n    'the cookbook whose use case motivates them, as numbered steps. There',\n    'is no separate per-capability file to consult.',\n    '',\n    '### Top-3 consumer gotchas',\n    '',\n    '1. **`update()` replaces the whole resource.** There is no partial',\n    `   update — resupply the full body. See \\`${corpus}/mutations.md\\`.`,\n    '2. **Paginated catalogs return `{ data, meta }`.** Catalog endpoints',\n    '   (`tools.list`, `dataSources.list`, etc.) wrap rows in `data` with a',\n    '   `meta` envelope — destructure, do not iterate the response directly.',\n    '3. **Resources are datalake-slug-scoped.** Every nested URL takes both',\n    '   `tenantSlug` and `datalakeSlug` — never a single slug. The datalake',\n    \"   slug comes from the server (`api.datalakes.create`'s response).\",\n    '',\n    MARKER_END,\n  ]\n  return lines.join('\\n')\n}\n\nexport function upsertManagedBlock(target: string, block: string): string {\n  const beginIdx = target.indexOf(MARKER_BEGIN)\n  const endIdx = target.indexOf(MARKER_END)\n\n  if (beginIdx === -1 && endIdx === -1) {\n    const tail =\n      target.length === 0\n        ? ''\n        : target.endsWith('\\n\\n')\n          ? ''\n          : target.endsWith('\\n')\n            ? '\\n'\n            : '\\n\\n'\n    return target + tail + block + '\\n'\n  }\n\n  if (beginIdx === -1 || endIdx === -1 || endIdx < beginIdx) {\n    throw new Error(\n      `AGENTS.md: managed-block markers are damaged ` +\n        `(BEGIN at ${beginIdx}, END at ${endIdx}). ` +\n        `Fix by hand or remove both markers, then re-run llm-export.`,\n    )\n  }\n\n  const before = target.slice(0, beginIdx)\n  const after = target.slice(endIdx + MARKER_END.length)\n  return before + block + after\n}\n\nexport function upsertClaudeImport(target: string): { next: string; written: boolean } {\n  const already = target.split('\\n').some((line) => line.trim() === CLAUDE_IMPORT_LINE)\n  if (already) return { next: target, written: false }\n  const tail =\n    target.length === 0\n      ? ''\n      : target.endsWith('\\n\\n')\n        ? ''\n        : target.endsWith('\\n')\n          ? '\\n'\n          : '\\n\\n'\n  return { next: target + tail + CLAUDE_IMPORT_LINE + '\\n', written: true }\n}\n\nfunction readOrEmpty(path: string): string {\n  return existsSync(path) ? readFileSync(path, 'utf8') : ''\n}\n\nexport function runLlmExport(cwd: string = process.cwd()): LlmExportResult {\n  const block = buildManagedBlock()\n\n  const agentsMdPath = join(cwd, 'AGENTS.md')\n  writeFileSync(agentsMdPath, upsertManagedBlock(readOrEmpty(agentsMdPath), block), 'utf8')\n\n  const claudeMdPath = join(cwd, 'CLAUDE.md')\n  const { next: claudeAfter, written: claudeImportWritten } = upsertClaudeImport(\n    readOrEmpty(claudeMdPath),\n  )\n  writeFileSync(claudeMdPath, claudeAfter, 'utf8')\n\n  return {\n    agentsMdPath,\n    claudeMdPath,\n    managedBlockBytes: block.length,\n    claudeImportWritten,\n  }\n}\n","#!/usr/bin/env node\n// The SDK's single bin (Stripe multi-subcommand pattern). Today the only\n// subcommand is `llm-export`, the consumer-facing agent-docs injector —\n// so an npm-only consumer runs `npx @alvera-ai/platform-sdk llm-export`\n// with no brew CLI install (architecture-decisions §2.10.13). The bin is\n// the ONLY thing reversed from inv #21; the SDK stays sub-export-less and\n// declares no postinstall.\nimport { runLlmExport } from '../llm-export.js'\n\nfunction usage(): void {\n  process.stderr.write(\n    'Usage: npx @alvera-ai/platform-sdk <command>\\n\\n' +\n      'Commands:\\n' +\n      '  llm-export    Write a managed AGENTS.md block pointing at the\\n' +\n      '                SDK agent-docs corpus in node_modules; ensure\\n' +\n      '                CLAUDE.md imports AGENTS.md. Idempotent.\\n',\n  )\n}\n\nfunction main(): void {\n  const command = process.argv[2]\n\n  if (command === undefined || command === '--help' || command === '-h') {\n    usage()\n    process.exit(command === undefined ? 1 : 0)\n  }\n\n  if (command === 'llm-export') {\n    try {\n      const result = runLlmExport(process.cwd())\n      process.stderr.write(\n        `✓ ${result.agentsMdPath} (${result.managedBlockBytes} bytes in managed block)\\n`,\n      )\n      process.stderr.write(\n        `✓ ${result.claudeMdPath}` +\n          `${result.claudeImportWritten ? ' (added @AGENTS.md import)' : ' (@AGENTS.md import already present)'}\\n`,\n      )\n      return\n    } catch (err) {\n      process.stderr.write(`llm-export failed: ${err instanceof Error ? err.message : String(err)}\\n`)\n      process.exit(1)\n    }\n  }\n\n  process.stderr.write(`unknown command \"${command}\".\\n\\n`)\n  usage()\n  process.exit(1)\n}\n\nmain()\n"],"mappings":"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"}