@open-mercato/ai-assistant 0.6.1-develop.3278.1.a4d26475e4 → 0.6.1-develop.3291.1.6fad645fd0

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.
Files changed (28) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +39 -14
  3. package/README.md +2 -1
  4. package/dist/modules/ai_assistant/lib/api-endpoint-index.js +0 -111
  5. package/dist/modules/ai_assistant/lib/api-endpoint-index.js.map +2 -2
  6. package/dist/modules/ai_assistant/lib/codemode-tools.js.map +2 -2
  7. package/dist/modules/ai_assistant/lib/http-server.js +0 -5
  8. package/dist/modules/ai_assistant/lib/http-server.js.map +2 -2
  9. package/dist/modules/ai_assistant/lib/mcp-dev-server.js +0 -5
  10. package/dist/modules/ai_assistant/lib/mcp-dev-server.js.map +2 -2
  11. package/dist/modules/ai_assistant/lib/mcp-server.js +0 -5
  12. package/dist/modules/ai_assistant/lib/mcp-server.js.map +2 -2
  13. package/package.json +4 -4
  14. package/src/modules/ai_assistant/lib/__tests__/mcp-startup-no-dead-index.test.ts +65 -0
  15. package/src/modules/ai_assistant/lib/api-endpoint-index.ts +5 -186
  16. package/src/modules/ai_assistant/lib/codemode-tools.ts +0 -2
  17. package/src/modules/ai_assistant/lib/http-server.ts +2 -9
  18. package/src/modules/ai_assistant/lib/mcp-dev-server.ts +2 -8
  19. package/src/modules/ai_assistant/lib/mcp-server.ts +1 -10
  20. package/dist/modules/ai_assistant/lib/api-discovery-tools.js +0 -170
  21. package/dist/modules/ai_assistant/lib/api-discovery-tools.js.map +0 -7
  22. package/dist/modules/ai_assistant/lib/api-endpoint-index-config.js +0 -177
  23. package/dist/modules/ai_assistant/lib/api-endpoint-index-config.js.map +0 -7
  24. package/dist/modules/ai_assistant/lib/entity-graph-tools.js +0 -127
  25. package/dist/modules/ai_assistant/lib/entity-graph-tools.js.map +0 -7
  26. package/src/modules/ai_assistant/lib/api-discovery-tools.ts +0 -250
  27. package/src/modules/ai_assistant/lib/api-endpoint-index-config.ts +0 -243
  28. package/src/modules/ai_assistant/lib/entity-graph-tools.ts +0 -192
@@ -1,2 +1,2 @@
1
- [build:ai-assistant] found 168 entry points
1
+ [build:ai-assistant] found 165 entry points
2
2
  [build:ai-assistant] built successfully
package/AGENTS.md CHANGED
@@ -466,9 +466,7 @@ packages/ai-assistant/
466
466
  │ │ │ ├── codemode-tools.ts # Code Mode search + execute tools
467
467
  │ │ │ ├── sandbox.ts # node:vm sandbox executor
468
468
  │ │ │ ├── truncate.ts # Response size limiter
469
- │ │ │ ├── api-endpoint-index.ts # OpenAPI endpoint indexing + raw spec cache
470
- │ │ │ ├── api-discovery-tools.ts # (legacy, unused) old find_api/call_api
471
- │ │ │ ├── entity-graph-tools.ts # (legacy, unused) old discover_schema
469
+ │ │ │ ├── api-endpoint-index.ts # OpenAPI endpoint parsing + raw spec cache for Code Mode
472
470
  │ │ │ ├── http-server.ts # MCP HTTP server implementation
473
471
  │ │ │ ├── mcp-server.ts # MCP stdio server implementation
474
472
  │ │ │ ├── tool-registry.ts # Global tool registration
@@ -889,21 +887,28 @@ normalizeCode(code: string): string // Strip markdown fences, validate shape
889
887
  truncateResult(value, maxChars?): string // Default 40K chars (~10K tokens)
890
888
  ```
891
889
 
892
- **Legacy files kept but unused**: `lib/api-discovery-tools.ts` (old find_api/call_api) and `lib/entity-graph-tools.ts` (old discover_schema) remain in the tree but are no longer imported.
893
-
894
890
  ## Rules for the API Endpoint Index
895
891
 
896
- Located in `lib/api-endpoint-index.ts`. Use the singleton pattern — never instantiate directly:
892
+ Located in `lib/api-endpoint-index.ts`. The module exposes pure functions — never instantiate. The Code Mode `search` and `execute` tools consume `getRawOpenApiSpec()` / `loadRichOpenApiSpec()` and `getApiEndpoints()`; no other live consumer exists.
897
893
 
898
894
  ```typescript
899
- class ApiEndpointIndex {
900
- static getInstance(): ApiEndpointIndex
901
- searchEndpoints(query: string, options?: SearchOptions): EndpointMatch[]
902
- getEndpoint(operationId: string): EndpointInfo | null
903
- getEndpointByPath(method: string, path: string): EndpointInfo | null
904
- }
895
+ // Endpoint parsing (cached per process)
896
+ getApiEndpoints(): Promise<ApiEndpoint[]>
897
+ getEndpointByOperationId(operationId: string): Promise<ApiEndpoint | null>
898
+ clearEndpointCache(): void
899
+
900
+ // Raw spec for Code Mode
901
+ getRawOpenApiSpec(): Promise<OpenApiDocument | null>
902
+ loadRichOpenApiSpec(): Promise<OpenApiDocument | null>
903
+ setRawSpecCache(doc: OpenApiDocument): void
904
+ clearRawSpecCache(): void
905
+
906
+ // Helper for request body schema flattening
907
+ simplifyRequestBodySchema(schema): { required, properties } | null
905
908
  ```
906
909
 
910
+ The module deliberately **does not** call `searchService.bulkIndex(...)` on boot — it has no live reader, and on a large OpenAPI surface (~600 operations) the fan-out triggered the embedding storm fixed by #1876.
911
+
907
912
  ## Docker Configuration
908
913
 
909
914
  ### Rules for the OpenCode Container
@@ -1407,6 +1412,26 @@ if (tool.requiredFeatures?.length) {
1407
1412
 
1408
1413
  ## Changelog
1409
1414
 
1415
+ ### 2026-05-13 - Remove dead `indexApiEndpoints` from MCP boot (#1876)
1416
+
1417
+ **What changed**:
1418
+ - MCP HTTP / stdio / dev entry points no longer call `indexApiEndpoints(searchService)` at startup. The Code Mode rewrite (2026-02-22) deleted the only readers (`find_api` / `call_api` / `discover_schema`), so the call was indexing into fulltext + tokens + vector indexes that nothing queried. On large specs (≳200 ops + remote embedding latency) the search-service fan-out also burned an `OpenAI` embedding storm + pgvector load on every boot — Code Mode reads the OpenAPI document directly via `getRawOpenApiSpec()` / `loadRichOpenApiSpec()`, in-memory.
1419
+ - Deleted `lib/api-discovery-tools.ts`, `lib/entity-graph-tools.ts`, `lib/api-endpoint-index-config.ts` — all dead since the 2026-02-22 rewrite, kept only by the boot-time indexing call we removed.
1420
+ - Pruned `lib/api-endpoint-index.ts`: removed `indexApiEndpoints`, `searchEndpoints`, `searchEndpointsFallback`, `buildSearchableContent`, `lastIndexChecksum`, and the `API_ENDPOINT_ENTITY` deprecated alias. Kept `parseApiEndpoints` (private), `getApiEndpoints`, `getEndpointByOperationId`, `getRawOpenApiSpec`, `loadRichOpenApiSpec`, `setRawSpecCache`, `clearRawSpecCache`, `clearEndpointCache`, `simplifyRequestBodySchema` — those still serve Code Mode.
1421
+
1422
+ **Files modified**:
1423
+ - `lib/http-server.ts`, `lib/mcp-server.ts`, `lib/mcp-dev-server.ts` — removed the `indexApiEndpoints` import + call
1424
+ - `lib/api-endpoint-index.ts` — pruned dead code
1425
+
1426
+ **Files deleted**:
1427
+ - `lib/api-discovery-tools.ts`
1428
+ - `lib/entity-graph-tools.ts`
1429
+ - `lib/api-endpoint-index-config.ts`
1430
+
1431
+ **Backward compatibility**: All removed symbols (`indexApiEndpoints`, `searchEndpoints`, `searchEndpointsFallback`, `endpointToIndexableRecord`, `API_ENDPOINT_ENTITY`, `API_ENDPOINT_SEARCH_CONFIG`, `apiEndpointEntityConfig`, `computeEndpointsChecksum`, `API_ENDPOINT_ENTITY_ID`, `GLOBAL_TENANT_ID` from `api-endpoint-index-config`) live inside the module's internal `lib/` path and are not part of the documented developer contract surface (see `BACKWARD_COMPATIBILITY.md`). They were already documented as legacy and unused.
1432
+
1433
+ **Operator cleanup (optional)**: If a deployment previously booted MCP and accumulated rows in fulltext/vector/tokens under `entityId: 'ai_assistant:api_endpoint'` / `tenantId: '00000000-0000-0000-0000-000000000000'`, they are orphaned but inert — no live workflow reads them. Manual purge is purely cosmetic.
1434
+
1410
1435
  ### 2026-05-08 - Phase 3 call-site cleanup (spec 2026-04-27-ai-agents-provider-model-baseurl-overrides)
1411
1436
 
1412
1437
  **What changed**:
@@ -1438,8 +1463,8 @@ if (tool.requiredFeatures?.length) {
1438
1463
  - `lib/mcp-server.ts` — Generates entity graph and caches spec for stdio mode
1439
1464
 
1440
1465
  **Files kept but unused**:
1441
- - `lib/api-discovery-tools.ts` — Old find_api/call_api (no longer imported)
1442
- - `lib/entity-graph-tools.ts` — Old discover_schema (no longer imported)
1466
+ - `lib/api-discovery-tools.ts` — Old find_api/call_api (no longer imported, deleted in #1876)
1467
+ - `lib/entity-graph-tools.ts` — Old discover_schema (no longer imported, deleted in #1876)
1443
1468
 
1444
1469
  ### 2026-01-17 - Session Persistence Fix
1445
1470
 
package/README.md CHANGED
@@ -512,7 +512,8 @@ packages/ai-assistant/
512
512
  │ │ ├── lib/
513
513
  │ │ │ ├── opencode-client.ts # OpenCode API client
514
514
  │ │ │ ├── opencode-handlers.ts # Request handlers
515
- │ │ │ ├── api-discovery-tools.ts # api_discover, api_execute, api_schema
515
+ │ │ │ ├── codemode-tools.ts # Code Mode meta-tools (search + execute)
516
+ │ │ │ ├── api-endpoint-index.ts # OpenAPI parsing + raw spec cache
516
517
  │ │ │ ├── http-server.ts # MCP HTTP server
517
518
  │ │ │ ├── mcp-dev-server.ts # Development MCP server
518
519
  │ │ │ └── tool-registry.ts # Tool registration
@@ -1,19 +1,11 @@
1
1
  import { buildOpenApiDocument } from "@open-mercato/shared/lib/openapi";
2
2
  import { fetchWithTimeout, resolveTimeoutMs } from "@open-mercato/shared/lib/http/fetchWithTimeout";
3
- import {
4
- API_ENDPOINT_ENTITY_ID,
5
- GLOBAL_TENANT_ID,
6
- API_ENDPOINT_SEARCH_CONFIG,
7
- endpointToIndexableRecord,
8
- computeEndpointsChecksum
9
- } from "./api-endpoint-index-config.js";
10
3
  const DEFAULT_OPENAPI_FETCH_TIMEOUT_MS = 1e4;
11
4
  function resolveOpenapiFetchTimeoutMs() {
12
5
  const raw = process.env.AI_OPENAPI_FETCH_TIMEOUT_MS;
13
6
  const parsed = raw ? Number.parseInt(raw, 10) : void 0;
14
7
  return resolveTimeoutMs(parsed, DEFAULT_OPENAPI_FETCH_TIMEOUT_MS);
15
8
  }
16
- const API_ENDPOINT_ENTITY = API_ENDPOINT_ENTITY_ID;
17
9
  let endpointsCache = null;
18
10
  let endpointsByOperationId = null;
19
11
  let rawSpecCache = null;
@@ -276,106 +268,6 @@ function extractRequestBodySchema(requestBody, schemas) {
276
268
  }
277
269
  return schema;
278
270
  }
279
- let lastIndexChecksum = null;
280
- async function indexApiEndpoints(searchService, force = false) {
281
- const endpoints = await getApiEndpoints();
282
- if (endpoints.length === 0) {
283
- console.error("[API Index] No endpoints to index");
284
- return 0;
285
- }
286
- const checksum = computeEndpointsChecksum(
287
- endpoints.map((e) => ({ operationId: e.operationId, method: e.method, path: e.path }))
288
- );
289
- if (!force && lastIndexChecksum === checksum) {
290
- console.error(`[API Index] Skipping indexing - ${endpoints.length} endpoints unchanged`);
291
- return 0;
292
- }
293
- const records = endpoints.map(
294
- (endpoint) => endpointToIndexableRecord(endpoint)
295
- );
296
- try {
297
- console.error(`[API Index] Starting bulk index of ${records.length} endpoints...`);
298
- const timeoutMs = 6e4;
299
- const indexPromise = searchService.bulkIndex(records);
300
- const timeoutPromise = new Promise(
301
- (_, reject) => setTimeout(() => reject(new Error(`Bulk index timed out after ${timeoutMs}ms`)), timeoutMs)
302
- );
303
- await Promise.race([indexPromise, timeoutPromise]);
304
- lastIndexChecksum = checksum;
305
- console.error(`[API Index] Indexed ${records.length} API endpoints for hybrid search`);
306
- return records.length;
307
- } catch (error) {
308
- console.error("[API Index] Failed to index endpoints:", error);
309
- lastIndexChecksum = checksum;
310
- return records.length;
311
- }
312
- }
313
- function buildSearchableContent(endpoint) {
314
- const parts = [
315
- endpoint.operationId,
316
- endpoint.method,
317
- endpoint.path,
318
- endpoint.summary,
319
- endpoint.description,
320
- ...endpoint.tags,
321
- ...endpoint.parameters.map((p) => `${p.name} ${p.description}`)
322
- ];
323
- return parts.filter(Boolean).join(" ");
324
- }
325
- async function searchEndpoints(searchService, query, options = {}) {
326
- const { limit = API_ENDPOINT_SEARCH_CONFIG.defaultLimit, method } = options;
327
- await getApiEndpoints();
328
- if (searchService) {
329
- try {
330
- const results = await searchService.search(query, {
331
- tenantId: GLOBAL_TENANT_ID,
332
- organizationId: null,
333
- entityTypes: [API_ENDPOINT_ENTITY_ID],
334
- limit: limit * 2
335
- // Get extra to account for filtering
336
- });
337
- const endpoints = [];
338
- for (const result of results) {
339
- if (endpoints.length >= limit) break;
340
- const endpoint = endpointsByOperationId?.get(result.recordId);
341
- if (endpoint) {
342
- if (method && endpoint.method !== method.toUpperCase()) continue;
343
- endpoints.push(endpoint);
344
- }
345
- }
346
- if (endpoints.length > 0) {
347
- return endpoints;
348
- }
349
- console.error("[API Index] No hybrid search results, falling back to in-memory search");
350
- } catch (error) {
351
- console.error("[API Index] Hybrid search failed, falling back to in-memory:", error);
352
- }
353
- }
354
- return searchEndpointsFallback(query, { limit, method });
355
- }
356
- function searchEndpointsFallback(query, options = {}) {
357
- const { limit = API_ENDPOINT_SEARCH_CONFIG.defaultLimit, method } = options;
358
- if (!endpointsCache) {
359
- return [];
360
- }
361
- const queryLower = query.toLowerCase();
362
- const queryTerms = queryLower.split(/\s+/).filter(Boolean);
363
- let matches = endpointsCache.filter((endpoint) => {
364
- const content = buildSearchableContent(endpoint).toLowerCase();
365
- return queryTerms.some((term) => content.includes(term));
366
- });
367
- if (method) {
368
- matches = matches.filter((e) => e.method === method.toUpperCase());
369
- }
370
- matches.sort((a, b) => {
371
- const aContent = buildSearchableContent(a).toLowerCase();
372
- const bContent = buildSearchableContent(b).toLowerCase();
373
- const aScore = queryTerms.filter((t) => aContent.includes(t)).length;
374
- const bScore = queryTerms.filter((t) => bContent.includes(t)).length;
375
- return bScore - aScore;
376
- });
377
- return matches.slice(0, limit);
378
- }
379
271
  function clearEndpointCache() {
380
272
  endpointsCache = null;
381
273
  endpointsByOperationId = null;
@@ -401,15 +293,12 @@ function simplifyRequestBodySchema(schema) {
401
293
  return { required, properties };
402
294
  }
403
295
  export {
404
- API_ENDPOINT_ENTITY,
405
296
  clearEndpointCache,
406
297
  clearRawSpecCache,
407
298
  getApiEndpoints,
408
299
  getEndpointByOperationId,
409
300
  getRawOpenApiSpec,
410
- indexApiEndpoints,
411
301
  loadRichOpenApiSpec,
412
- searchEndpoints,
413
302
  setRawSpecCache,
414
303
  simplifyRequestBodySchema
415
304
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/ai_assistant/lib/api-endpoint-index.ts"],
4
- "sourcesContent": ["/**\n * API Endpoint Index\n *\n * Parses OpenAPI spec and indexes endpoints for discovery via hybrid search.\n */\n\nimport type { OpenApiDocument } from '@open-mercato/shared/lib/openapi'\nimport { buildOpenApiDocument } from '@open-mercato/shared/lib/openapi'\nimport type { Module } from '@open-mercato/shared/modules/registry'\nimport type { SearchService } from '@open-mercato/search/service'\nimport type { IndexableRecord } from '@open-mercato/search/types'\nimport { fetchWithTimeout, resolveTimeoutMs } from '@open-mercato/shared/lib/http/fetchWithTimeout'\nimport {\n API_ENDPOINT_ENTITY_ID,\n GLOBAL_TENANT_ID,\n API_ENDPOINT_SEARCH_CONFIG,\n endpointToIndexableRecord,\n computeEndpointsChecksum,\n} from './api-endpoint-index-config'\n\nconst DEFAULT_OPENAPI_FETCH_TIMEOUT_MS = 10_000\n\nfunction resolveOpenapiFetchTimeoutMs(): number {\n const raw = process.env.AI_OPENAPI_FETCH_TIMEOUT_MS\n const parsed = raw ? Number.parseInt(raw, 10) : undefined\n return resolveTimeoutMs(parsed, DEFAULT_OPENAPI_FETCH_TIMEOUT_MS)\n}\n\n/**\n * Indexed API endpoint structure\n */\nexport interface ApiEndpoint {\n id: string\n operationId: string\n method: string\n path: string\n summary: string\n description: string\n tags: string[]\n requiredFeatures: string[]\n parameters: ApiParameter[]\n requestBodySchema: Record<string, unknown> | null\n deprecated: boolean\n}\n\nexport interface ApiParameter {\n name: string\n in: 'path' | 'query' | 'header'\n required: boolean\n type: string\n description: string\n}\n\n/**\n * Entity type for API endpoints in search index\n * @deprecated Use API_ENDPOINT_ENTITY_ID from api-endpoint-index-config.ts\n */\nexport const API_ENDPOINT_ENTITY = API_ENDPOINT_ENTITY_ID\n\n/**\n * In-memory cache of parsed endpoints (avoid re-parsing on each request)\n */\nlet endpointsCache: ApiEndpoint[] | null = null\nlet endpointsByOperationId: Map<string, ApiEndpoint> | null = null\n\n/**\n * In-memory cache of the raw OpenAPI spec document (for Code Mode search tool)\n */\nlet rawSpecCache: OpenApiDocument | null = null\n\n/**\n * Get all parsed API endpoints (cached)\n */\nexport async function getApiEndpoints(): Promise<ApiEndpoint[]> {\n if (endpointsCache) {\n return endpointsCache\n }\n\n endpointsCache = await parseApiEndpoints()\n endpointsByOperationId = new Map(endpointsCache.map((e) => [e.operationId, e]))\n\n return endpointsCache\n}\n\n/**\n * Get endpoint by operationId\n */\nexport async function getEndpointByOperationId(operationId: string): Promise<ApiEndpoint | null> {\n await getApiEndpoints() // Ensure cache is populated\n return endpointsByOperationId?.get(operationId) ?? null\n}\n\n/**\n * Get the raw OpenAPI spec document (cached).\n * Uses the same 3-tier loading strategy as parseApiEndpoints():\n * generated JSON \u2192 module registry \u2192 HTTP fetch.\n */\nexport async function getRawOpenApiSpec(): Promise<OpenApiDocument | null> {\n if (rawSpecCache) return rawSpecCache\n rawSpecCache = await loadRawOpenApiSpec()\n return rawSpecCache\n}\n\n/**\n * Set the raw OpenAPI spec cache directly.\n * Used by servers that want to inject a pre-built spec.\n */\nexport function setRawSpecCache(doc: OpenApiDocument): void {\n rawSpecCache = doc\n}\n\n/**\n * Clear the raw OpenAPI spec cache.\n */\nexport function clearRawSpecCache(): void {\n rawSpecCache = null\n}\n\n/**\n * Load the rich OpenAPI spec, skipping Tier 1 (static JSON) which lacks requestBody schemas.\n * Prefers Tier 2 (runtime module registry) which has full Zod-converted schemas.\n * Falls back to Tier 1 then Tier 3 if needed.\n */\nexport async function loadRichOpenApiSpec(): Promise<OpenApiDocument | null> {\n if (rawSpecCache) return rawSpecCache\n\n // Tier 2 first: Module registry (has full Zod-converted schemas)\n try {\n const { getModules } = await import('@open-mercato/shared/lib/modules/registry')\n const modules: Module[] = getModules()\n const modulesWithApis = modules.filter((m) => m.apis && m.apis.length > 0)\n\n if (modulesWithApis.length > 0) {\n const doc = buildOpenApiDocument(modules, {\n title: 'Open Mercato API',\n version: '1.0.0',\n servers: [{ url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' }],\n })\n if (!doc.paths || Object.keys(doc.paths).length === 0) {\n return null\n }\n console.error(`[API Index] Rich OpenAPI spec built from ${modulesWithApis.length} modules (Tier 2)`)\n rawSpecCache = doc\n return doc\n }\n } catch {\n // Registry not available \u2014 fall through\n }\n\n // Fall back to standard 3-tier loading (Tier 1 \u2192 Tier 3)\n rawSpecCache = await loadRawOpenApiSpec()\n return rawSpecCache\n}\n\n/**\n * Load raw OpenAPI spec using the 3-tier strategy.\n */\nasync function loadRawOpenApiSpec(): Promise<OpenApiDocument | null> {\n // Tier 1: Generated JSON file\n try {\n const fs = await import('node:fs')\n const path = await import('node:path')\n const { findAppRoot, findAllApps } = await import('@open-mercato/shared/lib/bootstrap/appResolver')\n\n let appRoot = findAppRoot()\n if (!appRoot) {\n let current = process.cwd()\n while (current !== path.dirname(current)) {\n const appsDir = path.join(current, 'apps')\n if (fs.existsSync(appsDir)) {\n const apps = findAllApps(current)\n if (apps.length > 0) {\n appRoot = apps[0]\n break\n }\n }\n current = path.dirname(current)\n }\n }\n\n if (appRoot) {\n const jsonPath = path.join(appRoot.generatedDir, 'openapi.generated.json')\n if (fs.existsSync(jsonPath)) {\n const doc = JSON.parse(fs.readFileSync(jsonPath, 'utf-8')) as OpenApiDocument\n console.error(`[API Index] Raw OpenAPI spec loaded from ${jsonPath}`)\n return doc\n }\n }\n } catch (error) {\n console.error('[API Index] Raw spec from JSON failed:', error instanceof Error ? error.message : error)\n }\n\n // Tier 2: Module registry\n try {\n const { getModules } = await import('@open-mercato/shared/lib/modules/registry')\n const modules: Module[] = getModules()\n const modulesWithApis = modules.filter((m) => m.apis && m.apis.length > 0)\n\n if (modulesWithApis.length > 0) {\n const doc = buildOpenApiDocument(modules, {\n title: 'Open Mercato API',\n version: '1.0.0',\n servers: [{ url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' }],\n })\n console.error(`[API Index] Raw OpenAPI spec built from ${modulesWithApis.length} modules`)\n return doc\n }\n } catch {\n // Registry not available\n }\n\n // Tier 3: HTTP fetch\n const baseUrl =\n process.env.NEXT_PUBLIC_API_BASE_URL ||\n process.env.NEXT_PUBLIC_APP_URL ||\n process.env.APP_URL ||\n 'http://localhost:3000'\n\n try {\n const response = await fetchWithTimeout(`${baseUrl}/api/docs/openapi`, {\n timeoutMs: resolveOpenapiFetchTimeoutMs(),\n })\n if (response.ok) {\n const doc = (await response.json()) as OpenApiDocument\n console.error('[API Index] Raw OpenAPI spec fetched via HTTP')\n return doc\n }\n } catch (error) {\n console.error('[API Index] Raw spec HTTP fetch failed:', error instanceof Error ? error.message : error)\n }\n\n return null\n}\n\n/**\n * Parse endpoints from generated OpenAPI JSON file (for CLI context).\n * This is generated by `yarn generate`.\n */\nasync function parseApiEndpointsFromGeneratedJson(): Promise<ApiEndpoint[]> {\n try {\n const fs = await import('node:fs')\n const path = await import('node:path')\n const { findAppRoot, findAllApps } = await import('@open-mercato/shared/lib/bootstrap/appResolver')\n\n let appRoot = findAppRoot()\n\n // Try monorepo structure if not found - walk up to find monorepo root\n if (!appRoot) {\n let current = process.cwd()\n // Walk up until we find a directory containing 'apps' folder\n while (current !== path.dirname(current)) {\n const appsDir = path.join(current, 'apps')\n if (fs.existsSync(appsDir)) {\n const apps = findAllApps(current)\n if (apps.length > 0) {\n appRoot = apps[0]\n break\n }\n }\n current = path.dirname(current)\n }\n }\n\n if (!appRoot) {\n console.error('[API Index] Could not find app root')\n return []\n }\n\n const jsonPath = path.join(appRoot.generatedDir, 'openapi.generated.json')\n if (!fs.existsSync(jsonPath)) {\n console.error('[API Index] openapi.generated.json not found - run yarn generate')\n return []\n }\n\n const doc = JSON.parse(fs.readFileSync(jsonPath, 'utf-8')) as OpenApiDocument\n console.error(`[API Index] Loaded OpenAPI from ${jsonPath}`)\n return extractEndpoints(doc)\n } catch (error) {\n console.error('[API Index] Error reading generated JSON:', error instanceof Error ? error.message : error)\n return []\n }\n}\n\n/**\n * Parse endpoints from registered modules (works in Next.js context).\n */\nasync function parseApiEndpointsFromModules(): Promise<ApiEndpoint[]> {\n try {\n const { getModules } = await import('@open-mercato/shared/lib/modules/registry')\n const modules: Module[] = getModules()\n\n // Count how many modules have APIs defined\n const modulesWithApis = modules.filter((m) => m.apis && m.apis.length > 0)\n\n if (modulesWithApis.length > 0) {\n console.error(\n `[API Index] Found ${modules.length} modules, ${modulesWithApis.length} with APIs`\n )\n\n // Generate OpenAPI spec from modules\n const doc = buildOpenApiDocument(modules, {\n title: 'Open Mercato API',\n version: '1.0.0',\n servers: [{ url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' }],\n })\n if (!doc.paths || Object.keys(doc.paths).length === 0) {\n return []\n }\n\n return extractEndpoints(doc)\n }\n } catch {\n // Registry not available\n }\n\n return []\n}\n\n/**\n * Parse OpenAPI spec via HTTP fetch.\n * Fetches the OpenAPI spec from the running app's /api/docs/openapi endpoint.\n */\nasync function parseApiEndpointsFromHttp(): Promise<ApiEndpoint[]> {\n const baseUrl =\n process.env.NEXT_PUBLIC_API_BASE_URL ||\n process.env.NEXT_PUBLIC_APP_URL ||\n process.env.APP_URL ||\n 'http://localhost:3000'\n\n const openApiUrl = `${baseUrl}/api/docs/openapi`\n\n try {\n console.error(`[API Index] Fetching OpenAPI spec from ${openApiUrl}...`)\n const response = await fetchWithTimeout(openApiUrl, {\n timeoutMs: resolveOpenapiFetchTimeoutMs(),\n })\n\n if (!response.ok) {\n console.error(`[API Index] Failed to fetch OpenAPI spec: ${response.status} ${response.statusText}`)\n return []\n }\n\n const doc = (await response.json()) as OpenApiDocument\n console.error(`[API Index] Successfully fetched OpenAPI spec`)\n return extractEndpoints(doc)\n } catch (error) {\n console.error('[API Index] Could not fetch OpenAPI spec:', error instanceof Error ? error.message : error)\n console.error('[API Index] Make sure the app is running at', baseUrl)\n return []\n }\n}\n\n/**\n * Parse API endpoints - tries generated JSON first (CLI), then modules (Next.js), then HTTP.\n */\nasync function parseApiEndpoints(): Promise<ApiEndpoint[]> {\n // Try generated JSON first (works in CLI context without Next.js)\n const fromJson = await parseApiEndpointsFromGeneratedJson()\n if (fromJson.length > 0) {\n console.error(`[API Index] Loaded ${fromJson.length} endpoints from generated JSON`)\n return fromJson\n }\n\n // Try loading from module registry (works in Next.js context)\n const fromModules = await parseApiEndpointsFromModules()\n if (fromModules.length > 0) {\n console.error(`[API Index] Loaded ${fromModules.length} endpoints from modules registry`)\n return fromModules\n }\n\n // Fall back to HTTP fetch (requires running Next.js app)\n console.error('[API Index] Generated JSON and modules not available, falling back to HTTP fetch...')\n return parseApiEndpointsFromHttp()\n}\n\n/**\n * Extract endpoints from OpenAPI document\n */\nfunction extractEndpoints(doc: OpenApiDocument): ApiEndpoint[] {\n const endpoints: ApiEndpoint[] = []\n const validMethods = ['get', 'post', 'put', 'patch', 'delete']\n\n if (!doc.paths) {\n return endpoints\n }\n\n for (const [path, pathItem] of Object.entries(doc.paths)) {\n if (!pathItem || typeof pathItem !== 'object') continue\n\n for (const [method, operation] of Object.entries(pathItem)) {\n if (!validMethods.includes(method.toLowerCase())) continue\n if (!operation || typeof operation !== 'object') continue\n\n const op = operation as any\n\n // Generate operationId if not present\n const operationId = op.operationId || generateOperationId(path, method)\n\n const endpoint: ApiEndpoint = {\n id: operationId,\n operationId,\n method: method.toUpperCase(),\n path,\n summary: op.summary || '',\n description: op.description || op.summary || `${method.toUpperCase()} ${path}`,\n tags: op.tags || [],\n requiredFeatures: op['x-require-features'] || [],\n deprecated: op.deprecated || false,\n parameters: extractParameters(op.parameters || []),\n requestBodySchema: extractRequestBodySchema(op.requestBody, doc.components?.schemas),\n }\n\n endpoints.push(endpoint)\n }\n }\n\n console.error(`[API Index] Parsed ${endpoints.length} endpoints from OpenAPI spec`)\n return endpoints\n}\n\n/**\n * Generate operationId from path and method\n */\nfunction generateOperationId(path: string, method: string): string {\n const pathParts = path\n .replace(/^\\//, '')\n .replace(/\\{([^}]+)\\}/g, 'by_$1')\n .split('/')\n .filter(Boolean)\n .join('_')\n\n return `${method.toLowerCase()}_${pathParts}`\n}\n\n/**\n * Extract parameter info\n */\nfunction extractParameters(params: any[]): ApiParameter[] {\n return params\n .filter((p) => p.in === 'path' || p.in === 'query')\n .map((p) => ({\n name: p.name,\n in: p.in,\n required: p.required ?? false,\n type: p.schema?.type || 'string',\n description: p.description || '',\n }))\n}\n\n/**\n * Extract request body schema (simplified)\n */\nfunction extractRequestBodySchema(\n requestBody: any,\n schemas?: Record<string, any>\n): Record<string, unknown> | null {\n if (!requestBody?.content?.['application/json']?.schema) {\n return null\n }\n\n const schema = requestBody.content['application/json'].schema\n\n // Resolve $ref if present\n if (schema.$ref && schemas) {\n const refPath = schema.$ref.replace('#/components/schemas/', '')\n return schemas[refPath] || schema\n }\n\n return schema\n}\n\n/**\n * Checksum from last indexing operation\n */\nlet lastIndexChecksum: string | null = null\n\n/**\n * Index endpoints for search discovery using hybrid search strategies.\n * Uses checksum-based change detection to avoid unnecessary re-indexing.\n *\n * @param searchService - The search service to use for indexing\n * @param force - Force re-indexing even if checksum hasn't changed\n * @returns Number of endpoints indexed\n */\nexport async function indexApiEndpoints(\n searchService: SearchService,\n force = false\n): Promise<number> {\n const endpoints = await getApiEndpoints()\n\n if (endpoints.length === 0) {\n console.error('[API Index] No endpoints to index')\n return 0\n }\n\n // Compute checksum to detect changes\n const checksum = computeEndpointsChecksum(\n endpoints.map((e) => ({ operationId: e.operationId, method: e.method, path: e.path }))\n )\n\n // Skip if checksum matches and not forced\n if (!force && lastIndexChecksum === checksum) {\n console.error(`[API Index] Skipping indexing - ${endpoints.length} endpoints unchanged`)\n return 0\n }\n\n // Convert to indexable records using the proper format\n const records: IndexableRecord[] = endpoints.map((endpoint) =>\n endpointToIndexableRecord(endpoint)\n )\n\n try {\n console.error(`[API Index] Starting bulk index of ${records.length} endpoints...`)\n // Bulk index using all available strategies (fulltext + vector)\n // Use Promise.race with timeout to prevent hanging\n const timeoutMs = 60000 // 60 second timeout\n const indexPromise = searchService.bulkIndex(records)\n const timeoutPromise = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(`Bulk index timed out after ${timeoutMs}ms`)), timeoutMs)\n )\n\n await Promise.race([indexPromise, timeoutPromise])\n lastIndexChecksum = checksum\n console.error(`[API Index] Indexed ${records.length} API endpoints for hybrid search`)\n return records.length\n } catch (error) {\n console.error('[API Index] Failed to index endpoints:', error)\n // Still return the count - some strategies may have succeeded\n lastIndexChecksum = checksum\n return records.length\n }\n}\n\n/**\n * Build searchable content from endpoint\n */\nfunction buildSearchableContent(endpoint: ApiEndpoint): string {\n const parts = [\n endpoint.operationId,\n endpoint.method,\n endpoint.path,\n endpoint.summary,\n endpoint.description,\n ...endpoint.tags,\n ...endpoint.parameters.map((p) => `${p.name} ${p.description}`),\n ]\n\n return parts.filter(Boolean).join(' ')\n}\n\n/**\n * Search endpoints using hybrid search (fulltext + vector).\n * Falls back to in-memory search if search service is not available.\n */\nexport async function searchEndpoints(\n searchService: SearchService | null,\n query: string,\n options: { limit?: number; method?: string } = {}\n): Promise<ApiEndpoint[]> {\n const { limit = API_ENDPOINT_SEARCH_CONFIG.defaultLimit, method } = options\n\n // Ensure endpoints are loaded\n await getApiEndpoints()\n\n // Try hybrid search first if search service is available\n if (searchService) {\n try {\n // Use hybrid search (fulltext + vector)\n const results = await searchService.search(query, {\n tenantId: GLOBAL_TENANT_ID,\n organizationId: null,\n entityTypes: [API_ENDPOINT_ENTITY_ID],\n limit: limit * 2, // Get extra to account for filtering\n })\n\n // Map search results back to ApiEndpoint objects\n const endpoints: ApiEndpoint[] = []\n for (const result of results) {\n if (endpoints.length >= limit) break\n\n const endpoint = endpointsByOperationId?.get(result.recordId)\n if (endpoint) {\n // Apply method filter if not handled by search\n if (method && endpoint.method !== method.toUpperCase()) continue\n endpoints.push(endpoint)\n }\n }\n\n if (endpoints.length > 0) {\n return endpoints\n }\n\n // Fall through to fallback if no results from hybrid search\n console.error('[API Index] No hybrid search results, falling back to in-memory search')\n } catch (error) {\n console.error('[API Index] Hybrid search failed, falling back to in-memory:', error)\n }\n }\n\n // Fallback: Simple in-memory text matching\n return searchEndpointsFallback(query, { limit, method })\n}\n\n/**\n * Fallback in-memory search when hybrid search is not available.\n */\nfunction searchEndpointsFallback(\n query: string,\n options: { limit?: number; method?: string } = {}\n): ApiEndpoint[] {\n const { limit = API_ENDPOINT_SEARCH_CONFIG.defaultLimit, method } = options\n\n if (!endpointsCache) {\n return []\n }\n\n const queryLower = query.toLowerCase()\n const queryTerms = queryLower.split(/\\s+/).filter(Boolean)\n\n let matches = endpointsCache.filter((endpoint) => {\n const content = buildSearchableContent(endpoint).toLowerCase()\n return queryTerms.some((term) => content.includes(term))\n })\n\n // Filter by method if specified\n if (method) {\n matches = matches.filter((e) => e.method === method.toUpperCase())\n }\n\n // Sort by relevance (number of matching terms)\n matches.sort((a, b) => {\n const aContent = buildSearchableContent(a).toLowerCase()\n const bContent = buildSearchableContent(b).toLowerCase()\n const aScore = queryTerms.filter((t) => aContent.includes(t)).length\n const bScore = queryTerms.filter((t) => bContent.includes(t)).length\n return bScore - aScore\n })\n\n return matches.slice(0, limit)\n}\n\n/**\n * Clear endpoint cache (for testing)\n */\nexport function clearEndpointCache(): void {\n endpointsCache = null\n endpointsByOperationId = null\n rawSpecCache = null\n}\n\n/**\n * Extract simplified request body schema for LLM consumption.\n * Returns required fields and basic property info without deep nesting.\n */\nexport function simplifyRequestBodySchema(\n schema: Record<string, unknown> | null\n): { required: string[]; properties: Record<string, { type: string; format?: string; enum?: string[] }> } | null {\n if (!schema) return null\n\n const properties: Record<string, { type: string; format?: string; enum?: string[] }> = {}\n const required: string[] = (schema.required as string[]) || []\n\n const schemaProps = (schema.properties || schema) as Record<string, unknown>\n\n for (const [key, value] of Object.entries(schemaProps)) {\n if (typeof value !== 'object' || value === null) continue\n const propSchema = value as Record<string, unknown>\n\n const prop: { type: string; format?: string; enum?: string[] } = {\n type: (propSchema.type as string) || 'unknown',\n }\n\n if (propSchema.format) prop.format = propSchema.format as string\n if (propSchema.enum && Array.isArray(propSchema.enum)) {\n prop.enum = propSchema.enum.slice(0, 10) as string[] // Limit enum values\n }\n\n properties[key] = prop\n }\n\n return { required, properties }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * API Endpoint Index\n *\n * Parses the OpenAPI spec into a cached, in-memory list of endpoints and\n * exposes the raw OpenAPI document to Code Mode's `search` tool. The Code\n * Mode rewrite (2026-02-22) made this the only consumer \u2014 the legacy\n * `find_api` / `call_api` / `discover_schema` tools and their search-index\n * fan-out have been removed.\n */\n\nimport type { OpenApiDocument } from '@open-mercato/shared/lib/openapi'\nimport { buildOpenApiDocument } from '@open-mercato/shared/lib/openapi'\nimport type { Module } from '@open-mercato/shared/modules/registry'\nimport { fetchWithTimeout, resolveTimeoutMs } from '@open-mercato/shared/lib/http/fetchWithTimeout'\n\nconst DEFAULT_OPENAPI_FETCH_TIMEOUT_MS = 10_000\n\nfunction resolveOpenapiFetchTimeoutMs(): number {\n const raw = process.env.AI_OPENAPI_FETCH_TIMEOUT_MS\n const parsed = raw ? Number.parseInt(raw, 10) : undefined\n return resolveTimeoutMs(parsed, DEFAULT_OPENAPI_FETCH_TIMEOUT_MS)\n}\n\n/**\n * Indexed API endpoint structure\n */\nexport interface ApiEndpoint {\n id: string\n operationId: string\n method: string\n path: string\n summary: string\n description: string\n tags: string[]\n requiredFeatures: string[]\n parameters: ApiParameter[]\n requestBodySchema: Record<string, unknown> | null\n deprecated: boolean\n}\n\nexport interface ApiParameter {\n name: string\n in: 'path' | 'query' | 'header'\n required: boolean\n type: string\n description: string\n}\n\n/**\n * In-memory cache of parsed endpoints (avoid re-parsing on each request)\n */\nlet endpointsCache: ApiEndpoint[] | null = null\nlet endpointsByOperationId: Map<string, ApiEndpoint> | null = null\n\n/**\n * In-memory cache of the raw OpenAPI spec document (for Code Mode search tool)\n */\nlet rawSpecCache: OpenApiDocument | null = null\n\n/**\n * Get all parsed API endpoints (cached)\n */\nexport async function getApiEndpoints(): Promise<ApiEndpoint[]> {\n if (endpointsCache) {\n return endpointsCache\n }\n\n endpointsCache = await parseApiEndpoints()\n endpointsByOperationId = new Map(endpointsCache.map((e) => [e.operationId, e]))\n\n return endpointsCache\n}\n\n/**\n * Get endpoint by operationId\n */\nexport async function getEndpointByOperationId(operationId: string): Promise<ApiEndpoint | null> {\n await getApiEndpoints() // Ensure cache is populated\n return endpointsByOperationId?.get(operationId) ?? null\n}\n\n/**\n * Get the raw OpenAPI spec document (cached).\n * Uses the same 3-tier loading strategy as parseApiEndpoints():\n * generated JSON \u2192 module registry \u2192 HTTP fetch.\n */\nexport async function getRawOpenApiSpec(): Promise<OpenApiDocument | null> {\n if (rawSpecCache) return rawSpecCache\n rawSpecCache = await loadRawOpenApiSpec()\n return rawSpecCache\n}\n\n/**\n * Set the raw OpenAPI spec cache directly.\n * Used by servers that want to inject a pre-built spec.\n */\nexport function setRawSpecCache(doc: OpenApiDocument): void {\n rawSpecCache = doc\n}\n\n/**\n * Clear the raw OpenAPI spec cache.\n */\nexport function clearRawSpecCache(): void {\n rawSpecCache = null\n}\n\n/**\n * Load the rich OpenAPI spec, skipping Tier 1 (static JSON) which lacks requestBody schemas.\n * Prefers Tier 2 (runtime module registry) which has full Zod-converted schemas.\n * Falls back to Tier 1 then Tier 3 if needed.\n */\nexport async function loadRichOpenApiSpec(): Promise<OpenApiDocument | null> {\n if (rawSpecCache) return rawSpecCache\n\n // Tier 2 first: Module registry (has full Zod-converted schemas)\n try {\n const { getModules } = await import('@open-mercato/shared/lib/modules/registry')\n const modules: Module[] = getModules()\n const modulesWithApis = modules.filter((m) => m.apis && m.apis.length > 0)\n\n if (modulesWithApis.length > 0) {\n const doc = buildOpenApiDocument(modules, {\n title: 'Open Mercato API',\n version: '1.0.0',\n servers: [{ url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' }],\n })\n if (!doc.paths || Object.keys(doc.paths).length === 0) {\n return null\n }\n console.error(`[API Index] Rich OpenAPI spec built from ${modulesWithApis.length} modules (Tier 2)`)\n rawSpecCache = doc\n return doc\n }\n } catch {\n // Registry not available \u2014 fall through\n }\n\n // Fall back to standard 3-tier loading (Tier 1 \u2192 Tier 3)\n rawSpecCache = await loadRawOpenApiSpec()\n return rawSpecCache\n}\n\n/**\n * Load raw OpenAPI spec using the 3-tier strategy.\n */\nasync function loadRawOpenApiSpec(): Promise<OpenApiDocument | null> {\n // Tier 1: Generated JSON file\n try {\n const fs = await import('node:fs')\n const path = await import('node:path')\n const { findAppRoot, findAllApps } = await import('@open-mercato/shared/lib/bootstrap/appResolver')\n\n let appRoot = findAppRoot()\n if (!appRoot) {\n let current = process.cwd()\n while (current !== path.dirname(current)) {\n const appsDir = path.join(current, 'apps')\n if (fs.existsSync(appsDir)) {\n const apps = findAllApps(current)\n if (apps.length > 0) {\n appRoot = apps[0]\n break\n }\n }\n current = path.dirname(current)\n }\n }\n\n if (appRoot) {\n const jsonPath = path.join(appRoot.generatedDir, 'openapi.generated.json')\n if (fs.existsSync(jsonPath)) {\n const doc = JSON.parse(fs.readFileSync(jsonPath, 'utf-8')) as OpenApiDocument\n console.error(`[API Index] Raw OpenAPI spec loaded from ${jsonPath}`)\n return doc\n }\n }\n } catch (error) {\n console.error('[API Index] Raw spec from JSON failed:', error instanceof Error ? error.message : error)\n }\n\n // Tier 2: Module registry\n try {\n const { getModules } = await import('@open-mercato/shared/lib/modules/registry')\n const modules: Module[] = getModules()\n const modulesWithApis = modules.filter((m) => m.apis && m.apis.length > 0)\n\n if (modulesWithApis.length > 0) {\n const doc = buildOpenApiDocument(modules, {\n title: 'Open Mercato API',\n version: '1.0.0',\n servers: [{ url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' }],\n })\n console.error(`[API Index] Raw OpenAPI spec built from ${modulesWithApis.length} modules`)\n return doc\n }\n } catch {\n // Registry not available\n }\n\n // Tier 3: HTTP fetch\n const baseUrl =\n process.env.NEXT_PUBLIC_API_BASE_URL ||\n process.env.NEXT_PUBLIC_APP_URL ||\n process.env.APP_URL ||\n 'http://localhost:3000'\n\n try {\n const response = await fetchWithTimeout(`${baseUrl}/api/docs/openapi`, {\n timeoutMs: resolveOpenapiFetchTimeoutMs(),\n })\n if (response.ok) {\n const doc = (await response.json()) as OpenApiDocument\n console.error('[API Index] Raw OpenAPI spec fetched via HTTP')\n return doc\n }\n } catch (error) {\n console.error('[API Index] Raw spec HTTP fetch failed:', error instanceof Error ? error.message : error)\n }\n\n return null\n}\n\n/**\n * Parse endpoints from generated OpenAPI JSON file (for CLI context).\n * This is generated by `yarn generate`.\n */\nasync function parseApiEndpointsFromGeneratedJson(): Promise<ApiEndpoint[]> {\n try {\n const fs = await import('node:fs')\n const path = await import('node:path')\n const { findAppRoot, findAllApps } = await import('@open-mercato/shared/lib/bootstrap/appResolver')\n\n let appRoot = findAppRoot()\n\n // Try monorepo structure if not found - walk up to find monorepo root\n if (!appRoot) {\n let current = process.cwd()\n // Walk up until we find a directory containing 'apps' folder\n while (current !== path.dirname(current)) {\n const appsDir = path.join(current, 'apps')\n if (fs.existsSync(appsDir)) {\n const apps = findAllApps(current)\n if (apps.length > 0) {\n appRoot = apps[0]\n break\n }\n }\n current = path.dirname(current)\n }\n }\n\n if (!appRoot) {\n console.error('[API Index] Could not find app root')\n return []\n }\n\n const jsonPath = path.join(appRoot.generatedDir, 'openapi.generated.json')\n if (!fs.existsSync(jsonPath)) {\n console.error('[API Index] openapi.generated.json not found - run yarn generate')\n return []\n }\n\n const doc = JSON.parse(fs.readFileSync(jsonPath, 'utf-8')) as OpenApiDocument\n console.error(`[API Index] Loaded OpenAPI from ${jsonPath}`)\n return extractEndpoints(doc)\n } catch (error) {\n console.error('[API Index] Error reading generated JSON:', error instanceof Error ? error.message : error)\n return []\n }\n}\n\n/**\n * Parse endpoints from registered modules (works in Next.js context).\n */\nasync function parseApiEndpointsFromModules(): Promise<ApiEndpoint[]> {\n try {\n const { getModules } = await import('@open-mercato/shared/lib/modules/registry')\n const modules: Module[] = getModules()\n\n // Count how many modules have APIs defined\n const modulesWithApis = modules.filter((m) => m.apis && m.apis.length > 0)\n\n if (modulesWithApis.length > 0) {\n console.error(\n `[API Index] Found ${modules.length} modules, ${modulesWithApis.length} with APIs`\n )\n\n // Generate OpenAPI spec from modules\n const doc = buildOpenApiDocument(modules, {\n title: 'Open Mercato API',\n version: '1.0.0',\n servers: [{ url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' }],\n })\n if (!doc.paths || Object.keys(doc.paths).length === 0) {\n return []\n }\n\n return extractEndpoints(doc)\n }\n } catch {\n // Registry not available\n }\n\n return []\n}\n\n/**\n * Parse OpenAPI spec via HTTP fetch.\n * Fetches the OpenAPI spec from the running app's /api/docs/openapi endpoint.\n */\nasync function parseApiEndpointsFromHttp(): Promise<ApiEndpoint[]> {\n const baseUrl =\n process.env.NEXT_PUBLIC_API_BASE_URL ||\n process.env.NEXT_PUBLIC_APP_URL ||\n process.env.APP_URL ||\n 'http://localhost:3000'\n\n const openApiUrl = `${baseUrl}/api/docs/openapi`\n\n try {\n console.error(`[API Index] Fetching OpenAPI spec from ${openApiUrl}...`)\n const response = await fetchWithTimeout(openApiUrl, {\n timeoutMs: resolveOpenapiFetchTimeoutMs(),\n })\n\n if (!response.ok) {\n console.error(`[API Index] Failed to fetch OpenAPI spec: ${response.status} ${response.statusText}`)\n return []\n }\n\n const doc = (await response.json()) as OpenApiDocument\n console.error(`[API Index] Successfully fetched OpenAPI spec`)\n return extractEndpoints(doc)\n } catch (error) {\n console.error('[API Index] Could not fetch OpenAPI spec:', error instanceof Error ? error.message : error)\n console.error('[API Index] Make sure the app is running at', baseUrl)\n return []\n }\n}\n\n/**\n * Parse API endpoints - tries generated JSON first (CLI), then modules (Next.js), then HTTP.\n */\nasync function parseApiEndpoints(): Promise<ApiEndpoint[]> {\n // Try generated JSON first (works in CLI context without Next.js)\n const fromJson = await parseApiEndpointsFromGeneratedJson()\n if (fromJson.length > 0) {\n console.error(`[API Index] Loaded ${fromJson.length} endpoints from generated JSON`)\n return fromJson\n }\n\n // Try loading from module registry (works in Next.js context)\n const fromModules = await parseApiEndpointsFromModules()\n if (fromModules.length > 0) {\n console.error(`[API Index] Loaded ${fromModules.length} endpoints from modules registry`)\n return fromModules\n }\n\n // Fall back to HTTP fetch (requires running Next.js app)\n console.error('[API Index] Generated JSON and modules not available, falling back to HTTP fetch...')\n return parseApiEndpointsFromHttp()\n}\n\n/**\n * Extract endpoints from OpenAPI document\n */\nfunction extractEndpoints(doc: OpenApiDocument): ApiEndpoint[] {\n const endpoints: ApiEndpoint[] = []\n const validMethods = ['get', 'post', 'put', 'patch', 'delete']\n\n if (!doc.paths) {\n return endpoints\n }\n\n for (const [path, pathItem] of Object.entries(doc.paths)) {\n if (!pathItem || typeof pathItem !== 'object') continue\n\n for (const [method, operation] of Object.entries(pathItem)) {\n if (!validMethods.includes(method.toLowerCase())) continue\n if (!operation || typeof operation !== 'object') continue\n\n const op = operation as any\n\n // Generate operationId if not present\n const operationId = op.operationId || generateOperationId(path, method)\n\n const endpoint: ApiEndpoint = {\n id: operationId,\n operationId,\n method: method.toUpperCase(),\n path,\n summary: op.summary || '',\n description: op.description || op.summary || `${method.toUpperCase()} ${path}`,\n tags: op.tags || [],\n requiredFeatures: op['x-require-features'] || [],\n deprecated: op.deprecated || false,\n parameters: extractParameters(op.parameters || []),\n requestBodySchema: extractRequestBodySchema(op.requestBody, doc.components?.schemas),\n }\n\n endpoints.push(endpoint)\n }\n }\n\n console.error(`[API Index] Parsed ${endpoints.length} endpoints from OpenAPI spec`)\n return endpoints\n}\n\n/**\n * Generate operationId from path and method\n */\nfunction generateOperationId(path: string, method: string): string {\n const pathParts = path\n .replace(/^\\//, '')\n .replace(/\\{([^}]+)\\}/g, 'by_$1')\n .split('/')\n .filter(Boolean)\n .join('_')\n\n return `${method.toLowerCase()}_${pathParts}`\n}\n\n/**\n * Extract parameter info\n */\nfunction extractParameters(params: any[]): ApiParameter[] {\n return params\n .filter((p) => p.in === 'path' || p.in === 'query')\n .map((p) => ({\n name: p.name,\n in: p.in,\n required: p.required ?? false,\n type: p.schema?.type || 'string',\n description: p.description || '',\n }))\n}\n\n/**\n * Extract request body schema (simplified)\n */\nfunction extractRequestBodySchema(\n requestBody: any,\n schemas?: Record<string, any>\n): Record<string, unknown> | null {\n if (!requestBody?.content?.['application/json']?.schema) {\n return null\n }\n\n const schema = requestBody.content['application/json'].schema\n\n // Resolve $ref if present\n if (schema.$ref && schemas) {\n const refPath = schema.$ref.replace('#/components/schemas/', '')\n return schemas[refPath] || schema\n }\n\n return schema\n}\n\n/**\n * Clear endpoint cache (for testing)\n */\nexport function clearEndpointCache(): void {\n endpointsCache = null\n endpointsByOperationId = null\n rawSpecCache = null\n}\n\n/**\n * Extract simplified request body schema for LLM consumption.\n * Returns required fields and basic property info without deep nesting.\n */\nexport function simplifyRequestBodySchema(\n schema: Record<string, unknown> | null\n): { required: string[]; properties: Record<string, { type: string; format?: string; enum?: string[] }> } | null {\n if (!schema) return null\n\n const properties: Record<string, { type: string; format?: string; enum?: string[] }> = {}\n const required: string[] = (schema.required as string[]) || []\n\n const schemaProps = (schema.properties || schema) as Record<string, unknown>\n\n for (const [key, value] of Object.entries(schemaProps)) {\n if (typeof value !== 'object' || value === null) continue\n const propSchema = value as Record<string, unknown>\n\n const prop: { type: string; format?: string; enum?: string[] } = {\n type: (propSchema.type as string) || 'unknown',\n }\n\n if (propSchema.format) prop.format = propSchema.format as string\n if (propSchema.enum && Array.isArray(propSchema.enum)) {\n prop.enum = propSchema.enum.slice(0, 10) as string[] // Limit enum values\n }\n\n properties[key] = prop\n }\n\n return { required, properties }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }