@juspay/neurolink
Version:
The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002
293 lines • 13.4 kB
JavaScript
/**
* Codex (ChatGPT) client configurator.
*
* Moved verbatim out of `proxy.ts`. Codex is the writer the other two should
* have copied: it probes for an installed CLI and reports whether it wrote.
*/
import { homedir } from "os";
import { join } from "path";
import { logger } from "../../utils/logger.js";
import { writeFileAtomic } from "./snapshot.js";
import { dottedTableIds, findDuplicateTable, ownedLineCount, readStringValue, splitTomlSections, } from "./tomlSections.js";
//
// Points the Codex CLI at the proxy by managing `~/.codex/config.toml`:
// - appends a marker-delimited `[model_providers.neurolink]` table
// - flips the top-level `model_provider` to "neurolink"
// The original `model_provider` value is snapshotted to a sidecar JSON so the
// restore works even across process crashes. All edits are guarded and wrapped
// so a failure never aborts proxy start/stop. Codex talks the Responses API to
// the proxy's /backend-api/codex path.
/**
* Resolved per call rather than at module load so `detect()` and `apply()`
* agree when HOME changes — under test, and on the `--dev` isolation path.
*/
function getCodexConfigPath() {
return join(homedir(), ".codex", "config.toml");
}
function getCodexSnapshotPath() {
return join(homedir(), ".neurolink", "codex-proxy-snapshot.json");
}
const CODEX_BLOCK_BEGIN = "# >>> neurolink-proxy (managed) >>>";
const CODEX_BLOCK_END = "# <<< neurolink-proxy (managed) <<<";
const CODEX_MANAGED_COMMENT_LINES = new Set([
CODEX_BLOCK_BEGIN,
CODEX_BLOCK_END,
]);
const CODEX_PROVIDER_ID = "neurolink";
const CODEX_PROVIDER_NAME = "NeuroLink Proxy";
const CODEX_PROVIDER_LINE_RE = /^[ \t]*model_provider[ \t]*=.*$/m;
const CODEX_MODEL_LINE_RE = /^[ \t]*model[ \t]*=.*$/m;
const CODEX_MANAGED_SELECTOR_RE = /^[ \t]*model_provider[ \t]*=[ \t]*"neurolink"[ \t]*$\n?/m;
/**
* What of a Codex config.toml the proxy owns, found by content rather than
* by the block markers. Codex's own saves keep comments (measured on
* codex-cli 0.155.1), but a hand edit or another tool need not, and a
* marker-only writer then appended a second `[model_providers.neurolink]`,
* which Codex cannot parse. The proxy's provider is that table carrying the
* name the proxy writes, with any sub-tables under it. `foreign` is set when
* a `neurolink` provider exists that the proxy did not write: another name, a
* dotted-key or array definition, or sub-tables with no provider table, which
* the proxy never writes. Writing ours beside it would declare the table twice
* or adopt the user's keys. `remainder` keeps every byte the proxy does not
* own, including comments the user wrote after its table.
*/
function inspectCodexConfig(text) {
const sections = splitTomlSections(text);
const isProvider = (section) => section.path !== null &&
section.path.length >= 2 &&
section.path[0] === "model_providers" &&
section.path[1] === CODEX_PROVIDER_ID;
const tables = sections.filter((section) => isProvider(section) && !section.arrayTable && section.path?.length === 2);
const ours = (section) => readStringValue(section, "name") === CODEX_PROVIDER_NAME;
const foreign = tables.some((table) => !ours(table)) ||
(tables.length === 0 && sections.some(isProvider)) ||
sections.some((section) => isProvider(section) && section.arrayTable) ||
dottedTableIds(sections, "model_providers").includes(CODEX_PROVIDER_ID);
const owned = sections.map((section) => isProvider(section) &&
!section.arrayTable &&
(section.path?.length === 2 ? ours(section) : !foreign));
const managedBaseUrls = [
...new Set(sections.flatMap((section, index) => {
const url = owned[index]
? readStringValue(section, "base_url")
: undefined;
return url === undefined ? [] : [url];
})),
];
const remainder = sections
.flatMap((section, index) => owned[index]
? section.lines.slice(ownedLineCount(section, CODEX_MANAGED_COMMENT_LINES))
: section.lines)
.filter((line) => !CODEX_MANAGED_COMMENT_LINES.has(line.trim()))
.join("");
return { remainder, managedBaseUrls, foreign };
}
/**
* Remove the selector the proxy injected. Only the preamble holds top-level
* keys: a profile's own `model_provider = "neurolink"` is the user's.
*/
function stripCodexSelector(text) {
return editTomlPreamble(text, (preamble) => CODEX_MANAGED_SELECTOR_RE.test(preamble)
? preamble.replace(CODEX_MANAGED_SELECTOR_RE, "")
: null);
}
/**
* Apply a top-level TOML key edit, confined to the document preamble.
*
* In TOML every key after a `[table]` header belongs to that table. A
* document-wide regex therefore happily rewrites `model_provider` inside
* `[model_providers.foo]`, which sets a table key instead of the top-level
* selector: Codex keeps using its original provider while the command reports
* success. Only the text before the first header can hold top-level keys.
*/
function editTomlPreamble(text, edit) {
const headerMatch = /^[ \t]*\[/m.exec(text);
const boundary = headerMatch ? headerMatch.index : text.length;
const preamble = text.slice(0, boundary);
const edited = edit(preamble);
return edited === null ? text : edited + text.slice(boundary);
}
function buildCodexProviderBlock(baseUrl) {
return [
CODEX_BLOCK_BEGIN,
`[model_providers.${CODEX_PROVIDER_ID}]`,
`name = "${CODEX_PROVIDER_NAME}"`,
`base_url = "${baseUrl}/backend-api/codex"`,
'wire_api = "responses"',
"requires_openai_auth = true",
CODEX_BLOCK_END,
"",
].join("\n");
}
export async function setCodexProxySettings(baseUrl) {
try {
const fs = await import("fs");
if (!fs.existsSync(getCodexConfigPath())) {
// Codex not installed / never configured — skip silently.
return false;
}
const original = fs.readFileSync(getCodexConfigPath(), "utf8");
const inspected = inspectCodexConfig(original);
if (inspected.foreign) {
logger.warn("[proxy] Codex: config.toml already defines a neurolink provider the proxy did not write; leaving it untouched");
return false;
}
let text = stripCodexSelector(inspected.remainder);
// Set the selector: replace an existing top-level model_provider or insert
// one right after the top-level `model = ...` line (stays before any table).
let selectorPlaced = false;
text = editTomlPreamble(text, (preamble) => {
if (CODEX_PROVIDER_LINE_RE.test(preamble)) {
selectorPlaced = true;
return preamble.replace(CODEX_PROVIDER_LINE_RE, 'model_provider = "neurolink"');
}
if (CODEX_MODEL_LINE_RE.test(preamble)) {
selectorPlaced = true;
return preamble.replace(CODEX_MODEL_LINE_RE, (line) => `${line}\nmodel_provider = "neurolink"`);
}
return null;
});
if (!selectorPlaced) {
text = `model_provider = "neurolink"\n${text}`;
}
const next = `${text.replace(/\s*$/, "\n")}\n${buildCodexProviderBlock(baseUrl)}`;
// Codex will not load a config that declares a table twice. Writing into
// one would only bury the user's own error under ours.
const duplicate = findDuplicateTable(next);
if (duplicate !== undefined) {
logger.warn(`[proxy] Codex: config.toml declares [${duplicate}] more than once; leaving it untouched`);
return false;
}
// Same preamble boundary the edit paths use. Matching document-wide would
// capture a `model_provider` belonging to a table — a legacy
// `[profiles.<name>]` block, say — and the restore would then write that
// profile's provider back as the top-level selector.
let providerMatch = null;
editTomlPreamble(original, (preamble) => {
providerMatch = preamble.match(CODEX_PROVIDER_LINE_RE);
return null;
});
// A selector naming "neurolink" is the one the proxy writes, possibly left
// by an earlier run, and never the user's choice.
const userProviderLine = providerMatch && !/"neurolink"/.test(providerMatch[0])
? providerMatch[0]
: null;
const selectorIsOurs = providerMatch !== null && userProviderLine === null;
// Snapshot on first touch, and again whenever the top-level selector is
// not the proxy's. An unclean exit skips restore and leaves the snapshot
// behind; a provider the user then picks by hand, or a selector they
// remove, is newer than that snapshot and is what restore must bring back.
if (!fs.existsSync(getCodexSnapshotPath()) || !selectorIsOurs) {
fs.mkdirSync(join(homedir(), ".neurolink"), { recursive: true });
await writeFileAtomic(getCodexSnapshotPath(), JSON.stringify({ originalProviderLine: userProviderLine }, null, 2), 0o600);
}
// Every proxy start applies. Rewriting a file that already matches only
// widens the window for racing Codex's own saves.
if (next !== original) {
await writeFileAtomic(getCodexConfigPath(), next);
}
return true;
}
catch (error) {
logger.debug(`[proxy] Codex client config not updated: ${error instanceof Error ? error.message : String(error)}`);
return false;
}
}
export async function clearCodexProxySettings(expectedBaseUrl) {
try {
const fs = await import("fs");
if (!fs.existsSync(getCodexConfigPath())) {
return false;
}
const original = fs.readFileSync(getCodexConfigPath(), "utf8");
const inspected = inspectCodexConfig(original);
// Apply refuses such a config, so the selector pointing at the user's own
// provider is theirs as well.
if (inspected.foreign) {
return false;
}
// The managed table records its owner in `base_url`. Without this check a
// second proxy shutting down on another port would strip the table that
// the still-running proxy installed, and restore the snapshot selector on
// top — silently taking Codex off the live proxy. Mirrors the Claude and
// OpenCode clear paths.
if (expectedBaseUrl &&
inspected.managedBaseUrls.length > 0 &&
!inspected.managedBaseUrls.includes(`${expectedBaseUrl}/backend-api/codex`)) {
return false;
}
let text = stripCodexSelector(inspected.remainder);
// Restore the user's original selector line if we snapshotted one.
let restored = null;
if (fs.existsSync(getCodexSnapshotPath())) {
try {
const snap = JSON.parse(fs.readFileSync(getCodexSnapshotPath(), "utf8"));
restored = snap.originalProviderLine ?? null;
}
catch {
restored = null;
}
}
if (restored) {
const restoredLine = restored;
let restorePlaced = false;
text = editTomlPreamble(text, (preamble) => {
if (CODEX_PROVIDER_LINE_RE.test(preamble)) {
restorePlaced = true;
return preamble.replace(CODEX_PROVIDER_LINE_RE, restoredLine);
}
if (CODEX_MODEL_LINE_RE.test(preamble)) {
restorePlaced = true;
return preamble.replace(CODEX_MODEL_LINE_RE, (line) => `${line}\n${restoredLine}`);
}
return null;
});
if (!restorePlaced) {
text = `${restoredLine}\n${text}`;
}
}
if (text === original) {
// Nothing managed remains, so the snapshot can no longer describe the
// user's current selector. Keeping it would let a later clear restore a
// value the user has since changed by hand.
try {
fs.rmSync(getCodexSnapshotPath(), { force: true });
}
catch {
// best-effort
}
return false;
}
await writeFileAtomic(getCodexConfigPath(), text.replace(/\s*$/, "\n"));
try {
fs.rmSync(getCodexSnapshotPath(), { force: true });
}
catch {
// best-effort
}
return true;
}
catch (error) {
logger.debug(`[proxy] Codex client config not cleared: ${error instanceof Error ? error.message : String(error)}`);
return false;
}
}
export const codexConfigurator = {
id: "codex",
displayName: "Codex",
detect: async () => {
const fs = await import("fs");
return fs.existsSync(getCodexConfigPath());
},
// setCodexProxySettings appends "/backend-api/codex" itself, so this takes
// the bare origin.
apply: (proxyBaseUrl) => setCodexProxySettings(proxyBaseUrl),
restore: (proxyBaseUrl) => clearCodexProxySettings(proxyBaseUrl),
};
export const __codexClientTestHooks = {
getCodexConfigPath,
getCodexSnapshotPath,
setCodexProxySettings,
clearCodexProxySettings,
};
//# sourceMappingURL=codex.js.map