@juspay/neurolink 12.3.0 → 12.4.0
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.
- package/CHANGELOG.md +2 -2
- package/dist/browser/neurolink.min.js +5 -5
- package/dist/cli/proxy-clients/gemini.d.ts +51 -0
- package/dist/cli/proxy-clients/gemini.js +278 -0
- package/dist/cli/proxy-clients/openCode.d.ts +41 -0
- package/dist/cli/proxy-clients/openCode.js +212 -27
- package/dist/cli/proxy-clients/registry.js +2 -0
- package/dist/cli/proxy-clients/snapshot.d.ts +13 -0
- package/dist/cli/proxy-clients/snapshot.js +18 -0
- package/dist/constants/proxyModels.d.ts +22 -0
- package/dist/constants/proxyModels.js +36 -0
- package/dist/proxy/proxyTranslationEngine.js +3 -22
- package/dist/types/proxyClient.d.ts +33 -0
- package/package.json +1 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini CLI client configurator.
|
|
3
|
+
*
|
|
4
|
+
* Gemini CLI was reachable through the proxy long before this file existed —
|
|
5
|
+
* `GOOGLE_GEMINI_BASE_URL` pointed at the proxy's `/v1beta` door works — but
|
|
6
|
+
* nothing wrote it, so every user had to know that and export it by hand. The
|
|
7
|
+
* proxy already serves the door (`geminiProxyRoutes`, `POST
|
|
8
|
+
* /v1beta/models/{model}:generateContent`); this only closes the onboarding
|
|
9
|
+
* gap.
|
|
10
|
+
*
|
|
11
|
+
* **Why a file and not an env script.** Copilot is configured through
|
|
12
|
+
* `~/.neurolink/copilot-env.sh`, which the user must source from their shell
|
|
13
|
+
* profile. On the machine this was developed against, nothing sourced it: the
|
|
14
|
+
* writer reported success, `applyAllClients` counted it applied, and Copilot
|
|
15
|
+
* had never once used the proxy. A file the CLI reads on its own has no such
|
|
16
|
+
* silent-failure mode. Gemini CLI loads `~/.gemini/.env` (its own error text
|
|
17
|
+
* says "set it in your environment or ~/.gemini/.env"), so that is what this
|
|
18
|
+
* writes.
|
|
19
|
+
*
|
|
20
|
+
* The snapshot lives in `~/.neurolink/`, following `codex.ts`, never inside
|
|
21
|
+
* the file being managed — see `openCode.ts` for what that cost.
|
|
22
|
+
*/
|
|
23
|
+
import type { CliProxyClientConfigurator } from "../../types/index.js";
|
|
24
|
+
/** Gemini CLI's config directory. Fixed; it has no XDG override. */
|
|
25
|
+
declare function getGeminiConfigDir(): string;
|
|
26
|
+
declare function getGeminiEnvPath(): string;
|
|
27
|
+
declare function getGeminiSnapshotPath(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Rewrite the two managed variables, preserving every other line verbatim.
|
|
30
|
+
*
|
|
31
|
+
* A whole-file rewrite would be simpler and wrong: `.env` is the user's file,
|
|
32
|
+
* it may hold unrelated keys for other tools, and comments and ordering are
|
|
33
|
+
* theirs to keep.
|
|
34
|
+
*/
|
|
35
|
+
declare function upsertEnvVars(original: string, vars: Record<string, string>): string;
|
|
36
|
+
/** Remove the managed variables, leaving the rest of the file untouched. */
|
|
37
|
+
declare function removeEnvVars(original: string, keys: string[]): string;
|
|
38
|
+
export declare function setGeminiProxySettings(baseUrl: string, proxyKey?: string): Promise<boolean>;
|
|
39
|
+
export declare function clearGeminiProxySettings(expectedBaseUrl?: string): Promise<boolean>;
|
|
40
|
+
/** Test-only export (CLAUDE.md rule 15 determinism exception). See openCode.ts. */
|
|
41
|
+
export declare const __geminiTestHooks: {
|
|
42
|
+
getGeminiConfigDir: typeof getGeminiConfigDir;
|
|
43
|
+
getGeminiEnvPath: typeof getGeminiEnvPath;
|
|
44
|
+
getGeminiSnapshotPath: typeof getGeminiSnapshotPath;
|
|
45
|
+
setGeminiProxySettings: typeof setGeminiProxySettings;
|
|
46
|
+
clearGeminiProxySettings: typeof clearGeminiProxySettings;
|
|
47
|
+
upsertEnvVars: typeof upsertEnvVars;
|
|
48
|
+
removeEnvVars: typeof removeEnvVars;
|
|
49
|
+
};
|
|
50
|
+
export declare const geminiConfigurator: CliProxyClientConfigurator;
|
|
51
|
+
export {};
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini CLI client configurator.
|
|
3
|
+
*
|
|
4
|
+
* Gemini CLI was reachable through the proxy long before this file existed —
|
|
5
|
+
* `GOOGLE_GEMINI_BASE_URL` pointed at the proxy's `/v1beta` door works — but
|
|
6
|
+
* nothing wrote it, so every user had to know that and export it by hand. The
|
|
7
|
+
* proxy already serves the door (`geminiProxyRoutes`, `POST
|
|
8
|
+
* /v1beta/models/{model}:generateContent`); this only closes the onboarding
|
|
9
|
+
* gap.
|
|
10
|
+
*
|
|
11
|
+
* **Why a file and not an env script.** Copilot is configured through
|
|
12
|
+
* `~/.neurolink/copilot-env.sh`, which the user must source from their shell
|
|
13
|
+
* profile. On the machine this was developed against, nothing sourced it: the
|
|
14
|
+
* writer reported success, `applyAllClients` counted it applied, and Copilot
|
|
15
|
+
* had never once used the proxy. A file the CLI reads on its own has no such
|
|
16
|
+
* silent-failure mode. Gemini CLI loads `~/.gemini/.env` (its own error text
|
|
17
|
+
* says "set it in your environment or ~/.gemini/.env"), so that is what this
|
|
18
|
+
* writes.
|
|
19
|
+
*
|
|
20
|
+
* The snapshot lives in `~/.neurolink/`, following `codex.ts`, never inside
|
|
21
|
+
* the file being managed — see `openCode.ts` for what that cost.
|
|
22
|
+
*/
|
|
23
|
+
import { homedir } from "os";
|
|
24
|
+
import { join } from "path";
|
|
25
|
+
import { logger } from "../../utils/logger.js";
|
|
26
|
+
import { isUsableSnapshot, writeFileAtomic } from "./snapshot.js";
|
|
27
|
+
/** Gemini CLI's config directory. Fixed; it has no XDG override. */
|
|
28
|
+
function getGeminiConfigDir() {
|
|
29
|
+
return join(homedir(), ".gemini");
|
|
30
|
+
}
|
|
31
|
+
function getGeminiEnvPath() {
|
|
32
|
+
return join(getGeminiConfigDir(), ".env");
|
|
33
|
+
}
|
|
34
|
+
function getGeminiSnapshotPath() {
|
|
35
|
+
return join(homedir(), ".neurolink", "gemini-proxy-snapshot.json");
|
|
36
|
+
}
|
|
37
|
+
/** The two variables this writer owns. Every other line is the user's. */
|
|
38
|
+
const BASE_URL_VAR = "GOOGLE_GEMINI_BASE_URL";
|
|
39
|
+
const API_KEY_VAR = "GEMINI_API_KEY";
|
|
40
|
+
/**
|
|
41
|
+
* The proxy's Gemini door needs no real credential — it terminates the
|
|
42
|
+
* client's key and swaps in a pooled account — but Gemini CLI refuses to start
|
|
43
|
+
* in API-key mode without *something* set, exactly as OpenCode's placeholder
|
|
44
|
+
* does.
|
|
45
|
+
*/
|
|
46
|
+
const PLACEHOLDER_KEY = "neurolink-proxy";
|
|
47
|
+
/**
|
|
48
|
+
* Rewrite the two managed variables, preserving every other line verbatim.
|
|
49
|
+
*
|
|
50
|
+
* A whole-file rewrite would be simpler and wrong: `.env` is the user's file,
|
|
51
|
+
* it may hold unrelated keys for other tools, and comments and ordering are
|
|
52
|
+
* theirs to keep.
|
|
53
|
+
*/
|
|
54
|
+
function upsertEnvVars(original, vars) {
|
|
55
|
+
let text = original;
|
|
56
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
57
|
+
// Match an assignment at line start, tolerating `export ` and surrounding
|
|
58
|
+
// spaces. Anchored per-line so a key mentioned inside a comment or another
|
|
59
|
+
// value is not rewritten.
|
|
60
|
+
const re = new RegExp(`^[ \\t]*(?:export[ \\t]+)?${key}[ \\t]*=.*$`, "m");
|
|
61
|
+
const line = `${key}=${value}`;
|
|
62
|
+
// A function replacement, never a string: `String.replace` expands `$&`,
|
|
63
|
+
// `$1` and friends inside a replacement *string*, so a proxy key
|
|
64
|
+
// containing `$&` would be stored as the matched assignment instead of
|
|
65
|
+
// itself. The callback form treats the value as literal.
|
|
66
|
+
text = re.test(text)
|
|
67
|
+
? text.replace(re, () => line)
|
|
68
|
+
: `${text.length > 0 && !text.endsWith("\n") ? `${text}\n` : text}${line}\n`;
|
|
69
|
+
}
|
|
70
|
+
return text;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Read the managed variables' values out of an `.env` body.
|
|
74
|
+
*
|
|
75
|
+
* Restore needs the original *values*, not the original file: replaying a
|
|
76
|
+
* whole snapshot would discard everything the user changed after apply().
|
|
77
|
+
*/
|
|
78
|
+
function readManagedVars(envText) {
|
|
79
|
+
const out = {};
|
|
80
|
+
for (const key of [BASE_URL_VAR, API_KEY_VAR]) {
|
|
81
|
+
const m = new RegExp(`^[ \\t]*(?:export[ \\t]+)?${key}[ \\t]*=(.*)$`, "m").exec(envText);
|
|
82
|
+
if (m) {
|
|
83
|
+
out[key] = m[1] ?? "";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
/** Remove the managed variables, leaving the rest of the file untouched. */
|
|
89
|
+
function removeEnvVars(original, keys) {
|
|
90
|
+
let text = original;
|
|
91
|
+
for (const key of keys) {
|
|
92
|
+
const re = new RegExp(`^[ \\t]*(?:export[ \\t]+)?${key}[ \\t]*=.*(?:\\r?\\n|$)`, "m");
|
|
93
|
+
text = text.replace(re, "");
|
|
94
|
+
}
|
|
95
|
+
return text;
|
|
96
|
+
}
|
|
97
|
+
async function readGeminiSnapshot() {
|
|
98
|
+
const fs = await import("fs");
|
|
99
|
+
let parsed;
|
|
100
|
+
try {
|
|
101
|
+
parsed = JSON.parse(fs.readFileSync(getGeminiSnapshotPath(), "utf8"));
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
// Without `originalEnv` there is nothing to write back, and the restore path
|
|
107
|
+
// would hand `undefined` to writeFileAtomic and throw — which
|
|
108
|
+
// restoreAllClients catches, leaving the user pointed at a dead proxy with
|
|
109
|
+
// no visible failure.
|
|
110
|
+
if (!isUsableSnapshot(parsed, "originalEnv")) {
|
|
111
|
+
logger.debug("[proxy] Gemini: ignoring a malformed snapshot rather than treating it as empty");
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const originalEnv = parsed.originalEnv;
|
|
115
|
+
if (originalEnv !== null && typeof originalEnv !== "string") {
|
|
116
|
+
logger.debug("[proxy] Gemini: snapshot originalEnv has the wrong type");
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
return parsed;
|
|
120
|
+
}
|
|
121
|
+
export async function setGeminiProxySettings(baseUrl, proxyKey) {
|
|
122
|
+
const fs = await import("fs");
|
|
123
|
+
try {
|
|
124
|
+
fs.accessSync(getGeminiConfigDir());
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Gemini CLI not installed. Report the skip rather than creating a config
|
|
128
|
+
// directory for a CLI the user does not have.
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
let original;
|
|
132
|
+
try {
|
|
133
|
+
original = fs.readFileSync(getGeminiEnvPath(), "utf8");
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
original = null;
|
|
137
|
+
}
|
|
138
|
+
// "No usable snapshot" is not the same as "no snapshot file", and the
|
|
139
|
+
// difference is the user's API key. A file that exists but cannot be parsed
|
|
140
|
+
// used to satisfy the existence check, so apply() skipped recording, wrote
|
|
141
|
+
// the placeholder over the real key, and restore later found nothing to put
|
|
142
|
+
// back and simply removed the variable — the key was gone with no record of
|
|
143
|
+
// it anywhere. Refuse to touch .env instead: returning false is the
|
|
144
|
+
// configurator contract for "nothing was written", so the caller reports a
|
|
145
|
+
// skip rather than a success.
|
|
146
|
+
const existingSnapshot = await readGeminiSnapshot();
|
|
147
|
+
if (existingSnapshot === null && fs.existsSync(getGeminiSnapshotPath())) {
|
|
148
|
+
logger.warn("[proxy] Gemini: snapshot file is unreadable; leaving .env untouched rather than overwriting credentials with no way back");
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
// Snapshot once — but only while the existing record still describes the
|
|
152
|
+
// file. If restore ran and could not delete the snapshot, or the user edited
|
|
153
|
+
// a managed variable afterwards, the stored record is stale: reusing it would
|
|
154
|
+
// make the NEXT restore write yesterday's values over today's. Re-capture
|
|
155
|
+
// whenever what is on disk is not what we last wrote.
|
|
156
|
+
const currentManaged = readManagedVars(original ?? "");
|
|
157
|
+
const lastWritten = existingSnapshot?.written;
|
|
158
|
+
const snapshotIsStale = existingSnapshot !== null &&
|
|
159
|
+
(lastWritten === undefined ||
|
|
160
|
+
currentManaged[BASE_URL_VAR] !== lastWritten.baseUrl ||
|
|
161
|
+
currentManaged[API_KEY_VAR] !== lastWritten.apiKey);
|
|
162
|
+
if (snapshotIsStale) {
|
|
163
|
+
logger.debug("[proxy] Gemini: stored snapshot no longer matches .env; re-capturing");
|
|
164
|
+
}
|
|
165
|
+
if (existingSnapshot === null || snapshotIsStale) {
|
|
166
|
+
fs.mkdirSync(join(homedir(), ".neurolink"), { recursive: true });
|
|
167
|
+
// 0o600: a pre-existing .env routinely holds the user's real API key.
|
|
168
|
+
await writeFileAtomic(getGeminiSnapshotPath(), JSON.stringify({
|
|
169
|
+
originalEnv: original,
|
|
170
|
+
written: { baseUrl, apiKey: proxyKey || PLACEHOLDER_KEY },
|
|
171
|
+
}, null, 2), 0o600);
|
|
172
|
+
}
|
|
173
|
+
const next = upsertEnvVars(original ?? "", {
|
|
174
|
+
[BASE_URL_VAR]: baseUrl,
|
|
175
|
+
[API_KEY_VAR]: proxyKey || PLACEHOLDER_KEY,
|
|
176
|
+
});
|
|
177
|
+
// 0o600 on create: this file carries credentials. An existing file keeps its
|
|
178
|
+
// own mode, which is writeFileAtomic's documented behaviour.
|
|
179
|
+
await writeFileAtomic(getGeminiEnvPath(), next, original === null ? 0o600 : undefined);
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
export async function clearGeminiProxySettings(expectedBaseUrl) {
|
|
183
|
+
const fs = await import("fs");
|
|
184
|
+
let current;
|
|
185
|
+
try {
|
|
186
|
+
current = fs.readFileSync(getGeminiEnvPath(), "utf8");
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
const configured = new RegExp(`^[ \\t]*(?:export[ \\t]+)?${BASE_URL_VAR}[ \\t]*=[ \\t]*(.*)$`, "m").exec(current);
|
|
192
|
+
if (!configured) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
if (expectedBaseUrl && configured[1]?.trim() !== expectedBaseUrl) {
|
|
196
|
+
// Pointed somewhere else — the user's choice, not ours to revert.
|
|
197
|
+
logger.debug("[proxy] Gemini clear: base URL is not the one we wrote, leaving it intact");
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
const snapshot = await readGeminiSnapshot();
|
|
201
|
+
if (snapshot === null) {
|
|
202
|
+
// No record of what was here before. Stripping the managed variables looks
|
|
203
|
+
// tidy and is destructive: GEMINI_API_KEY may hold the user's real key,
|
|
204
|
+
// and once removed there is nothing left to restore it from. Leaving a
|
|
205
|
+
// stale base URL behind costs the user a failed request they can diagnose;
|
|
206
|
+
// deleting a credential costs them something they cannot get back. Report
|
|
207
|
+
// the skip instead — false is the contract for "nothing was written".
|
|
208
|
+
logger.warn("[proxy] Gemini clear: no usable snapshot, leaving .env untouched rather than removing variables we cannot restore");
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
if (snapshot.originalEnv === null) {
|
|
212
|
+
// The user had no .env before the proxy created one. Remove it, unless the
|
|
213
|
+
// user has since added lines of their own — then keep theirs.
|
|
214
|
+
const remainder = removeEnvVars(current, [
|
|
215
|
+
BASE_URL_VAR,
|
|
216
|
+
API_KEY_VAR,
|
|
217
|
+
]).trim();
|
|
218
|
+
if (remainder.length === 0) {
|
|
219
|
+
fs.rmSync(getGeminiEnvPath(), { force: true });
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
await writeFileAtomic(getGeminiEnvPath(), `${remainder}\n`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
// Undo our two variables against the CURRENT file rather than writing the
|
|
227
|
+
// snapshot over it. Anything the user changed or added after apply() is
|
|
228
|
+
// theirs and must survive; replaying the whole snapshot would silently
|
|
229
|
+
// discard it. Restoring each managed variable in place also keeps its
|
|
230
|
+
// original position, so an untouched file round-trips byte-for-byte.
|
|
231
|
+
const originalValues = readManagedVars(snapshot.originalEnv);
|
|
232
|
+
let next = current;
|
|
233
|
+
for (const key of [BASE_URL_VAR, API_KEY_VAR]) {
|
|
234
|
+
const originalValue = originalValues[key];
|
|
235
|
+
next =
|
|
236
|
+
originalValue === undefined
|
|
237
|
+
? removeEnvVars(next, [key])
|
|
238
|
+
: upsertEnvVars(next, { [key]: originalValue });
|
|
239
|
+
}
|
|
240
|
+
await writeFileAtomic(getGeminiEnvPath(), next);
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
fs.rmSync(getGeminiSnapshotPath(), { force: true });
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
// Harmless: the next apply() overwrites it.
|
|
247
|
+
}
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
/** Test-only export (CLAUDE.md rule 15 determinism exception). See openCode.ts. */
|
|
251
|
+
export const __geminiTestHooks = {
|
|
252
|
+
getGeminiConfigDir,
|
|
253
|
+
getGeminiEnvPath,
|
|
254
|
+
getGeminiSnapshotPath,
|
|
255
|
+
setGeminiProxySettings,
|
|
256
|
+
clearGeminiProxySettings,
|
|
257
|
+
upsertEnvVars,
|
|
258
|
+
removeEnvVars,
|
|
259
|
+
};
|
|
260
|
+
export const geminiConfigurator = {
|
|
261
|
+
id: "gemini-cli",
|
|
262
|
+
displayName: "Gemini CLI",
|
|
263
|
+
detect: async () => {
|
|
264
|
+
const fs = await import("fs");
|
|
265
|
+
try {
|
|
266
|
+
fs.accessSync(getGeminiConfigDir());
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
// Gemini CLI appends `/v1beta/models/...` itself, so it takes the bare
|
|
274
|
+
// origin — unlike OpenCode and Qwen, which need the `/v1` suffix.
|
|
275
|
+
apply: (proxyBaseUrl) => setGeminiProxySettings(proxyBaseUrl),
|
|
276
|
+
restore: (proxyBaseUrl) => clearGeminiProxySettings(proxyBaseUrl),
|
|
277
|
+
};
|
|
278
|
+
//# sourceMappingURL=gemini.js.map
|
|
@@ -3,10 +3,50 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Moved verbatim out of `proxy.ts` so that adding a CLI means adding a file
|
|
5
5
|
* here rather than editing a 5,000-line command module in seven places.
|
|
6
|
+
*
|
|
7
|
+
* Two defects made every config this writer produced unusable, and both are
|
|
8
|
+
* fixed here. They are recorded because each was invisible to the tests that
|
|
9
|
+
* were supposed to cover this file.
|
|
10
|
+
*
|
|
11
|
+
* 1. The snapshot lived in `opencode.json` itself, under two `__proxy_*` keys
|
|
12
|
+
* at the top level. OpenCode validates its config against a closed schema
|
|
13
|
+
* and rejects unknown top-level keys outright:
|
|
14
|
+
*
|
|
15
|
+
* Error: Configuration is invalid at ~/.config/opencode/opencode.json
|
|
16
|
+
* ↳ Unrecognized keys: "__proxy_original_neurolink", "__proxy_written_neurolink"
|
|
17
|
+
*
|
|
18
|
+
* Every `opencode` invocation failed at startup — not just proxied ones —
|
|
19
|
+
* so auto-configuration bricked the CLI it was meant to onboard. The
|
|
20
|
+
* snapshot now lives beside Codex's, in `~/.neurolink/`, which is what
|
|
21
|
+
* `codex.ts` has always done. Claude Code and Qwen embed a snapshot the
|
|
22
|
+
* same way and survive it only because their schemas ignore unknown keys;
|
|
23
|
+
* that is tolerance, not permission, and new writers should not rely on it.
|
|
24
|
+
*
|
|
25
|
+
* 2. `models` was written as `{}`. OpenCode resolves `--model provider/id`
|
|
26
|
+
* against that map and never calls `/v1/models`, so an empty map meant
|
|
27
|
+
* every id was unknown:
|
|
28
|
+
*
|
|
29
|
+
* ProviderModelNotFoundError: providerID "neurolink", suggestions: []
|
|
30
|
+
*
|
|
31
|
+
* Fixing only the keys exposed this one immediately underneath.
|
|
32
|
+
*
|
|
33
|
+
* Configs written by the previous version are repaired in place: both apply()
|
|
34
|
+
* and restore() adopt a legacy in-file snapshot before deleting the keys, so
|
|
35
|
+
* an existing broken config heals on the next `proxy start` without losing the
|
|
36
|
+
* user's original provider block.
|
|
6
37
|
*/
|
|
7
38
|
import type { CliProxyClientConfigurator } from "../../types/index.js";
|
|
8
39
|
declare function getOpenCodeConfigDir(): string;
|
|
9
40
|
declare function getOpenCodeConfigPath(): string;
|
|
41
|
+
/**
|
|
42
|
+
* Where the snapshot of the user's pre-existing `provider.neurolink` lives.
|
|
43
|
+
*
|
|
44
|
+
* Outside `opencode.json`, for the reason in the file header. Persisting it on
|
|
45
|
+
* disk (rather than in process memory) means restoration still works when the
|
|
46
|
+
* proxy crashes or shutdown runs in a different process — the property the
|
|
47
|
+
* in-file version was reaching for.
|
|
48
|
+
*/
|
|
49
|
+
declare function getOpenCodeSnapshotPath(): string;
|
|
10
50
|
export declare function setOpenCodeProxySettings(baseUrl: string, proxyKey?: string): Promise<boolean>;
|
|
11
51
|
export declare function clearOpenCodeProxySettings(expectedBaseUrl?: string): Promise<boolean>;
|
|
12
52
|
/**
|
|
@@ -19,6 +59,7 @@ export declare function clearOpenCodeProxySettings(expectedBaseUrl?: string): Pr
|
|
|
19
59
|
export declare const __openCodeTestHooks: {
|
|
20
60
|
getOpenCodeConfigDir: typeof getOpenCodeConfigDir;
|
|
21
61
|
getOpenCodeConfigPath: typeof getOpenCodeConfigPath;
|
|
62
|
+
getOpenCodeSnapshotPath: typeof getOpenCodeSnapshotPath;
|
|
22
63
|
setOpenCodeProxySettings: typeof setOpenCodeProxySettings;
|
|
23
64
|
clearOpenCodeProxySettings: typeof clearOpenCodeProxySettings;
|
|
24
65
|
};
|