(req: any, res: any)
| 92 | } |
| 93 | |
| 94 | async function handleQuery(req: any, res: any) { |
| 95 | const method = req.method; |
| 96 | const params = method === "POST" ? (req.body || {}) : (req.query || {}); |
| 97 | const { repo, query_type, target, cypher_query, branch, commit } = params; |
| 98 | |
| 99 | if (!query_type || typeof query_type !== "string") { |
| 100 | return res.status(400).json({ |
| 101 | error: "Missing required parameter 'query_type'. Expected: 'definitions', 'callers', 'callees', 'file_structure', or 'cypher'." |
| 102 | }); |
| 103 | } |
| 104 | |
| 105 | const isGlobalTool = query_type === "list_indexed_repositories" || query_type === "search_registry_bundles"; |
| 106 | |
| 107 | if (!isGlobalTool) { |
| 108 | if (!repo || typeof repo !== "string") { |
| 109 | return res.status(400).json({ error: "Missing required parameter 'repo' (owner/repo)." }); |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | const supabaseUrl = process.env.VITE_SUPABASE_URL || process.env.SUPABASE_URL; |
| 114 | const supabaseAnonKey = process.env.VITE_SUPABASE_ANON_KEY || process.env.SUPABASE_ANON_KEY; |
| 115 | |
| 116 | if (!supabaseUrl || !supabaseAnonKey) { |
| 117 | return res.status(500).json({ |
| 118 | error: "Server configuration error: Supabase credentials are not configured on Vercel." |
| 119 | }); |
| 120 | } |
| 121 | |
| 122 | const supabase = createClient(supabaseUrl, supabaseAnonKey); |
| 123 | |
| 124 | const wasmQueries = ["definitions", "callers", "callees", "file_structure", "search", "cypher"]; |
| 125 | const isWasmQuery = wasmQueries.includes(query_type); |
| 126 | |
| 127 | const sessionToken = extractSessionToken(params); |
| 128 | |
| 129 | if (!sessionToken) { |
| 130 | // MUST be 200 — ChatGPT maps non-2xx responses to ClientResponseError and drops the body |
| 131 | return res.status(200).json({ |
| 132 | ...MISSING_SESSION_PAYLOAD, |
| 133 | ...offlineResponse(typeof query_type === "string" ? query_type : "unknown"), |
| 134 | }); |
| 135 | } |
| 136 | |
| 137 | const channelName = `cgc-tunnel-${sessionToken}`; |
| 138 | |
| 139 | const channel = supabase.channel(channelName); |
| 140 | const requestId = Math.random().toString(36).substring(2, 15); |
| 141 | let hasResponded = false; |
| 142 | |
| 143 | // Cleanup helper |
| 144 | const cleanup = () => { |
| 145 | try { supabase.removeChannel(channel); } catch (err) {} |
| 146 | }; |
| 147 | |
| 148 | try { |
| 149 | if (isWasmQuery) { |
| 150 | // ── WASM Query Path (definitions, callers, callees, file_structure, search, cypher) ── |
| 151 | let wasmResponse: any = null; |
no test coverage detected