@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 +466 -27
- package/dist/main.js.map +1 -1
- package/package.json +68 -68
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
|
|
269
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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);
|