import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { readFileSync, existsSync } from "node:fs"; import { join } from "node:path"; const BASE_URL = "http://exacode-chat.lge.com/v1"; // The EXACODE gateway routes requests by the `X-Title` header. // - "EXACODE Agent Roo" -> the official VS Code extension route; gated by a // client-version check (blocks curl/SDKs/outdated builds). // - "EXACODE SWE(API)" -> the API-client route; the intended tier for // programmatic access. Requires an SWE(API)-scoped bearer token, which // lives in `.env` as EXACODE_API_KEY (different from the Roo Code JWT). // // `X-Model` echoes the requested model id (redundant -- it also travels in the // request body -- but the official example sets it as a default header). const X_TITLE = "EXACODE SWE(API)"; // pi does not auto-load `.env`, so populate process.env ourselves at startup. // Real environment variables take precedence over the file. function loadDotEnv(file: string) { if (!existsSync(file)) return; const text = readFileSync(file, "utf8"); for (const rawLine of text.split(/\r?\n/)) { const line = rawLine.trim(); if (!line || line.startsWith("#")) continue; const eq = line.indexOf("="); if (eq < 0) continue; const key = line.slice(0, eq).trim(); let value = line.slice(eq + 1).trim(); if ( (value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")) ) { value = value.slice(1, -1); } if (key && process.env[key] === undefined) { process.env[key] = value; } } } loadDotEnv(join(process.cwd(), ".env")); const MODELS = [ { id: "Chat-EXACODE-A", name: "Chat-EXACODE-A (EXACODE 3.5)", reasoning: true, input: ["text"] as const, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131072, maxTokens: 8192, headers: { "X-Model": "Chat-EXACODE-A" }, }, ]; export default function (pi: ExtensionAPI) { pi.registerProvider("exacode", { name: "exacode", baseUrl: BASE_URL, apiKey: "$EXACODE_API_KEY", authHeader: true, api: "openai-completions", headers: { "X-Title": X_TITLE, }, models: MODELS, }); }