copilot-gateway 0.10.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.
@@ -0,0 +1,281 @@
1
+ import consola from "consola";
2
+ import fs from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { randomUUID } from "node:crypto";
6
+ import fs$1 from "node:fs";
7
+
8
+ //#region src/lib/paths.ts
9
+ const APP_DIR = path.join(os.homedir(), ".local", "share", "copilot-gateway");
10
+ const GITHUB_TOKEN_PATH = path.join(APP_DIR, "github_token");
11
+ const CONFIG_PATH = path.join(APP_DIR, "config.json");
12
+ const ENTERPRISE_URL_PATH = path.join(APP_DIR, "enterprise_url");
13
+ const PATHS = {
14
+ APP_DIR,
15
+ GITHUB_TOKEN_PATH,
16
+ CONFIG_PATH,
17
+ ENTERPRISE_URL_PATH
18
+ };
19
+ async function ensurePaths() {
20
+ await fs.mkdir(PATHS.APP_DIR, { recursive: true });
21
+ await ensureFile(PATHS.GITHUB_TOKEN_PATH);
22
+ await ensureFile(PATHS.CONFIG_PATH);
23
+ await ensureFile(PATHS.ENTERPRISE_URL_PATH);
24
+ }
25
+ async function ensureFile(filePath) {
26
+ try {
27
+ await fs.access(filePath, fs.constants.W_OK);
28
+ } catch {
29
+ await fs.writeFile(filePath, "");
30
+ await fs.chmod(filePath, 384);
31
+ }
32
+ }
33
+
34
+ //#endregion
35
+ //#region src/lib/state.ts
36
+ const state = {
37
+ accountType: "individual",
38
+ manualApprove: false,
39
+ rateLimitWait: false,
40
+ showToken: false,
41
+ verbose: false,
42
+ enterpriseUrl: void 0
43
+ };
44
+
45
+ //#endregion
46
+ //#region src/lib/url.ts
47
+ function normalizeDomain(url) {
48
+ if (!url) return void 0;
49
+ return url.replace(/^https?:\/\//, "").replace(/\/+$/, "");
50
+ }
51
+ function githubBaseUrl(enterprise) {
52
+ if (!enterprise) return "https://github.com";
53
+ return `https://${normalizeDomain(enterprise)}`;
54
+ }
55
+ function githubApiBaseUrl(enterprise) {
56
+ if (!enterprise) return "https://api.github.com";
57
+ return `https://api.${normalizeDomain(enterprise)}`;
58
+ }
59
+
60
+ //#endregion
61
+ //#region src/lib/api-config.ts
62
+ const standardHeaders = () => ({
63
+ "content-type": "application/json",
64
+ accept: "application/json"
65
+ });
66
+ const COPILOT_VERSION = "0.35.0";
67
+ const EDITOR_PLUGIN_VERSION = `copilot-chat/${COPILOT_VERSION}`;
68
+ const USER_AGENT = `GitHubCopilotChat/${COPILOT_VERSION}`;
69
+ const API_VERSION = "2025-10-01";
70
+ const copilotBaseUrl = (state$1) => {
71
+ if (state$1.enterpriseUrl) return `https://copilot-api.${state$1.enterpriseUrl}`;
72
+ return state$1.accountType === "individual" ? "https://api.githubcopilot.com" : `https://api.${state$1.accountType}.githubcopilot.com`;
73
+ };
74
+ const copilotHeaders = (state$1, vision = false) => {
75
+ const headers = {
76
+ Authorization: `Bearer ${state$1.copilotToken}`,
77
+ "content-type": standardHeaders()["content-type"],
78
+ "copilot-integration-id": "vscode-chat",
79
+ "editor-version": `vscode/${state$1.vsCodeVersion}`,
80
+ "editor-plugin-version": EDITOR_PLUGIN_VERSION,
81
+ "user-agent": USER_AGENT,
82
+ "openai-intent": "conversation-panel",
83
+ "x-github-api-version": API_VERSION,
84
+ "x-request-id": randomUUID(),
85
+ "x-vscode-user-agent-library-version": "electron-fetch"
86
+ };
87
+ if (vision) headers["copilot-vision-request"] = "true";
88
+ return headers;
89
+ };
90
+ const GITHUB_API_BASE_URL = () => githubApiBaseUrl(state.enterpriseUrl);
91
+ const githubHeaders = (state$1) => ({
92
+ ...standardHeaders(),
93
+ authorization: `token ${state$1.githubToken}`,
94
+ "editor-version": `vscode/${state$1.vsCodeVersion}`,
95
+ "editor-plugin-version": EDITOR_PLUGIN_VERSION,
96
+ "user-agent": USER_AGENT,
97
+ "x-github-api-version": API_VERSION,
98
+ "x-vscode-user-agent-library-version": "electron-fetch"
99
+ });
100
+ const GITHUB_BASE_URL = () => githubBaseUrl(state.enterpriseUrl);
101
+ const GITHUB_CLIENT_ID = "Iv1.b507a08c87ecfe98";
102
+ const GITHUB_APP_SCOPES = ["read:user"].join(" ");
103
+
104
+ //#endregion
105
+ //#region src/lib/error.ts
106
+ var HTTPError = class extends Error {
107
+ response;
108
+ constructor(message, response) {
109
+ super(message);
110
+ this.response = response;
111
+ }
112
+ };
113
+ async function forwardError(c, error) {
114
+ consola.error("Error occurred:", error);
115
+ if (error instanceof HTTPError) {
116
+ const errorText = await error.response.text();
117
+ let errorJson;
118
+ try {
119
+ errorJson = JSON.parse(errorText);
120
+ } catch {
121
+ errorJson = errorText;
122
+ }
123
+ consola.error("HTTP error:", errorJson);
124
+ return c.json({ error: {
125
+ message: errorText,
126
+ type: "error"
127
+ } }, error.response.status);
128
+ }
129
+ return c.json({ error: {
130
+ message: error.message,
131
+ type: "error"
132
+ } }, 500);
133
+ }
134
+
135
+ //#endregion
136
+ //#region src/services/copilot/get-models.ts
137
+ const getModels = async () => {
138
+ const response = await fetch(`${copilotBaseUrl(state)}/models`, { headers: copilotHeaders(state) });
139
+ if (!response.ok) throw new HTTPError("Failed to get models", response);
140
+ return await response.json();
141
+ };
142
+
143
+ //#endregion
144
+ //#region src/services/get-vscode-version.ts
145
+ const FALLBACK = "1.107.0";
146
+ async function getVSCodeVersion() {
147
+ const controller = new AbortController();
148
+ const timeout = setTimeout(() => {
149
+ controller.abort();
150
+ }, 5e3);
151
+ try {
152
+ const match = (await (await fetch("https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=visual-studio-code-bin", { signal: controller.signal })).text()).match(/pkgver=([0-9.]+)/);
153
+ if (match) return match[1];
154
+ return FALLBACK;
155
+ } catch {
156
+ return FALLBACK;
157
+ } finally {
158
+ clearTimeout(timeout);
159
+ }
160
+ }
161
+ await getVSCodeVersion();
162
+
163
+ //#endregion
164
+ //#region src/lib/utils.ts
165
+ const sleep = (ms) => new Promise((resolve) => {
166
+ setTimeout(resolve, ms);
167
+ });
168
+ const isNullish = (value) => value === null || value === void 0;
169
+ async function cacheModels() {
170
+ state.models = await getModels();
171
+ }
172
+ const cacheVSCodeVersion = async () => {
173
+ const response = await getVSCodeVersion();
174
+ state.vsCodeVersion = response;
175
+ consola.info(`Using VSCode version: ${response}`);
176
+ };
177
+ const constantTimeEqual = (a, b) => {
178
+ if (a.length !== b.length) return false;
179
+ let result = 0;
180
+ for (let i = 0; i < a.length; i++) result |= (a.codePointAt(i) ?? 0) ^ (b.codePointAt(i) ?? 0);
181
+ return result === 0;
182
+ };
183
+
184
+ //#endregion
185
+ //#region src/services/github/get-copilot-usage.ts
186
+ const getCopilotUsage = async () => {
187
+ const response = await fetch(`${GITHUB_API_BASE_URL()}/copilot_internal/user`, { headers: githubHeaders(state) });
188
+ if (!response.ok) throw new HTTPError("Failed to get Copilot usage", response);
189
+ return await response.json();
190
+ };
191
+
192
+ //#endregion
193
+ //#region src/lib/config.ts
194
+ const gpt5ExplorationPrompt = `## Exploration and reading files
195
+ - **Think first.** Before any tool call, decide ALL files/resources you will need.
196
+ - **Batch everything.** If you need multiple files (even from different places), read them together.
197
+ - **multi_tool_use.parallel** Use multi_tool_use.parallel to parallelize tool calls and only this.
198
+ - **Only make sequential calls if you truly cannot know the next file without seeing a result first.**
199
+ - **Workflow:** (a) plan all needed reads → (b) issue one parallel batch → (c) analyze results → (d) repeat if new, unpredictable reads arise.`;
200
+ const defaultConfig = {
201
+ extraPrompts: {
202
+ "gpt-5-mini": gpt5ExplorationPrompt,
203
+ "gpt-5.1-codex-max": gpt5ExplorationPrompt
204
+ },
205
+ smallModel: "gpt-5-mini",
206
+ modelReasoningEfforts: { "gpt-5-mini": "low" }
207
+ };
208
+ let cachedConfig = null;
209
+ function ensureConfigFile() {
210
+ try {
211
+ fs$1.accessSync(PATHS.CONFIG_PATH, fs$1.constants.R_OK | fs$1.constants.W_OK);
212
+ } catch {
213
+ fs$1.mkdirSync(PATHS.APP_DIR, { recursive: true });
214
+ fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(defaultConfig, null, 2)}\n`, "utf8");
215
+ try {
216
+ fs$1.chmodSync(PATHS.CONFIG_PATH, 384);
217
+ } catch {
218
+ return;
219
+ }
220
+ }
221
+ }
222
+ function readConfigFromDisk() {
223
+ ensureConfigFile();
224
+ try {
225
+ const raw = fs$1.readFileSync(PATHS.CONFIG_PATH, "utf8");
226
+ if (!raw.trim()) {
227
+ fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(defaultConfig, null, 2)}\n`, "utf8");
228
+ return defaultConfig;
229
+ }
230
+ return JSON.parse(raw);
231
+ } catch (error) {
232
+ consola.error("Failed to read config file, using default config", error);
233
+ return defaultConfig;
234
+ }
235
+ }
236
+ function mergeDefaultExtraPrompts(config) {
237
+ const extraPrompts = config.extraPrompts ?? {};
238
+ const defaultExtraPrompts = defaultConfig.extraPrompts ?? {};
239
+ if (Object.keys(defaultExtraPrompts).filter((model) => !Object.hasOwn(extraPrompts, model)).length === 0) return {
240
+ mergedConfig: config,
241
+ changed: false
242
+ };
243
+ return {
244
+ mergedConfig: {
245
+ ...config,
246
+ extraPrompts: {
247
+ ...defaultExtraPrompts,
248
+ ...extraPrompts
249
+ }
250
+ },
251
+ changed: true
252
+ };
253
+ }
254
+ function mergeConfigWithDefaults() {
255
+ const config = readConfigFromDisk();
256
+ const { mergedConfig, changed } = mergeDefaultExtraPrompts(config);
257
+ if (changed) try {
258
+ fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(mergedConfig, null, 2)}\n`, "utf8");
259
+ } catch (writeError) {
260
+ consola.warn("Failed to write merged extraPrompts to config file", writeError);
261
+ }
262
+ cachedConfig = mergedConfig;
263
+ return mergedConfig;
264
+ }
265
+ function getConfig() {
266
+ cachedConfig ??= readConfigFromDisk();
267
+ return cachedConfig;
268
+ }
269
+ function getExtraPromptForModel(model) {
270
+ return getConfig().extraPrompts?.[model] ?? "";
271
+ }
272
+ function getSmallModel() {
273
+ return getConfig().smallModel ?? "gpt-5-mini";
274
+ }
275
+ function getReasoningEffortForModel(model) {
276
+ return getConfig().modelReasoningEfforts?.[model] ?? "high";
277
+ }
278
+
279
+ //#endregion
280
+ export { GITHUB_API_BASE_URL, GITHUB_APP_SCOPES, GITHUB_BASE_URL, GITHUB_CLIENT_ID, HTTPError, PATHS, cacheModels, cacheVSCodeVersion, constantTimeEqual, copilotBaseUrl, copilotHeaders, ensurePaths, forwardError, getCopilotUsage, getExtraPromptForModel, getReasoningEffortForModel, getSmallModel, githubHeaders, isNullish, mergeConfigWithDefaults, sleep, standardHeaders, state };
281
+ //# sourceMappingURL=config-CcK4TXHc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-CcK4TXHc.js","names":["state: State","state","headers: Record<string, string>","errorJson: unknown","defaultConfig: AppConfig","cachedConfig: AppConfig | null","fs"],"sources":["../src/lib/paths.ts","../src/lib/state.ts","../src/lib/url.ts","../src/lib/api-config.ts","../src/lib/error.ts","../src/services/copilot/get-models.ts","../src/services/get-vscode-version.ts","../src/lib/utils.ts","../src/services/github/get-copilot-usage.ts","../src/lib/config.ts"],"sourcesContent":["import fs from \"node:fs/promises\"\nimport os from \"node:os\"\nimport path from \"node:path\"\n\nconst APP_DIR = path.join(os.homedir(), \".local\", \"share\", \"copilot-gateway\")\n\nconst GITHUB_TOKEN_PATH = path.join(APP_DIR, \"github_token\")\nconst CONFIG_PATH = path.join(APP_DIR, \"config.json\")\nconst ENTERPRISE_URL_PATH = path.join(APP_DIR, \"enterprise_url\")\n\nexport const PATHS = {\n APP_DIR,\n GITHUB_TOKEN_PATH,\n CONFIG_PATH,\n ENTERPRISE_URL_PATH,\n}\n\nexport async function ensurePaths(): Promise<void> {\n await fs.mkdir(PATHS.APP_DIR, { recursive: true })\n await ensureFile(PATHS.GITHUB_TOKEN_PATH)\n await ensureFile(PATHS.CONFIG_PATH)\n await ensureFile(PATHS.ENTERPRISE_URL_PATH)\n}\n\nasync function ensureFile(filePath: string): Promise<void> {\n try {\n await fs.access(filePath, fs.constants.W_OK)\n } catch {\n await fs.writeFile(filePath, \"\")\n await fs.chmod(filePath, 0o600)\n }\n}\n","import type { ModelsResponse } from \"~/services/copilot/get-models\"\n\nexport interface State {\n githubToken?: string\n copilotToken?: string\n enterpriseUrl?: string\n\n accountType: string\n models?: ModelsResponse\n vsCodeVersion?: string\n\n manualApprove: boolean\n rateLimitWait: boolean\n showToken: boolean\n\n // Rate limiting configuration\n rateLimitSeconds?: number\n lastRequestTimestamp?: number\n verbose: boolean\n // API key validation\n apiKeys?: Array<string>\n}\n\nexport const state: State = {\n accountType: \"individual\",\n manualApprove: false,\n rateLimitWait: false,\n showToken: false,\n verbose: false,\n enterpriseUrl: undefined,\n}\n","export function normalizeDomain(url: string | undefined): string | undefined {\n if (!url) return undefined\n return url.replace(/^https?:\\/\\//, \"\").replace(/\\/+$/, \"\")\n}\n\nexport function githubBaseUrl(enterprise?: string): string {\n if (!enterprise) return \"https://github.com\"\n const domain = normalizeDomain(enterprise)\n return `https://${domain}`\n}\n\nexport function githubApiBaseUrl(enterprise?: string): string {\n if (!enterprise) return \"https://api.github.com\"\n const domain = normalizeDomain(enterprise)\n return `https://api.${domain}`\n}\n","import { randomUUID } from \"node:crypto\"\n\nimport type { State } from \"./state\"\n\nimport { state } from \"./state\"\nimport { githubBaseUrl, githubApiBaseUrl } from \"./url\"\n\nexport const standardHeaders = () => ({\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n})\n\nconst COPILOT_VERSION = \"0.35.0\"\nconst EDITOR_PLUGIN_VERSION = `copilot-chat/${COPILOT_VERSION}`\nconst USER_AGENT = `GitHubCopilotChat/${COPILOT_VERSION}`\n\nconst API_VERSION = \"2025-10-01\"\n\nexport const copilotBaseUrl = (state: State) => {\n // If enterprise URL is configured, use enterprise Copilot API endpoint\n if (state.enterpriseUrl) {\n return `https://copilot-api.${state.enterpriseUrl}`\n }\n\n // Otherwise use standard GitHub Copilot endpoints based on account type\n return state.accountType === \"individual\" ?\n \"https://api.githubcopilot.com\"\n : `https://api.${state.accountType}.githubcopilot.com`\n}\nexport const copilotHeaders = (state: State, vision: boolean = false) => {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${state.copilotToken}`,\n \"content-type\": standardHeaders()[\"content-type\"],\n \"copilot-integration-id\": \"vscode-chat\",\n \"editor-version\": `vscode/${state.vsCodeVersion}`,\n \"editor-plugin-version\": EDITOR_PLUGIN_VERSION,\n \"user-agent\": USER_AGENT,\n \"openai-intent\": \"conversation-panel\",\n \"x-github-api-version\": API_VERSION,\n \"x-request-id\": randomUUID(),\n \"x-vscode-user-agent-library-version\": \"electron-fetch\",\n }\n\n if (vision) headers[\"copilot-vision-request\"] = \"true\"\n\n return headers\n}\n\nexport const GITHUB_API_BASE_URL = () => githubApiBaseUrl(state.enterpriseUrl)\nexport const githubHeaders = (state: State) => ({\n ...standardHeaders(),\n authorization: `token ${state.githubToken}`,\n \"editor-version\": `vscode/${state.vsCodeVersion}`,\n \"editor-plugin-version\": EDITOR_PLUGIN_VERSION,\n \"user-agent\": USER_AGENT,\n \"x-github-api-version\": API_VERSION,\n \"x-vscode-user-agent-library-version\": \"electron-fetch\",\n})\n\nexport const GITHUB_BASE_URL = () => githubBaseUrl(state.enterpriseUrl)\nexport const GITHUB_CLIENT_ID = \"Iv1.b507a08c87ecfe98\"\nexport const GITHUB_APP_SCOPES = [\"read:user\"].join(\" \")\n","import type { Context } from \"hono\"\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\"\n\nimport consola from \"consola\"\n\nexport class HTTPError extends Error {\n response: Response\n\n constructor(message: string, response: Response) {\n super(message)\n this.response = response\n }\n}\n\nexport async function forwardError(c: Context, error: unknown) {\n consola.error(\"Error occurred:\", error)\n\n if (error instanceof HTTPError) {\n const errorText = await error.response.text()\n let errorJson: unknown\n try {\n errorJson = JSON.parse(errorText)\n } catch {\n errorJson = errorText\n }\n consola.error(\"HTTP error:\", errorJson)\n return c.json(\n {\n error: {\n message: errorText,\n type: \"error\",\n },\n },\n error.response.status as ContentfulStatusCode,\n )\n }\n\n return c.json(\n {\n error: {\n message: (error as Error).message,\n type: \"error\",\n },\n },\n 500,\n )\n}\n","import { copilotBaseUrl, copilotHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\n\nexport const getModels = async () => {\n const response = await fetch(`${copilotBaseUrl(state)}/models`, {\n headers: copilotHeaders(state),\n })\n\n if (!response.ok) throw new HTTPError(\"Failed to get models\", response)\n\n return (await response.json()) as ModelsResponse\n}\n\nexport interface ModelsResponse {\n data: Array<Model>\n object: string\n}\n\ninterface ModelLimits {\n max_context_window_tokens?: number\n max_output_tokens?: number\n max_prompt_tokens?: number\n max_inputs?: number\n}\n\ninterface ModelSupports {\n max_thinking_budget?: number\n min_thinking_budget?: number\n tool_calls?: boolean\n parallel_tool_calls?: boolean\n dimensions?: boolean\n streaming?: boolean\n structured_outputs?: boolean\n vision?: boolean\n}\n\ninterface ModelCapabilities {\n family: string\n limits: ModelLimits\n object: string\n supports: ModelSupports\n tokenizer: string\n type: string\n}\n\nexport interface Model {\n capabilities: ModelCapabilities\n id: string\n model_picker_enabled: boolean\n name: string\n object: string\n preview: boolean\n vendor: string\n version: string\n policy?: {\n state: string\n terms: string\n }\n supported_endpoints?: Array<string>\n}\n","const FALLBACK = \"1.107.0\"\n\nexport async function getVSCodeVersion() {\n const controller = new AbortController()\n const timeout = setTimeout(() => {\n controller.abort()\n }, 5000)\n\n try {\n const response = await fetch(\n \"https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=visual-studio-code-bin\",\n {\n signal: controller.signal,\n },\n )\n\n const pkgbuild = await response.text()\n const pkgverRegex = /pkgver=([0-9.]+)/\n const match = pkgbuild.match(pkgverRegex)\n\n if (match) {\n return match[1]\n }\n\n return FALLBACK\n } catch {\n return FALLBACK\n } finally {\n clearTimeout(timeout)\n }\n}\n\nawait getVSCodeVersion()\n","import consola from \"consola\"\n\nimport { getModels } from \"~/services/copilot/get-models\"\nimport { getVSCodeVersion } from \"~/services/get-vscode-version\"\n\nimport { state } from \"./state\"\n\nexport const sleep = (ms: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms)\n })\n\nexport const isNullish = (value: unknown): value is null | undefined =>\n value === null || value === undefined\n\nexport async function cacheModels(): Promise<void> {\n const models = await getModels()\n state.models = models\n}\n\nexport const cacheVSCodeVersion = async () => {\n const response = await getVSCodeVersion()\n state.vsCodeVersion = response\n\n consola.info(`Using VSCode version: ${response}`)\n}\n\nexport const constantTimeEqual = (a: string, b: string): boolean => {\n if (a.length !== b.length) {\n return false\n }\n let result = 0\n for (let i = 0; i < a.length; i++) {\n result |= (a.codePointAt(i) ?? 0) ^ (b.codePointAt(i) ?? 0)\n }\n return result === 0\n}\n","import { GITHUB_API_BASE_URL, githubHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\n\nexport const getCopilotUsage = async (): Promise<CopilotUsageResponse> => {\n const response = await fetch(\n `${GITHUB_API_BASE_URL()}/copilot_internal/user`,\n {\n headers: githubHeaders(state),\n },\n )\n\n if (!response.ok) {\n throw new HTTPError(\"Failed to get Copilot usage\", response)\n }\n\n return (await response.json()) as CopilotUsageResponse\n}\n\nexport interface QuotaDetail {\n entitlement: number\n overage_count: number\n overage_permitted: boolean\n percent_remaining: number\n quota_id: string\n quota_remaining: number\n remaining: number\n unlimited: boolean\n}\n\ninterface QuotaSnapshots {\n chat: QuotaDetail\n completions: QuotaDetail\n premium_interactions: QuotaDetail\n}\n\ninterface CopilotUsageResponse {\n access_type_sku: string\n analytics_tracking_id: string\n assigned_date: string\n can_signup_for_limited: boolean\n chat_enabled: boolean\n copilot_plan: string\n organization_login_list: Array<unknown>\n organization_list: Array<unknown>\n quota_reset_date: string\n quota_snapshots: QuotaSnapshots\n}\n","import consola from \"consola\"\nimport fs from \"node:fs\"\n\nimport { PATHS } from \"./paths\"\n\nexport interface AppConfig {\n extraPrompts?: Record<string, string>\n smallModel?: string\n modelReasoningEfforts?: Record<\n string,\n \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\"\n >\n}\n\nconst gpt5ExplorationPrompt = `## Exploration and reading files\n- **Think first.** Before any tool call, decide ALL files/resources you will need.\n- **Batch everything.** If you need multiple files (even from different places), read them together.\n- **multi_tool_use.parallel** Use multi_tool_use.parallel to parallelize tool calls and only this.\n- **Only make sequential calls if you truly cannot know the next file without seeing a result first.**\n- **Workflow:** (a) plan all needed reads → (b) issue one parallel batch → (c) analyze results → (d) repeat if new, unpredictable reads arise.`\n\nconst defaultConfig: AppConfig = {\n extraPrompts: {\n \"gpt-5-mini\": gpt5ExplorationPrompt,\n \"gpt-5.1-codex-max\": gpt5ExplorationPrompt,\n },\n smallModel: \"gpt-5-mini\",\n modelReasoningEfforts: {\n \"gpt-5-mini\": \"low\",\n },\n}\n\nlet cachedConfig: AppConfig | null = null\n\nfunction ensureConfigFile(): void {\n try {\n fs.accessSync(PATHS.CONFIG_PATH, fs.constants.R_OK | fs.constants.W_OK)\n } catch {\n fs.mkdirSync(PATHS.APP_DIR, { recursive: true })\n fs.writeFileSync(\n PATHS.CONFIG_PATH,\n `${JSON.stringify(defaultConfig, null, 2)}\\n`,\n \"utf8\",\n )\n try {\n fs.chmodSync(PATHS.CONFIG_PATH, 0o600)\n } catch {\n return\n }\n }\n}\n\nfunction readConfigFromDisk(): AppConfig {\n ensureConfigFile()\n try {\n const raw = fs.readFileSync(PATHS.CONFIG_PATH, \"utf8\")\n if (!raw.trim()) {\n fs.writeFileSync(\n PATHS.CONFIG_PATH,\n `${JSON.stringify(defaultConfig, null, 2)}\\n`,\n \"utf8\",\n )\n return defaultConfig\n }\n return JSON.parse(raw) as AppConfig\n } catch (error) {\n consola.error(\"Failed to read config file, using default config\", error)\n return defaultConfig\n }\n}\n\nfunction mergeDefaultExtraPrompts(config: AppConfig): {\n mergedConfig: AppConfig\n changed: boolean\n} {\n const extraPrompts = config.extraPrompts ?? {}\n const defaultExtraPrompts = defaultConfig.extraPrompts ?? {}\n\n const missingExtraPromptModels = Object.keys(defaultExtraPrompts).filter(\n (model) => !Object.hasOwn(extraPrompts, model),\n )\n\n if (missingExtraPromptModels.length === 0) {\n return { mergedConfig: config, changed: false }\n }\n\n return {\n mergedConfig: {\n ...config,\n extraPrompts: {\n ...defaultExtraPrompts,\n ...extraPrompts,\n },\n },\n changed: true,\n }\n}\n\nexport function mergeConfigWithDefaults(): AppConfig {\n const config = readConfigFromDisk()\n const { mergedConfig, changed } = mergeDefaultExtraPrompts(config)\n\n if (changed) {\n try {\n fs.writeFileSync(\n PATHS.CONFIG_PATH,\n `${JSON.stringify(mergedConfig, null, 2)}\\n`,\n \"utf8\",\n )\n } catch (writeError) {\n consola.warn(\n \"Failed to write merged extraPrompts to config file\",\n writeError,\n )\n }\n }\n\n cachedConfig = mergedConfig\n return mergedConfig\n}\n\nexport function getConfig(): AppConfig {\n cachedConfig ??= readConfigFromDisk()\n return cachedConfig\n}\n\nexport function getExtraPromptForModel(model: string): string {\n const config = getConfig()\n return config.extraPrompts?.[model] ?? \"\"\n}\n\nexport function getSmallModel(): string {\n const config = getConfig()\n return config.smallModel ?? \"gpt-5-mini\"\n}\n\nexport function getReasoningEffortForModel(\n model: string,\n): \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" {\n const config = getConfig()\n return config.modelReasoningEfforts?.[model] ?? \"high\"\n}\n"],"mappings":"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"}