@ashsec/copilot-api 0.7.0 → 0.7.2

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/dist/main.js CHANGED
@@ -22,10 +22,12 @@ import { streamSSE } from "hono/streaming";
22
22
  const APP_DIR = path.join(os.homedir(), ".local", "share", "copilot-api");
23
23
  const GITHUB_TOKEN_PATH = path.join(APP_DIR, "github_token");
24
24
  const AZURE_OPENAI_CONFIG_PATH = path.join(APP_DIR, "azure_openai_config");
25
+ const REPLACEMENTS_CONFIG_PATH = path.join(APP_DIR, "replacements.json");
25
26
  const PATHS = {
26
27
  APP_DIR,
27
28
  GITHUB_TOKEN_PATH,
28
- AZURE_OPENAI_CONFIG_PATH
29
+ AZURE_OPENAI_CONFIG_PATH,
30
+ REPLACEMENTS_CONFIG_PATH
29
31
  };
30
32
  async function ensurePaths() {
31
33
  await fs.mkdir(PATHS.APP_DIR, { recursive: true });
@@ -164,15 +166,15 @@ async function loadAzureOpenAIConfig() {
164
166
  const content = await fs.readFile(PATHS.AZURE_OPENAI_CONFIG_PATH, "utf8");
165
167
  if (!content.trim()) return null;
166
168
  const decoded = Buffer.from(content.trim(), "base64").toString("utf8");
167
- const config = JSON.parse(decoded);
168
- if (!config.endpoint || !config.apiKey) return null;
169
- return config;
169
+ const config$1 = JSON.parse(decoded);
170
+ if (!config$1.endpoint || !config$1.apiKey) return null;
171
+ return config$1;
170
172
  } catch {
171
173
  return null;
172
174
  }
173
175
  }
174
- async function saveAzureOpenAIConfig(config) {
175
- const encoded = Buffer.from(JSON.stringify(config)).toString("base64");
176
+ async function saveAzureOpenAIConfig(config$1) {
177
+ const encoded = Buffer.from(JSON.stringify(config$1)).toString("base64");
176
178
  await fs.writeFile(PATHS.AZURE_OPENAI_CONFIG_PATH, encoded, "utf8");
177
179
  await fs.chmod(PATHS.AZURE_OPENAI_CONFIG_PATH, 384);
178
180
  consola.success("Azure OpenAI configuration saved");
@@ -192,12 +194,12 @@ async function promptAzureOpenAISetup() {
192
194
  consola.warn("No API key provided, skipping Azure OpenAI setup");
193
195
  return null;
194
196
  }
195
- const config = {
197
+ const config$1 = {
196
198
  endpoint: endpoint.trim().replace(/\/$/, ""),
197
199
  apiKey: apiKey.trim()
198
200
  };
199
- await saveAzureOpenAIConfig(config);
200
- return config;
201
+ await saveAzureOpenAIConfig(config$1);
202
+ return config$1;
201
203
  }
202
204
  function isAzureOpenAIModel(modelId) {
203
205
  return modelId.startsWith(AZURE_OPENAI_MODEL_PREFIX);
@@ -209,7 +211,7 @@ function getAzureDeploymentName(modelId) {
209
211
  //#endregion
210
212
  //#region src/services/azure-openai/create-chat-completions.ts
211
213
  const AZURE_API_VERSION = "2024-10-21";
212
- async function createAzureOpenAIChatCompletions(config, payload) {
214
+ async function createAzureOpenAIChatCompletions(config$1, payload) {
213
215
  const deploymentName = getAzureDeploymentName(payload.model);
214
216
  const { max_tokens,...restPayload } = payload;
215
217
  const azurePayload = {
@@ -217,10 +219,10 @@ async function createAzureOpenAIChatCompletions(config, payload) {
217
219
  model: deploymentName,
218
220
  ...max_tokens != null && { max_completion_tokens: max_tokens }
219
221
  };
220
- const response = await fetch(`${config.endpoint}/openai/deployments/${deploymentName}/chat/completions?api-version=${AZURE_API_VERSION}`, {
222
+ const response = await fetch(`${config$1.endpoint}/openai/deployments/${deploymentName}/chat/completions?api-version=${AZURE_API_VERSION}`, {
221
223
  method: "POST",
222
224
  headers: {
223
- "api-key": config.apiKey,
225
+ "api-key": config$1.apiKey,
224
226
  "Content-Type": "application/json"
225
227
  },
226
228
  body: JSON.stringify(azurePayload)
@@ -236,10 +238,10 @@ async function createAzureOpenAIChatCompletions(config, payload) {
236
238
  //#endregion
237
239
  //#region src/services/azure-openai/get-models.ts
238
240
  const AZURE_DEPLOYMENTS_API_VERSION = "2022-12-01";
239
- async function getAzureOpenAIDeployments(config) {
241
+ async function getAzureOpenAIDeployments(config$1) {
240
242
  try {
241
- const response = await fetch(`${config.endpoint}/openai/deployments?api-version=${AZURE_DEPLOYMENTS_API_VERSION}`, { headers: {
242
- "api-key": config.apiKey,
243
+ const response = await fetch(`${config$1.endpoint}/openai/deployments?api-version=${AZURE_DEPLOYMENTS_API_VERSION}`, { headers: {
244
+ "api-key": config$1.apiKey,
243
245
  "Content-Type": "application/json"
244
246
  } });
245
247
  if (!response.ok) {
@@ -265,8 +267,20 @@ async function getAzureOpenAIDeployments(config) {
265
267
  //#endregion
266
268
  //#region src/services/copilot/get-models.ts
267
269
  const getModels = async () => {
268
- const response = await fetch(`${copilotBaseUrl(state)}/models`, { headers: copilotHeaders(state) });
269
- if (!response.ok) throw new HTTPError("Failed to get models", response);
270
+ const url = `${copilotBaseUrl(state)}/models`;
271
+ const response = await fetch(url, { headers: copilotHeaders(state) });
272
+ if (!response.ok) {
273
+ const errorBody = await response.text();
274
+ let errorDetails;
275
+ try {
276
+ const parsed = JSON.parse(errorBody);
277
+ errorDetails = JSON.stringify(parsed, null, 2);
278
+ } catch {
279
+ errorDetails = errorBody || "(empty response)";
280
+ }
281
+ consola.error(`Failed to get models from ${url}\nStatus: ${response.status} ${response.statusText}\nResponse: ${errorDetails}`);
282
+ throw new HTTPError(`Failed to get models: ${response.status} ${response.statusText}`, response);
283
+ }
270
284
  return await response.json();
271
285
  };
272
286
 
@@ -297,7 +311,16 @@ const sleep = (ms) => new Promise((resolve) => {
297
311
  });
298
312
  const isNullish = (value) => value === null || value === void 0;
299
313
  async function cacheModels() {
300
- state.models = await getModels();
314
+ try {
315
+ state.models = await getModels();
316
+ } catch (error) {
317
+ consola.error("Failed to fetch and cache models. This could be due to:");
318
+ consola.error(" - Invalid or expired Copilot token");
319
+ consola.error(" - Network connectivity issues");
320
+ consola.error(" - GitHub Copilot service unavailable");
321
+ consola.error(" - Account type mismatch (try --account-type=individual or --account-type=business)");
322
+ throw error;
323
+ }
301
324
  }
302
325
  const cacheVSCodeVersion = async () => {
303
326
  const response = await getVSCodeVersion();
@@ -305,16 +328,16 @@ const cacheVSCodeVersion = async () => {
305
328
  consola.info(`Using VSCode version: ${response}`);
306
329
  };
307
330
  async function setupAzureOpenAI() {
308
- let config = await loadAzureOpenAIConfig();
309
- if (!config) config = await promptAzureOpenAISetup();
310
- if (!config) {
331
+ let config$1 = await loadAzureOpenAIConfig();
332
+ if (!config$1) config$1 = await promptAzureOpenAISetup();
333
+ if (!config$1) {
311
334
  consola.info("Azure OpenAI not configured");
312
335
  return;
313
336
  }
314
- state.azureOpenAIConfig = config;
337
+ state.azureOpenAIConfig = config$1;
315
338
  consola.info("Azure OpenAI configuration loaded");
316
339
  try {
317
- const deployments = await getAzureOpenAIDeployments(config);
340
+ const deployments = await getAzureOpenAIDeployments(config$1);
318
341
  state.azureOpenAIDeployments = deployments;
319
342
  if (deployments.length > 0) consola.info(`Loaded ${deployments.length} Azure OpenAI deployment(s):\n${deployments.map((d) => `- ${d.id} (${d.model})`).join("\n")}`);
320
343
  else consola.warn("No Azure OpenAI deployments found");
@@ -488,6 +511,377 @@ const checkUsage = defineCommand({
488
511
  }
489
512
  });
490
513
 
514
+ //#endregion
515
+ //#region src/lib/auto-replace.ts
516
+ const SYSTEM_REPLACEMENTS = [{
517
+ id: "system-anthropic-billing",
518
+ pattern: "x-anthropic-billing-header:[^\n]*\n?",
519
+ replacement: "",
520
+ isRegex: true,
521
+ enabled: true,
522
+ isSystem: true
523
+ }];
524
+ let userReplacements = [];
525
+ let isLoaded = false;
526
+ /**
527
+ * Load user replacements from disk
528
+ */
529
+ async function loadReplacements() {
530
+ try {
531
+ const data = await fs.readFile(PATHS.REPLACEMENTS_CONFIG_PATH);
532
+ userReplacements = JSON.parse(data).filter((r) => !r.isSystem);
533
+ isLoaded = true;
534
+ consola.debug(`Loaded ${userReplacements.length} user replacement rules`);
535
+ } catch {
536
+ userReplacements = [];
537
+ isLoaded = true;
538
+ }
539
+ }
540
+ /**
541
+ * Save user replacements to disk
542
+ */
543
+ async function saveReplacements() {
544
+ try {
545
+ await fs.writeFile(PATHS.REPLACEMENTS_CONFIG_PATH, JSON.stringify(userReplacements, null, 2), "utf8");
546
+ consola.debug(`Saved ${userReplacements.length} user replacement rules`);
547
+ } catch (error) {
548
+ consola.error("Failed to save replacement rules:", error);
549
+ throw error;
550
+ }
551
+ }
552
+ /**
553
+ * Ensure replacements are loaded before accessing
554
+ */
555
+ async function ensureLoaded() {
556
+ if (!isLoaded) await loadReplacements();
557
+ }
558
+ /**
559
+ * Get all replacement rules (system + user)
560
+ */
561
+ async function getAllReplacements() {
562
+ await ensureLoaded();
563
+ return [...SYSTEM_REPLACEMENTS, ...userReplacements];
564
+ }
565
+ /**
566
+ * Get only user-configurable replacements
567
+ */
568
+ async function getUserReplacements() {
569
+ await ensureLoaded();
570
+ return userReplacements;
571
+ }
572
+ /**
573
+ * Add a new user replacement rule
574
+ */
575
+ async function addReplacement(pattern, replacement, isRegex = false) {
576
+ await ensureLoaded();
577
+ const rule = {
578
+ id: `user-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
579
+ pattern,
580
+ replacement,
581
+ isRegex,
582
+ enabled: true,
583
+ isSystem: false
584
+ };
585
+ userReplacements.push(rule);
586
+ await saveReplacements();
587
+ consola.info(`Added replacement rule: "${pattern}" -> "${replacement}"`);
588
+ return rule;
589
+ }
590
+ /**
591
+ * Remove a user replacement rule by ID
592
+ */
593
+ async function removeReplacement(id) {
594
+ await ensureLoaded();
595
+ const rule = userReplacements.find((r) => r.id === id);
596
+ if (!rule) return false;
597
+ if (rule.isSystem) {
598
+ consola.warn("Cannot remove system replacement rule");
599
+ return false;
600
+ }
601
+ userReplacements = userReplacements.filter((r) => r.id !== id);
602
+ await saveReplacements();
603
+ consola.info(`Removed replacement rule: ${id}`);
604
+ return true;
605
+ }
606
+ /**
607
+ * Toggle a replacement rule on/off
608
+ */
609
+ async function toggleReplacement(id) {
610
+ await ensureLoaded();
611
+ const userRule = userReplacements.find((r) => r.id === id);
612
+ if (userRule) {
613
+ userRule.enabled = !userRule.enabled;
614
+ await saveReplacements();
615
+ consola.info(`Toggled replacement rule ${id}: ${userRule.enabled ? "enabled" : "disabled"}`);
616
+ return userRule;
617
+ }
618
+ if (SYSTEM_REPLACEMENTS.find((r) => r.id === id)) {
619
+ consola.warn("Cannot toggle system replacement rule");
620
+ return null;
621
+ }
622
+ return null;
623
+ }
624
+ /**
625
+ * Clear all user replacements
626
+ */
627
+ async function clearUserReplacements() {
628
+ userReplacements = [];
629
+ await saveReplacements();
630
+ consola.info("Cleared all user replacement rules");
631
+ }
632
+ /**
633
+ * Apply a single replacement rule to text
634
+ */
635
+ function applyRule(text, rule) {
636
+ if (!rule.enabled) return text;
637
+ if (rule.isRegex) try {
638
+ const regex = new RegExp(rule.pattern, "g");
639
+ return text.replace(regex, rule.replacement);
640
+ } catch {
641
+ consola.warn(`Invalid regex pattern in rule ${rule.id}: ${rule.pattern}`);
642
+ return text;
643
+ }
644
+ return text.split(rule.pattern).join(rule.replacement);
645
+ }
646
+ /**
647
+ * Apply all replacement rules to text
648
+ */
649
+ async function applyReplacements(text) {
650
+ let result = text;
651
+ const allRules = await getAllReplacements();
652
+ for (const rule of allRules) {
653
+ const before = result;
654
+ result = applyRule(result, rule);
655
+ if (before !== result) consola.debug(`Applied replacement rule: ${rule.id}`);
656
+ }
657
+ return result;
658
+ }
659
+ /**
660
+ * Apply replacements to a chat completions payload
661
+ * This modifies message content in place
662
+ */
663
+ async function applyReplacementsToPayload(payload) {
664
+ const processedMessages = await Promise.all(payload.messages.map(async (message) => {
665
+ if (typeof message.content === "string") return {
666
+ ...message,
667
+ content: await applyReplacements(message.content)
668
+ };
669
+ if (Array.isArray(message.content)) return {
670
+ ...message,
671
+ content: await Promise.all(message.content.map(async (part) => {
672
+ if (typeof part === "object" && part.type === "text" && part.text) return {
673
+ ...part,
674
+ text: await applyReplacements(part.text)
675
+ };
676
+ return part;
677
+ }))
678
+ };
679
+ return message;
680
+ }));
681
+ return {
682
+ ...payload,
683
+ messages: processedMessages
684
+ };
685
+ }
686
+
687
+ //#endregion
688
+ //#region src/config.ts
689
+ function formatRule(rule, index) {
690
+ const status = rule.enabled ? "✓" : "✗";
691
+ const type = rule.isRegex ? "regex" : "string";
692
+ const system = rule.isSystem ? " [system]" : "";
693
+ const replacement = rule.replacement || "(empty)";
694
+ return `${index + 1}. [${status}] (${type})${system} "${rule.pattern}" → "${replacement}"`;
695
+ }
696
+ async function listReplacements() {
697
+ const all = await getAllReplacements();
698
+ if (all.length === 0) {
699
+ consola.info("No replacement rules configured.");
700
+ return;
701
+ }
702
+ consola.info("\n📋 Replacement Rules:\n");
703
+ for (const [i, element] of all.entries()) console.log(formatRule(element, i));
704
+ console.log();
705
+ }
706
+ async function addNewReplacement() {
707
+ const matchType = await consola.prompt("Match type:", {
708
+ type: "select",
709
+ options: [{
710
+ label: "String (exact match)",
711
+ value: "string"
712
+ }, {
713
+ label: "Regex (regular expression)",
714
+ value: "regex"
715
+ }]
716
+ });
717
+ if (typeof matchType === "symbol") {
718
+ consola.info("Cancelled.");
719
+ return;
720
+ }
721
+ const pattern = await consola.prompt("Pattern to match:", { type: "text" });
722
+ if (typeof pattern === "symbol" || !pattern) {
723
+ consola.info("Cancelled.");
724
+ return;
725
+ }
726
+ if (matchType === "regex") try {
727
+ new RegExp(pattern);
728
+ } catch {
729
+ consola.error(`Invalid regex pattern: ${pattern}`);
730
+ return;
731
+ }
732
+ const replacement = await consola.prompt("Replacement text (leave empty to delete matches):", {
733
+ type: "text",
734
+ default: ""
735
+ });
736
+ if (typeof replacement === "symbol") {
737
+ consola.info("Cancelled.");
738
+ return;
739
+ }
740
+ const rule = await addReplacement(pattern, replacement, matchType === "regex");
741
+ consola.success(`Added rule: ${rule.id}`);
742
+ }
743
+ async function removeExistingReplacement() {
744
+ const userRules = await getUserReplacements();
745
+ if (userRules.length === 0) {
746
+ consola.info("No user rules to remove.");
747
+ return;
748
+ }
749
+ const options = userRules.map((rule, i) => ({
750
+ label: formatRule(rule, i),
751
+ value: rule.id
752
+ }));
753
+ const selected = await consola.prompt("Select rule to remove:", {
754
+ type: "select",
755
+ options
756
+ });
757
+ if (typeof selected === "symbol") {
758
+ consola.info("Cancelled.");
759
+ return;
760
+ }
761
+ if (await removeReplacement(selected)) consola.success("Rule removed.");
762
+ else consola.error("Failed to remove rule.");
763
+ }
764
+ async function toggleExistingReplacement() {
765
+ const userRules = await getUserReplacements();
766
+ if (userRules.length === 0) {
767
+ consola.info("No user rules to toggle.");
768
+ return;
769
+ }
770
+ const options = userRules.map((rule$1, i) => ({
771
+ label: formatRule(rule$1, i),
772
+ value: rule$1.id
773
+ }));
774
+ const selected = await consola.prompt("Select rule to toggle:", {
775
+ type: "select",
776
+ options
777
+ });
778
+ if (typeof selected === "symbol") {
779
+ consola.info("Cancelled.");
780
+ return;
781
+ }
782
+ const rule = await toggleReplacement(selected);
783
+ if (rule) consola.success(`Rule ${rule.enabled ? "enabled" : "disabled"}.`);
784
+ else consola.error("Failed to toggle rule.");
785
+ }
786
+ async function testReplacements() {
787
+ const testText = await consola.prompt("Enter text to test replacements:", { type: "text" });
788
+ if (typeof testText === "symbol" || !testText) {
789
+ consola.info("Cancelled.");
790
+ return;
791
+ }
792
+ const result = await applyReplacements(testText);
793
+ consola.info("\n📝 Original:");
794
+ console.log(testText);
795
+ consola.info("\n✨ After replacements:");
796
+ console.log(result);
797
+ console.log();
798
+ }
799
+ async function clearAllReplacements() {
800
+ if (await consola.prompt("Are you sure you want to clear all user replacements?", {
801
+ type: "confirm",
802
+ initial: false
803
+ })) {
804
+ await clearUserReplacements();
805
+ consola.success("All user replacements cleared.");
806
+ } else consola.info("Cancelled.");
807
+ }
808
+ async function mainMenu() {
809
+ consola.info(`\n🔧 Copilot API - Replacement Configuration`);
810
+ consola.info(`Config file: ${PATHS.REPLACEMENTS_CONFIG_PATH}\n`);
811
+ let running = true;
812
+ while (running) {
813
+ const action = await consola.prompt("What would you like to do?", {
814
+ type: "select",
815
+ options: [
816
+ {
817
+ label: "📋 List all rules",
818
+ value: "list"
819
+ },
820
+ {
821
+ label: "➕ Add new rule",
822
+ value: "add"
823
+ },
824
+ {
825
+ label: "➖ Remove rule",
826
+ value: "remove"
827
+ },
828
+ {
829
+ label: "🔄 Toggle rule on/off",
830
+ value: "toggle"
831
+ },
832
+ {
833
+ label: "🧪 Test replacements",
834
+ value: "test"
835
+ },
836
+ {
837
+ label: "🗑️ Clear all user rules",
838
+ value: "clear"
839
+ },
840
+ {
841
+ label: "🚪 Exit",
842
+ value: "exit"
843
+ }
844
+ ]
845
+ });
846
+ if (typeof action === "symbol") break;
847
+ switch (action) {
848
+ case "list":
849
+ await listReplacements();
850
+ break;
851
+ case "add":
852
+ await addNewReplacement();
853
+ break;
854
+ case "remove":
855
+ await removeExistingReplacement();
856
+ break;
857
+ case "toggle":
858
+ await toggleExistingReplacement();
859
+ break;
860
+ case "test":
861
+ await testReplacements();
862
+ break;
863
+ case "clear":
864
+ await clearAllReplacements();
865
+ break;
866
+ case "exit":
867
+ running = false;
868
+ break;
869
+ default: break;
870
+ }
871
+ }
872
+ consola.info("Goodbye! 👋");
873
+ }
874
+ const config = defineCommand({
875
+ meta: {
876
+ name: "config",
877
+ description: "Configure replacement rules interactively"
878
+ },
879
+ run: async () => {
880
+ await ensurePaths();
881
+ await mainMenu();
882
+ }
883
+ });
884
+
491
885
  //#endregion
492
886
  //#region src/debug.ts
493
887
  async function getPackageVersion() {
@@ -917,7 +1311,8 @@ const createChatCompletions = async (payload) => {
917
1311
  //#region src/routes/chat-completions/handler.ts
918
1312
  async function handleCompletion$1(c) {
919
1313
  await checkRateLimit(state);
920
- let payload = await c.req.json();
1314
+ const rawPayload = await c.req.json();
1315
+ let payload = await applyReplacementsToPayload(rawPayload);
921
1316
  consola.debug("Request payload:", JSON.stringify(payload).slice(-400));
922
1317
  if (isAzureOpenAIModel(payload.model)) {
923
1318
  if (!state.azureOpenAIConfig) return c.json({ error: "Azure OpenAI not configured" }, 500);
@@ -1366,7 +1761,8 @@ async function handleCompletion(c) {
1366
1761
  await checkRateLimit(state);
1367
1762
  const anthropicPayload = await c.req.json();
1368
1763
  consola.debug("Anthropic request payload:", JSON.stringify(anthropicPayload));
1369
- const openAIPayload = translateToOpenAI(anthropicPayload);
1764
+ const translatedPayload = translateToOpenAI(anthropicPayload);
1765
+ const openAIPayload = await applyReplacementsToPayload(translatedPayload);
1370
1766
  consola.debug("Translated OpenAI request payload:", JSON.stringify(openAIPayload));
1371
1767
  if (state.manualApprove) await awaitApproval();
1372
1768
  if (isAzureOpenAIModel(openAIPayload.model)) {
@@ -1490,6 +1886,37 @@ modelRoutes.get("/", async (c) => {
1490
1886
  }
1491
1887
  });
1492
1888
 
1889
+ //#endregion
1890
+ //#region src/routes/replacements/route.ts
1891
+ const replacementsRoute = new Hono();
1892
+ replacementsRoute.get("/", async (c) => {
1893
+ return c.json({
1894
+ all: await getAllReplacements(),
1895
+ user: await getUserReplacements()
1896
+ });
1897
+ });
1898
+ replacementsRoute.post("/", async (c) => {
1899
+ const body = await c.req.json();
1900
+ if (!body.pattern) return c.json({ error: "Pattern is required" }, 400);
1901
+ const rule = await addReplacement(body.pattern, body.replacement ?? "", body.isRegex ?? false);
1902
+ return c.json(rule, 201);
1903
+ });
1904
+ replacementsRoute.delete("/:id", async (c) => {
1905
+ const id = c.req.param("id");
1906
+ if (!await removeReplacement(id)) return c.json({ error: "Replacement not found or is a system rule" }, 404);
1907
+ return c.json({ success: true });
1908
+ });
1909
+ replacementsRoute.patch("/:id/toggle", async (c) => {
1910
+ const id = c.req.param("id");
1911
+ const rule = await toggleReplacement(id);
1912
+ if (!rule) return c.json({ error: "Replacement not found or is a system rule" }, 404);
1913
+ return c.json(rule);
1914
+ });
1915
+ replacementsRoute.delete("/", async (c) => {
1916
+ await clearUserReplacements();
1917
+ return c.json({ success: true });
1918
+ });
1919
+
1493
1920
  //#endregion
1494
1921
  //#region src/routes/token/route.ts
1495
1922
  const tokenRoute = new Hono();
@@ -1529,6 +1956,7 @@ server.route("/models", modelRoutes);
1529
1956
  server.route("/embeddings", embeddingRoutes);
1530
1957
  server.route("/usage", usageRoute);
1531
1958
  server.route("/token", tokenRoute);
1959
+ server.route("/replacements", replacementsRoute);
1532
1960
  server.route("/v1/chat/completions", completionRoutes);
1533
1961
  server.route("/v1/models", modelRoutes);
1534
1962
  server.route("/v1/embeddings", embeddingRoutes);
@@ -1537,6 +1965,10 @@ server.route("/v1/messages", messageRoutes);
1537
1965
  //#endregion
1538
1966
  //#region src/start.ts
1539
1967
  async function runServer(options) {
1968
+ if (options.insecure) {
1969
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
1970
+ consola.warn("SSL certificate verification disabled (insecure mode)");
1971
+ }
1540
1972
  if (options.proxyEnv) initProxyFromEnv();
1541
1973
  if (options.verbose) {
1542
1974
  consola.level = 5;
@@ -1656,6 +2088,11 @@ const start = defineCommand({
1656
2088
  type: "boolean",
1657
2089
  default: false,
1658
2090
  description: "Initialize proxy from environment variables"
2091
+ },
2092
+ insecure: {
2093
+ type: "boolean",
2094
+ default: false,
2095
+ description: "Disable SSL certificate verification (for corporate proxies with self-signed certs)"
1659
2096
  }
1660
2097
  },
1661
2098
  run({ args }) {
@@ -1671,7 +2108,8 @@ const start = defineCommand({
1671
2108
  githubToken: args["github-token"],
1672
2109
  claudeCode: args["claude-code"],
1673
2110
  showToken: args["show-token"],
1674
- proxyEnv: args["proxy-env"]
2111
+ proxyEnv: args["proxy-env"],
2112
+ insecure: args.insecure
1675
2113
  });
1676
2114
  }
1677
2115
  });
@@ -1687,7 +2125,8 @@ const main = defineCommand({
1687
2125
  auth,
1688
2126
  start,
1689
2127
  "check-usage": checkUsage,
1690
- debug
2128
+ debug,
2129
+ config
1691
2130
  }
1692
2131
  });
1693
2132
  await runMain(main);