()
| 35 | * both shapes come from a single R2 read. Throws if the spec has not been seeded. |
| 36 | */ |
| 37 | export async function getSpec(): Promise<{ text: string; paths: SpecPaths }> { |
| 38 | const now = Date.now() |
| 39 | if (fresh(specEntry, now)) return specEntry.value |
| 40 | |
| 41 | const obj = await env.SPEC_BUCKET.get('spec.json') |
| 42 | if (!obj) throw new Error('spec.json not found in R2. Run the scheduled handler to populate it.') |
| 43 | const text = await obj.text() |
| 44 | const paths = (JSON.parse(text) as { paths: SpecPaths }).paths |
| 45 | |
| 46 | const value = { text, paths } |
| 47 | specEntry = { value, expiresAt: now + TTL_MS } |
| 48 | return value |
| 49 | } |
| 50 | |
| 51 | /** |
| 52 | * Protocol-ready non-Code-Mode tools/list artifact. Falls back to deriving it |
no test coverage detected