@hsupu/copilot-api 0.7.9 → 0.7.10

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
@@ -558,15 +558,12 @@ const SUPPORTED_VERSIONS = {
558
558
  min: "2.0.0",
559
559
  max: "2.1.10"
560
560
  },
561
- v2b: {
562
- min: "2.1.11",
563
- max: "2.1.12"
564
- }
561
+ v2b: { min: "2.1.11" }
565
562
  };
566
563
  const PATTERNS = {
567
564
  funcOriginal: /function HR\(A\)\{if\(A\.includes\("\[1m\]"\)\)return 1e6;return 200000\}/,
568
565
  funcPatched: /function HR\(A\)\{if\(A\.includes\("\[1m\]"\)\)return 1e6;return \d+\}/,
569
- variable: /var BS9=(\d+)/
566
+ variable: /var ([A-Za-z_$]\w*)=(\d+)(?=,\w+=20000,)/
570
567
  };
571
568
  /**
572
569
  * Parse semver version string to comparable parts
@@ -592,14 +589,14 @@ function compareVersions(a, b) {
592
589
  }
593
590
  function getPatternTypeForVersion(version$1) {
594
591
  if (compareVersions(version$1, SUPPORTED_VERSIONS.v2a.min) >= 0 && compareVersions(version$1, SUPPORTED_VERSIONS.v2a.max) <= 0) return "func";
595
- if (compareVersions(version$1, SUPPORTED_VERSIONS.v2b.min) >= 0 && compareVersions(version$1, SUPPORTED_VERSIONS.v2b.max) <= 0) return "variable";
592
+ if (compareVersions(version$1, SUPPORTED_VERSIONS.v2b.min) >= 0) return "variable";
596
593
  return null;
597
594
  }
598
595
  /**
599
596
  * Get supported version range string for error messages
600
597
  */
601
598
  function getSupportedRangeString() {
602
- return `${SUPPORTED_VERSIONS.v2a.min}-${SUPPORTED_VERSIONS.v2a.max}, ${SUPPORTED_VERSIONS.v2b.min}-${SUPPORTED_VERSIONS.v2b.max}`;
599
+ return `${SUPPORTED_VERSIONS.v2a.min}-${SUPPORTED_VERSIONS.v2a.max}, ${SUPPORTED_VERSIONS.v2b.min}+`;
603
600
  }
604
601
  /**
605
602
  * Get Claude Code version from package.json
@@ -632,9 +629,9 @@ function findInVoltaTools(voltaHome) {
632
629
  return paths;
633
630
  }
634
631
  /**
635
- * Find Claude Code CLI path by checking common locations
632
+ * Find all Claude Code CLI paths by checking common locations
636
633
  */
637
- function findClaudeCodePath() {
634
+ function findAllClaudeCodePaths() {
638
635
  const possiblePaths = [];
639
636
  const home = process.env.HOME || "";
640
637
  const voltaHome = process.env.VOLTA_HOME || join(home, ".volta");
@@ -649,22 +646,41 @@ function findClaudeCodePath() {
649
646
  for (const base of globalPaths) possiblePaths.push(join(base, "@anthropic-ai", "claude-code", "cli.js"));
650
647
  const bunGlobal = join(home, ".bun", "install", "global");
651
648
  if (existsSync(bunGlobal)) possiblePaths.push(join(bunGlobal, "node_modules", "@anthropic-ai", "claude-code", "cli.js"));
652
- return possiblePaths.find((p) => existsSync(p)) ?? null;
649
+ return [...new Set(possiblePaths.filter((p) => existsSync(p)))];
653
650
  }
654
651
  /**
655
- * Get current context limit from Claude Code
652
+ * Get installation info for a CLI path
656
653
  */
657
- function getCurrentLimit(content) {
654
+ function getInstallationInfo(cliPath) {
655
+ const version$1 = getClaudeCodeVersion(cliPath);
656
+ const content = readFileSync(cliPath, "utf8");
657
+ const limit = getCurrentLimit(content);
658
+ return {
659
+ path: cliPath,
660
+ version: version$1,
661
+ limit
662
+ };
663
+ }
664
+ function getCurrentLimitInfo(content) {
658
665
  const varMatch = content.match(PATTERNS.variable);
659
- if (varMatch) return Number.parseInt(varMatch[1], 10);
666
+ if (varMatch) return {
667
+ limit: Number.parseInt(varMatch[2], 10),
668
+ varName: varMatch[1]
669
+ };
660
670
  const funcMatch = content.match(PATTERNS.funcPatched);
661
671
  if (funcMatch) {
662
672
  const limitMatch = funcMatch[0].match(/return (\d+)\}$/);
663
- return limitMatch ? Number.parseInt(limitMatch[1], 10) : null;
673
+ return limitMatch ? { limit: Number.parseInt(limitMatch[1], 10) } : null;
664
674
  }
665
675
  return null;
666
676
  }
667
677
  /**
678
+ * Get current context limit from Claude Code (legacy wrapper)
679
+ */
680
+ function getCurrentLimit(content) {
681
+ return getCurrentLimitInfo(content)?.limit ?? null;
682
+ }
683
+ /**
668
684
  * Check if Claude Code version is supported for patching
669
685
  */
670
686
  function checkVersionSupport(cliPath) {
@@ -696,22 +712,25 @@ function patchClaudeCode(cliPath, newLimit) {
696
712
  const versionCheck = checkVersionSupport(cliPath);
697
713
  if (!versionCheck.supported) {
698
714
  consola.error(versionCheck.error);
699
- return false;
715
+ return "failed";
700
716
  }
701
717
  consola.info(`Claude Code version: ${versionCheck.version}`);
702
- if (getCurrentLimit(content) === newLimit) {
703
- consola.info(`Already patched with limit ${newLimit}`);
704
- return true;
705
- }
718
+ const limitInfo = getCurrentLimitInfo(content);
719
+ if (limitInfo?.limit === newLimit) return "already_patched";
706
720
  let newContent;
707
- if (versionCheck.patternType === "variable") newContent = content.replace(PATTERNS.variable, `var BS9=${newLimit}`);
708
- else {
721
+ if (versionCheck.patternType === "variable") {
722
+ if (!limitInfo?.varName) {
723
+ consola.error("Could not detect variable name for patching");
724
+ return "failed";
725
+ }
726
+ newContent = content.replace(PATTERNS.variable, `var ${limitInfo.varName}=${newLimit}`);
727
+ } else {
709
728
  const replacement = `function HR(A){if(A.includes("[1m]"))return 1e6;return ${newLimit}}`;
710
729
  const pattern = PATTERNS.funcOriginal.test(content) ? PATTERNS.funcOriginal : PATTERNS.funcPatched;
711
730
  newContent = content.replace(pattern, replacement);
712
731
  }
713
732
  writeFileSync(cliPath, newContent);
714
- return true;
733
+ return "success";
715
734
  }
716
735
  /**
717
736
  * Restore Claude Code to original 200k limit
@@ -724,13 +743,19 @@ function restoreClaudeCode(cliPath) {
724
743
  return false;
725
744
  }
726
745
  consola.info(`Claude Code version: ${versionCheck.version}`);
727
- if (getCurrentLimit(content) === 2e5) {
746
+ const limitInfo = getCurrentLimitInfo(content);
747
+ if (limitInfo?.limit === 2e5) {
728
748
  consola.info("Already at original 200000 limit");
729
749
  return true;
730
750
  }
731
751
  let newContent;
732
- if (versionCheck.patternType === "variable") newContent = content.replace(PATTERNS.variable, "var BS9=200000");
733
- else newContent = content.replace(PATTERNS.funcPatched, "function HR(A){if(A.includes(\"[1m]\"))return 1e6;return 200000}");
752
+ if (versionCheck.patternType === "variable") {
753
+ if (!limitInfo?.varName) {
754
+ consola.error("Could not detect variable name for restoring");
755
+ return false;
756
+ }
757
+ newContent = content.replace(PATTERNS.variable, `var ${limitInfo.varName}=200000`);
758
+ } else newContent = content.replace(PATTERNS.funcPatched, "function HR(A){if(A.includes(\"[1m]\"))return 1e6;return 200000}");
734
759
  writeFileSync(cliPath, newContent);
735
760
  return true;
736
761
  }
@@ -739,7 +764,7 @@ function showStatus(cliPath, currentLimit) {
739
764
  if (version$1) consola.info(`Claude Code version: ${version$1}`);
740
765
  if (currentLimit === null) {
741
766
  consola.warn("Could not detect current limit - CLI may have been updated");
742
- consola.info("Look for the BS9 variable or HR function pattern in cli.js");
767
+ consola.info("Look for a variable like 'var XXX=200000' followed by ',YYY=20000,' in cli.js");
743
768
  } else if (currentLimit === 2e5) consola.info("Status: Original (200k context window)");
744
769
  else consola.info(`Status: Patched (${currentLimit} context window)`);
745
770
  }
@@ -773,17 +798,42 @@ const patchClaude = defineCommand({
773
798
  description: "Show current patch status without modifying"
774
799
  }
775
800
  },
776
- run({ args }) {
777
- const cliPath = args.path || findClaudeCodePath();
778
- if (!cliPath) {
779
- consola.error("Could not find Claude Code installation");
780
- consola.info("Searched in: volta, npm global, bun global");
781
- consola.info("Use --path to specify the path to cli.js manually");
782
- process.exit(1);
783
- }
784
- if (!existsSync(cliPath)) {
785
- consola.error(`File not found: ${cliPath}`);
786
- process.exit(1);
801
+ async run({ args }) {
802
+ let cliPath;
803
+ if (args.path) {
804
+ cliPath = args.path;
805
+ if (!existsSync(cliPath)) {
806
+ consola.error(`File not found: ${cliPath}`);
807
+ process.exit(1);
808
+ }
809
+ } else {
810
+ const installations = findAllClaudeCodePaths();
811
+ if (installations.length === 0) {
812
+ consola.error("Could not find Claude Code installation");
813
+ consola.info("Searched in: volta, npm global, bun global");
814
+ consola.info("Use --path to specify the path to cli.js manually");
815
+ process.exit(1);
816
+ }
817
+ if (installations.length === 1) cliPath = installations[0];
818
+ else {
819
+ consola.info(`Found ${installations.length} Claude Code installations:`);
820
+ const options = installations.map((path$1) => {
821
+ const info = getInstallationInfo(path$1);
822
+ let status = "unknown";
823
+ if (info.limit === 2e5) status = "original";
824
+ else if (info.limit) status = `patched: ${info.limit}`;
825
+ return {
826
+ label: `v${info.version ?? "?"} (${status}) - ${path$1}`,
827
+ value: path$1
828
+ };
829
+ });
830
+ const selected = await consola.prompt("Select installation to patch:", {
831
+ type: "select",
832
+ options
833
+ });
834
+ if (typeof selected === "symbol") process.exit(0);
835
+ cliPath = selected;
836
+ }
787
837
  }
788
838
  consola.info(`Claude Code path: ${cliPath}`);
789
839
  const content = readFileSync(cliPath, "utf8");
@@ -806,13 +856,14 @@ const patchClaude = defineCommand({
806
856
  consola.error("Invalid limit value. Must be a number >= 1000");
807
857
  process.exit(1);
808
858
  }
809
- if (patchClaudeCode(cliPath, limit)) {
810
- consola.success(`Patched context window: 200000 → ${limit}`);
859
+ const result = patchClaudeCode(cliPath, limit);
860
+ if (result === "success") {
861
+ consola.success(`Patched context window: ${currentLimit ?? 2e5} → ${limit}`);
811
862
  consola.info("Note: You may need to re-run this after Claude Code updates");
812
- } else {
863
+ } else if (result === "already_patched") consola.success(`Already patched with limit ${limit}`);
864
+ else {
813
865
  consola.error("Failed to patch - pattern not found");
814
866
  consola.info("Claude Code may have been updated to a new version");
815
- consola.info("Check the cli.js for the HR function pattern");
816
867
  process.exit(1);
817
868
  }
818
869
  }
@@ -821,7 +872,7 @@ const patchClaude = defineCommand({
821
872
  //#endregion
822
873
  //#region package.json
823
874
  var name = "@hsupu/copilot-api";
824
- var version = "0.7.9";
875
+ var version = "0.7.10";
825
876
  var description = "Turn GitHub Copilot into OpenAI/Anthropic API compatible server. Usable with Claude Code!";
826
877
  var keywords = [
827
878
  "proxy",
@@ -4109,16 +4160,33 @@ function translateAnthropicMessagesToOpenAI(anthropicMessages, system, toolNameM
4109
4160
  const otherMessages = anthropicMessages.flatMap((message) => message.role === "user" ? handleUserMessage(message) : handleAssistantMessage(message, toolNameMapping));
4110
4161
  return [...systemMessages, ...otherMessages];
4111
4162
  }
4163
+ const RESERVED_KEYWORDS = ["x-anthropic-billing-header"];
4164
+ /**
4165
+ * Filter out reserved keywords from system prompt text.
4166
+ * Copilot API rejects requests containing these keywords.
4167
+ * Removes the entire line containing the keyword to keep the prompt clean.
4168
+ */
4169
+ function filterReservedKeywords(text) {
4170
+ let filtered = text;
4171
+ for (const keyword of RESERVED_KEYWORDS) if (text.includes(keyword)) {
4172
+ consola.debug(`[Reserved Keyword] Removing line containing "${keyword}"`);
4173
+ filtered = filtered.split("\n").filter((line) => !line.includes(keyword)).join("\n");
4174
+ }
4175
+ return filtered;
4176
+ }
4112
4177
  function handleSystemPrompt(system) {
4113
4178
  if (!system) return [];
4114
4179
  if (typeof system === "string") return [{
4115
4180
  role: "system",
4116
- content: system
4117
- }];
4118
- else return [{
4119
- role: "system",
4120
- content: system.map((block) => block.text).join("\n\n")
4181
+ content: filterReservedKeywords(system)
4121
4182
  }];
4183
+ else {
4184
+ const systemText = system.map((block) => block.text).join("\n\n");
4185
+ return [{
4186
+ role: "system",
4187
+ content: filterReservedKeywords(systemText)
4188
+ }];
4189
+ }
4122
4190
  }
4123
4191
  function handleUserMessage(message) {
4124
4192
  const newMessages = [];