* Build the static component files from block and tool registries. * This only needs to happen once per process. * * Integration paths are derived deterministically from the block registry's * `tools.access` arrays rather than splitting tool IDs on underscores. * Each block declares which tools
()
| 159 | * suffix) becomes the service directory name. |
| 160 | */ |
| 161 | function getStaticComponentFiles(): Map<string, string> { |
| 162 | if (staticComponentFiles) return staticComponentFiles |
| 163 | |
| 164 | const files = new Map<string, string>() |
| 165 | |
| 166 | const allBlocks = getAllBlocks() |
| 167 | const visibleBlocks = allBlocks.filter((b) => !b.hideFromToolbar) |
| 168 | |
| 169 | let blocksFiltered = 0 |
| 170 | for (const block of visibleBlocks) { |
| 171 | const path = `components/blocks/${block.type}.json` |
| 172 | files.set(path, serializeBlockSchema(block)) |
| 173 | } |
| 174 | blocksFiltered = allBlocks.length - visibleBlocks.length |
| 175 | |
| 176 | let integrationCount = 0 |
| 177 | |
| 178 | const oauthServices = new Map<string, { provider: string; operations: string[] }>() |
| 179 | const apiKeyServices = new Map<string, { params: string[]; operations: string[] }>() |
| 180 | |
| 181 | // Integration tools come from the shared exposed-tool set (latest version of |
| 182 | // each operation owned by a visible block), the same set used to build the |
| 183 | // deferred callable tools — so discovery and execution can never drift. |
| 184 | for (const { config: tool, service, operation } of getExposedIntegrationTools()) { |
| 185 | const path = `components/integrations/${service}/${operation}.json` |
| 186 | files.set(path, serializeIntegrationSchema(tool)) |
| 187 | integrationCount++ |
| 188 | |
| 189 | if (tool.oauth?.required) { |
| 190 | const existing = oauthServices.get(service) |
| 191 | if (existing) { |
| 192 | existing.operations.push(operation) |
| 193 | } else { |
| 194 | oauthServices.set(service, { provider: tool.oauth.provider, operations: [operation] }) |
| 195 | } |
| 196 | } else if (tool.hosting?.apiKeyParam) { |
| 197 | const existing = apiKeyServices.get(service) |
| 198 | if (existing) { |
| 199 | if (!existing.params.includes(tool.hosting.apiKeyParam)) { |
| 200 | existing.params.push(tool.hosting.apiKeyParam) |
| 201 | } |
| 202 | existing.operations.push(operation) |
| 203 | } else { |
| 204 | apiKeyServices.set(service, { |
| 205 | params: [tool.hosting.apiKeyParam], |
| 206 | operations: [operation], |
| 207 | }) |
| 208 | } |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | files.set( |
| 213 | 'environment/oauth-integrations.json', |
| 214 | JSON.stringify(Object.fromEntries(oauthServices), null, 2) |
| 215 | ) |
| 216 | files.set( |
| 217 | 'environment/api-key-integrations.json', |
| 218 | JSON.stringify(Object.fromEntries(apiKeyServices), null, 2) |
no test coverage detected