@alvera-ai/platform-sdk 0.10.0-rc.16 → 0.10.0-rc.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.agent/AGENTS.md CHANGED
@@ -6,6 +6,54 @@ filesystem walk. Consumers include downstream agents writing
6
6
  code in CLIs, React apps, integration test suites, and code
7
7
  generators.
8
8
 
9
+ > **Wiring this into your project:** run `npx @alvera-ai/platform-sdk
10
+ > llm-export` from your project root to add a pointer to this corpus in
11
+ > your `AGENTS.md` (and a `@AGENTS.md` import in `CLAUDE.md`). Idempotent.
12
+
13
+ ## ⚠ This file is a LOOKUP ONLY. Read the per-kind MD before emitting code.
14
+
15
+ `AGENTS.md` (this file) is a **DIRECTORY**. It points you to
16
+ per-kind MDs (`tools.md`, `workflows.md`, `data_sources.md`,
17
+ etc.). It is **NOT** a schema reference, and you **CANNOT**
18
+ ground any wire-level claim from this file alone.
19
+
20
+ ### Mandatory protocol before emitting any code that touches a kind
21
+
22
+ 1. **Locate the kind** in the Resources section below.
23
+ 2. **Open the kind's MD** (`<kind>.md`).
24
+ 3. **Read §1 (Wire shape) and §2 (Rules the type cannot encode)**
25
+ at minimum.
26
+ 4. **Every claim you emit** about wire shape, polymorphism,
27
+ constraints, enum values, or required fields **MUST be
28
+ grounded in §1 or §2 of the kind's MD**. If you cannot
29
+ point at the section, re-read before emitting.
30
+
31
+ ### Failure mode this prevents
32
+
33
+ The one-line summaries in the Resources section are
34
+ **navigation labels**, not schemas. Example:
35
+
36
+ > The index line `workflows.md — Filter + decision + action;
37
+ > standard + agent-driven variants` **cannot** tell you whether
38
+ > `variant` is a body field. It is not. `workflows.md` §1 shows
39
+ > the variant is **structural** — via the presence of the
40
+ > `ai_agents[]` array on the request body — **not** a top-level
41
+ > discriminator.
42
+
43
+ An agent that paraphrases the index into a wire-level claim is
44
+ **hallucinating**. The index summary is too compressed to encode
45
+ polymorphism, constraints, enum values, or required fields.
46
+
47
+ ### Rule of thumb
48
+
49
+ Treat index summaries like file names in a directory listing:
50
+ useful for finding the right file, useless as a substitute for
51
+ opening it. If you find yourself emitting a claim about a kind's
52
+ wire shape without having opened that kind's MD in the current
53
+ session, **stop and open it now**.
54
+
55
+ ---
56
+
9
57
  ## If you know MCP — vocabulary mapping
10
58
 
11
59
  The platform's resources don't implement MCP wire format, but
@@ -369,7 +417,7 @@ of restating:
369
417
  In your consumer repo, run:
370
418
 
371
419
  ```
372
- npx @alvera-ai/platform-sdk agent-init
420
+ npx @alvera-ai/platform-sdk llm-export
373
421
  ```
374
422
 
375
423
  This writes a managed block into your `AGENTS.md` pointing back
@@ -250,8 +250,8 @@ created tenant / datalake / user — each run mints fresh names via
250
250
  `runSuffix` so reruns do not collide, and the seeded local DB is
251
251
  cheap to reset (`mix ecto.reset` on the platform). When the
252
252
  validator graduates to continuous integration at Milestone 18
253
- (running against the offline Prism contract mock), no real state
254
- is created at all.
253
+ (running against the mockoon-backed mock env at `ALVERA_ENV=mock`),
254
+ no real state is created at all.
255
255
 
256
256
  # Outcome
257
257
 
@@ -246,8 +246,8 @@ created tenant / datalake / user — each run mints fresh names via
246
246
  `runSuffix` so reruns do not collide, and the seeded local DB is
247
247
  cheap to reset (`mix ecto.reset` on the platform). When the
248
248
  validator graduates to continuous integration at Milestone 18
249
- (running against the offline Prism contract mock), no real state
250
- is created at all.
249
+ (running against the mockoon-backed mock env at `ALVERA_ENV=mock`),
250
+ no real state is created at all.
251
251
 
252
252
  # Outcome
253
253
 
@@ -247,8 +247,8 @@ created tenant / datalake / user — each run mints fresh names via
247
247
  `runSuffix` so reruns do not collide, and the seeded local DB is
248
248
  cheap to reset (`mix ecto.reset` on the platform). When the
249
249
  validator graduates to continuous integration at Milestone 18
250
- (running against the offline Prism contract mock), no real state
251
- is created at all.
250
+ (running against the mockoon-backed mock env at `ALVERA_ENV=mock`),
251
+ no real state is created at all.
252
252
 
253
253
  # Outcome
254
254
 
@@ -251,8 +251,8 @@ created tenant / datalake / user — each run mints fresh names via
251
251
  `runSuffix` so reruns do not collide, and the seeded local DB is
252
252
  cheap to reset (`mix ecto.reset` on the platform). When the
253
253
  validator graduates to continuous integration at Milestone 18
254
- (running against the offline Prism contract mock), no real state
255
- is created at all.
254
+ (running against the mockoon-backed mock env at `ALVERA_ENV=mock`),
255
+ no real state is created at all.
256
256
 
257
257
  # Outcome
258
258
 
package/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # `@alvera-ai/platform-sdk`
2
+
3
+ Typed SDK for the Alvera platform API — manage datalakes, data sources,
4
+ tools, AI agents, action status updaters, and more.
5
+
6
+ ## Install
7
+
8
+ ```sh
9
+ npm install @alvera-ai/platform-sdk
10
+ # or: pnpm add / bun add / yarn add
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ import { AlveraClient } from '@alvera-ai/platform-sdk'
17
+
18
+ const alvera = new AlveraClient({ baseUrl, sessionToken })
19
+ const { data } = await alvera.datalakes.list(tenantSlug)
20
+ ```
21
+
22
+ ## Agent setup
23
+
24
+ This package ships an agent-docs corpus under `.agent/` (it lands at
25
+ `node_modules/@alvera-ai/platform-sdk/.agent/` after install). Coding
26
+ agents that walk `node_modules` discover it automatically.
27
+
28
+ To wire a pointer into your project's `AGENTS.md` (and a `@AGENTS.md`
29
+ import into `CLAUDE.md`), run — no global install needed:
30
+
31
+ ```sh
32
+ npx @alvera-ai/platform-sdk llm-export
33
+ ```
34
+
35
+ It writes an idempotent managed block; re-running replaces only that
36
+ block. Start from the corpus index: `.agent/AGENTS.md`.
37
+
38
+ ## License
39
+
40
+ Elastic-2.0
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+
5
+ //#region src/llm-export.ts
6
+ const MARKER_BEGIN = "<!-- @alvera-ai/platform-sdk:BEGIN -->";
7
+ const MARKER_END = "<!-- @alvera-ai/platform-sdk:END -->";
8
+ const CLAUDE_IMPORT_LINE = "@AGENTS.md";
9
+ const SDK_PACKAGE_NAME = "@alvera-ai/platform-sdk";
10
+ function buildManagedBlock() {
11
+ const corpus = `node_modules/${SDK_PACKAGE_NAME}/.agent`;
12
+ return [
13
+ MARKER_BEGIN,
14
+ "",
15
+ "<!-- Managed by `alvera llm-export` — do not edit by hand. -->",
16
+ "<!-- Re-running the command replaces only this block. -->",
17
+ "",
18
+ `## \`${SDK_PACKAGE_NAME}\` corpus`,
19
+ "",
20
+ "The SDK ships an agent-docs corpus next to its npm install. Read the",
21
+ "corpus index first (Claude Code inlines this import):",
22
+ "",
23
+ `@${corpus}/AGENTS.md`,
24
+ "",
25
+ `Cookbook recipes live under \`${corpus}/cookbook/\`.`,
26
+ "",
27
+ "### Top-3 consumer gotchas",
28
+ "",
29
+ "1. **Updates use PUT, not PATCH.** The SDK exposes no PATCH method;",
30
+ ` mutations replace the resource whole. See \`${corpus}/mutations.md\`.`,
31
+ "2. **Paginated catalogs return `{ data, meta }`.** Catalog endpoints",
32
+ " (`tools.list`, `dataSources.list`, etc.) wrap rows in `data` with a",
33
+ " `meta` envelope — destructure, do not iterate the response directly.",
34
+ "3. **Resources are datalake-slug-scoped.** Every nested URL takes both",
35
+ " `tenantSlug` and `datalakeSlug` — never a single slug. The datalake",
36
+ " slug comes from the server (`api.datalakes.create`'s response).",
37
+ "",
38
+ MARKER_END
39
+ ].join("\n");
40
+ }
41
+ function upsertManagedBlock(target, block) {
42
+ const beginIdx = target.indexOf(MARKER_BEGIN);
43
+ const endIdx = target.indexOf(MARKER_END);
44
+ if (beginIdx === -1 && endIdx === -1) return target + (target.length === 0 ? "" : target.endsWith("\n\n") ? "" : target.endsWith("\n") ? "\n" : "\n\n") + block + "\n";
45
+ if (beginIdx === -1 || endIdx === -1 || endIdx < beginIdx) throw new Error(`AGENTS.md: managed-block markers are damaged (BEGIN at ${beginIdx}, END at ${endIdx}). Fix by hand or remove both markers, then re-run llm-export.`);
46
+ const before = target.slice(0, beginIdx);
47
+ const after = target.slice(endIdx + 36);
48
+ return before + block + after;
49
+ }
50
+ function upsertClaudeImport(target) {
51
+ if (target.split("\n").some((line) => line.trim() === CLAUDE_IMPORT_LINE)) return {
52
+ next: target,
53
+ written: false
54
+ };
55
+ return {
56
+ next: target + (target.length === 0 ? "" : target.endsWith("\n\n") ? "" : target.endsWith("\n") ? "\n" : "\n\n") + CLAUDE_IMPORT_LINE + "\n",
57
+ written: true
58
+ };
59
+ }
60
+ function readOrEmpty(path) {
61
+ return existsSync(path) ? readFileSync(path, "utf8") : "";
62
+ }
63
+ function runLlmExport(cwd = process.cwd()) {
64
+ const block = buildManagedBlock();
65
+ const agentsMdPath = join(cwd, "AGENTS.md");
66
+ writeFileSync(agentsMdPath, upsertManagedBlock(readOrEmpty(agentsMdPath), block), "utf8");
67
+ const claudeMdPath = join(cwd, "CLAUDE.md");
68
+ const { next: claudeAfter, written: claudeImportWritten } = upsertClaudeImport(readOrEmpty(claudeMdPath));
69
+ writeFileSync(claudeMdPath, claudeAfter, "utf8");
70
+ return {
71
+ agentsMdPath,
72
+ claudeMdPath,
73
+ managedBlockBytes: block.length,
74
+ claudeImportWritten
75
+ };
76
+ }
77
+
78
+ //#endregion
79
+ //#region src/bin/platform-sdk.ts
80
+ function usage() {
81
+ process.stderr.write("Usage: npx @alvera-ai/platform-sdk <command>\n\nCommands:\n llm-export Write a managed AGENTS.md block pointing at the\n SDK agent-docs corpus in node_modules; ensure\n CLAUDE.md imports AGENTS.md. Idempotent.\n");
82
+ }
83
+ function main() {
84
+ const command = process.argv[2];
85
+ if (command === void 0 || command === "--help" || command === "-h") {
86
+ usage();
87
+ process.exit(command === void 0 ? 1 : 0);
88
+ }
89
+ if (command === "llm-export") try {
90
+ const result = runLlmExport(process.cwd());
91
+ process.stderr.write(`✓ ${result.agentsMdPath} (${result.managedBlockBytes} bytes in managed block)\n`);
92
+ process.stderr.write(`✓ ${result.claudeMdPath}${result.claudeImportWritten ? " (added @AGENTS.md import)" : " (@AGENTS.md import already present)"}\n`);
93
+ return;
94
+ } catch (err) {
95
+ process.stderr.write(`llm-export failed: ${err instanceof Error ? err.message : String(err)}\n`);
96
+ process.exit(1);
97
+ }
98
+ process.stderr.write(`unknown command "${command}".\n\n`);
99
+ usage();
100
+ process.exit(1);
101
+ }
102
+ main();
103
+
104
+ //#endregion
105
+ export { };
106
+ //# sourceMappingURL=platform-sdk.mjs.map
@@ -0,0 +1 @@
1
+ {"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/\\`.`,\n '',\n '### Top-3 consumer gotchas',\n '',\n '1. **Updates use PUT, not PATCH.** The SDK exposes no PATCH method;',\n ` mutations replace the resource whole. 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":";;;;;AAoBA,MAAM,eAAe;AACrB,MAAM,aAAa;AACnB,MAAM,qBAAqB;AAC3B,MAAM,mBAAmB;AAWzB,SAAgB,oBAA4B;CAC1C,MAAM,SAAS,gBAAgB,iBAAiB;AA6BhD,QA5BwB;EACtB;EACA;EACA;EACA;EACA;EACA,QAAQ,iBAAiB;EACzB;EACA;EACA;EACA;EACA,IAAI,OAAO;EACX;EACA,iCAAiC,OAAO;EACxC;EACA;EACA;EACA;EACA,kDAAkD,OAAO;EACzD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACY,KAAK,KAAK;;AAGzB,SAAgB,mBAAmB,QAAgB,OAAuB;CACxE,MAAM,WAAW,OAAO,QAAQ,aAAa;CAC7C,MAAM,SAAS,OAAO,QAAQ,WAAW;AAEzC,KAAI,aAAa,MAAM,WAAW,GAShC,QAAO,UAPL,OAAO,WAAW,IACd,KACA,OAAO,SAAS,OAAO,GACrB,KACA,OAAO,SAAS,KAAK,GACnB,OACA,UACa,QAAQ;AAGjC,KAAI,aAAa,MAAM,WAAW,MAAM,SAAS,SAC/C,OAAM,IAAI,MACR,0DACe,SAAS,WAAW,OAAO,gEAE3C;CAGH,MAAM,SAAS,OAAO,MAAM,GAAG,SAAS;CACxC,MAAM,QAAQ,OAAO,MAAM,SAAS,GAAkB;AACtD,QAAO,SAAS,QAAQ;;AAG1B,SAAgB,mBAAmB,QAAoD;AAErF,KADgB,OAAO,MAAM,KAAK,CAAC,MAAM,SAAS,KAAK,MAAM,KAAK,mBAAmB,CACxE,QAAO;EAAE,MAAM;EAAQ,SAAS;EAAO;AASpD,QAAO;EAAE,MAAM,UAPb,OAAO,WAAW,IACd,KACA,OAAO,SAAS,OAAO,GACrB,KACA,OAAO,SAAS,KAAK,GACnB,OACA,UACqB,qBAAqB;EAAM,SAAS;EAAM;;AAG3E,SAAS,YAAY,MAAsB;AACzC,QAAO,WAAW,KAAK,GAAG,aAAa,MAAM,OAAO,GAAG;;AAGzD,SAAgB,aAAa,MAAc,QAAQ,KAAK,EAAmB;CACzE,MAAM,QAAQ,mBAAmB;CAEjC,MAAM,eAAe,KAAK,KAAK,YAAY;AAC3C,eAAc,cAAc,mBAAmB,YAAY,aAAa,EAAE,MAAM,EAAE,OAAO;CAEzF,MAAM,eAAe,KAAK,KAAK,YAAY;CAC3C,MAAM,EAAE,MAAM,aAAa,SAAS,wBAAwB,mBAC1D,YAAY,aAAa,CAC1B;AACD,eAAc,cAAc,aAAa,OAAO;AAEhD,QAAO;EACL;EACA;EACA,mBAAmB,MAAM;EACzB;EACD;;;;;AC1HH,SAAS,QAAc;AACrB,SAAQ,OAAO,MACb,wPAKD;;AAGH,SAAS,OAAa;CACpB,MAAM,UAAU,QAAQ,KAAK;AAE7B,KAAI,YAAY,UAAa,YAAY,YAAY,YAAY,MAAM;AACrE,SAAO;AACP,UAAQ,KAAK,YAAY,SAAY,IAAI,EAAE;;AAG7C,KAAI,YAAY,aACd,KAAI;EACF,MAAM,SAAS,aAAa,QAAQ,KAAK,CAAC;AAC1C,UAAQ,OAAO,MACb,KAAK,OAAO,aAAa,IAAI,OAAO,kBAAkB,4BACvD;AACD,UAAQ,OAAO,MACb,KAAK,OAAO,eACP,OAAO,sBAAsB,+BAA+B,uCAAuC,IACzG;AACD;UACO,KAAK;AACZ,UAAQ,OAAO,MAAM,sBAAsB,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC,IAAI;AAChG,UAAQ,KAAK,EAAE;;AAInB,SAAQ,OAAO,MAAM,oBAAoB,QAAQ,QAAQ;AACzD,QAAO;AACP,SAAQ,KAAK,EAAE;;AAGjB,MAAM"}