@hsupu/copilot-api 0.7.2 → 0.7.4
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/README.md +1 -0
- package/dist/main.js +64 -474
- package/dist/main.js.map +1 -1
- package/package.json +4 -7
package/dist/main.js
CHANGED
|
@@ -12,40 +12,12 @@ import { getProxyForUrl } from "proxy-from-env";
|
|
|
12
12
|
import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
|
|
13
13
|
import { execSync } from "node:child_process";
|
|
14
14
|
import process$1 from "node:process";
|
|
15
|
-
import
|
|
16
|
-
import React, { useEffect, useState } from "react";
|
|
17
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
15
|
+
import pc from "picocolors";
|
|
18
16
|
import { Hono } from "hono";
|
|
19
17
|
import { cors } from "hono/cors";
|
|
20
18
|
import { streamSSE } from "hono/streaming";
|
|
21
19
|
import { events } from "fetch-event-stream";
|
|
22
20
|
|
|
23
|
-
//#region rolldown:runtime
|
|
24
|
-
var __create = Object.create;
|
|
25
|
-
var __defProp = Object.defineProperty;
|
|
26
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
27
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
28
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
29
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
30
|
-
var __commonJS = (cb, mod) => function() {
|
|
31
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
32
|
-
};
|
|
33
|
-
var __copyProps = (to, from, except, desc) => {
|
|
34
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
35
|
-
key = keys[i];
|
|
36
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
37
|
-
get: ((k) => from[k]).bind(null, key),
|
|
38
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
return to;
|
|
42
|
-
};
|
|
43
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
44
|
-
value: mod,
|
|
45
|
-
enumerable: true
|
|
46
|
-
}) : target, mod));
|
|
47
|
-
|
|
48
|
-
//#endregion
|
|
49
21
|
//#region src/lib/paths.ts
|
|
50
22
|
const APP_DIR = path.join(os.homedir(), ".local", "share", "copilot-api");
|
|
51
23
|
const GITHUB_TOKEN_PATH = path.join(APP_DIR, "github_token");
|
|
@@ -154,9 +126,24 @@ function formatTokenLimitError(current, limit) {
|
|
|
154
126
|
}
|
|
155
127
|
};
|
|
156
128
|
}
|
|
129
|
+
/** Format Anthropic-compatible error for request too large (413) */
|
|
130
|
+
function formatRequestTooLargeError() {
|
|
131
|
+
return {
|
|
132
|
+
type: "error",
|
|
133
|
+
error: {
|
|
134
|
+
type: "invalid_request_error",
|
|
135
|
+
message: "Request body too large. The HTTP request exceeds the server's size limit. Try reducing the conversation history or removing large content like images."
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
157
139
|
async function forwardError(c, error) {
|
|
158
140
|
consola.error("Error occurred:", error);
|
|
159
141
|
if (error instanceof HTTPError) {
|
|
142
|
+
if (error.status === 413) {
|
|
143
|
+
const formattedError = formatRequestTooLargeError();
|
|
144
|
+
consola.debug("Returning formatted 413 error:", formattedError);
|
|
145
|
+
return c.json(formattedError, 413);
|
|
146
|
+
}
|
|
160
147
|
let errorJson;
|
|
161
148
|
try {
|
|
162
149
|
errorJson = JSON.parse(error.responseText);
|
|
@@ -831,7 +818,7 @@ function initProxyFromEnv() {
|
|
|
831
818
|
//#endregion
|
|
832
819
|
//#region src/lib/shell.ts
|
|
833
820
|
function getShell() {
|
|
834
|
-
const { platform, ppid, env
|
|
821
|
+
const { platform, ppid, env } = process$1;
|
|
835
822
|
if (platform === "win32") {
|
|
836
823
|
try {
|
|
837
824
|
const command = `wmic process get ParentProcessId,Name | findstr "${ppid}"`;
|
|
@@ -841,7 +828,7 @@ function getShell() {
|
|
|
841
828
|
}
|
|
842
829
|
return "cmd";
|
|
843
830
|
} else {
|
|
844
|
-
const shellPath = env
|
|
831
|
+
const shellPath = env.SHELL;
|
|
845
832
|
if (shellPath) {
|
|
846
833
|
if (shellPath.endsWith("zsh")) return "zsh";
|
|
847
834
|
if (shellPath.endsWith("fish")) return "fish";
|
|
@@ -881,91 +868,27 @@ function generateEnvScript(envVars, commandToRun = "") {
|
|
|
881
868
|
return commandBlock || commandToRun;
|
|
882
869
|
}
|
|
883
870
|
|
|
884
|
-
//#endregion
|
|
885
|
-
//#region node_modules/picocolors/picocolors.js
|
|
886
|
-
var require_picocolors = /* @__PURE__ */ __commonJS({ "node_modules/picocolors/picocolors.js": ((exports, module) => {
|
|
887
|
-
let p = process || {}, argv = p.argv || [], env = p.env || {};
|
|
888
|
-
let isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
|
|
889
|
-
let formatter = (open, close, replace = open) => (input) => {
|
|
890
|
-
let string = "" + input, index = string.indexOf(close, open.length);
|
|
891
|
-
return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
|
|
892
|
-
};
|
|
893
|
-
let replaceClose = (string, close, replace, index) => {
|
|
894
|
-
let result = "", cursor = 0;
|
|
895
|
-
do {
|
|
896
|
-
result += string.substring(cursor, index) + replace;
|
|
897
|
-
cursor = index + close.length;
|
|
898
|
-
index = string.indexOf(close, cursor);
|
|
899
|
-
} while (~index);
|
|
900
|
-
return result + string.substring(cursor);
|
|
901
|
-
};
|
|
902
|
-
let createColors = (enabled = isColorSupported) => {
|
|
903
|
-
let f = enabled ? formatter : () => String;
|
|
904
|
-
return {
|
|
905
|
-
isColorSupported: enabled,
|
|
906
|
-
reset: f("\x1B[0m", "\x1B[0m"),
|
|
907
|
-
bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
|
|
908
|
-
dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
|
|
909
|
-
italic: f("\x1B[3m", "\x1B[23m"),
|
|
910
|
-
underline: f("\x1B[4m", "\x1B[24m"),
|
|
911
|
-
inverse: f("\x1B[7m", "\x1B[27m"),
|
|
912
|
-
hidden: f("\x1B[8m", "\x1B[28m"),
|
|
913
|
-
strikethrough: f("\x1B[9m", "\x1B[29m"),
|
|
914
|
-
black: f("\x1B[30m", "\x1B[39m"),
|
|
915
|
-
red: f("\x1B[31m", "\x1B[39m"),
|
|
916
|
-
green: f("\x1B[32m", "\x1B[39m"),
|
|
917
|
-
yellow: f("\x1B[33m", "\x1B[39m"),
|
|
918
|
-
blue: f("\x1B[34m", "\x1B[39m"),
|
|
919
|
-
magenta: f("\x1B[35m", "\x1B[39m"),
|
|
920
|
-
cyan: f("\x1B[36m", "\x1B[39m"),
|
|
921
|
-
white: f("\x1B[37m", "\x1B[39m"),
|
|
922
|
-
gray: f("\x1B[90m", "\x1B[39m"),
|
|
923
|
-
bgBlack: f("\x1B[40m", "\x1B[49m"),
|
|
924
|
-
bgRed: f("\x1B[41m", "\x1B[49m"),
|
|
925
|
-
bgGreen: f("\x1B[42m", "\x1B[49m"),
|
|
926
|
-
bgYellow: f("\x1B[43m", "\x1B[49m"),
|
|
927
|
-
bgBlue: f("\x1B[44m", "\x1B[49m"),
|
|
928
|
-
bgMagenta: f("\x1B[45m", "\x1B[49m"),
|
|
929
|
-
bgCyan: f("\x1B[46m", "\x1B[49m"),
|
|
930
|
-
bgWhite: f("\x1B[47m", "\x1B[49m"),
|
|
931
|
-
blackBright: f("\x1B[90m", "\x1B[39m"),
|
|
932
|
-
redBright: f("\x1B[91m", "\x1B[39m"),
|
|
933
|
-
greenBright: f("\x1B[92m", "\x1B[39m"),
|
|
934
|
-
yellowBright: f("\x1B[93m", "\x1B[39m"),
|
|
935
|
-
blueBright: f("\x1B[94m", "\x1B[39m"),
|
|
936
|
-
magentaBright: f("\x1B[95m", "\x1B[39m"),
|
|
937
|
-
cyanBright: f("\x1B[96m", "\x1B[39m"),
|
|
938
|
-
whiteBright: f("\x1B[97m", "\x1B[39m"),
|
|
939
|
-
bgBlackBright: f("\x1B[100m", "\x1B[49m"),
|
|
940
|
-
bgRedBright: f("\x1B[101m", "\x1B[49m"),
|
|
941
|
-
bgGreenBright: f("\x1B[102m", "\x1B[49m"),
|
|
942
|
-
bgYellowBright: f("\x1B[103m", "\x1B[49m"),
|
|
943
|
-
bgBlueBright: f("\x1B[104m", "\x1B[49m"),
|
|
944
|
-
bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
|
|
945
|
-
bgCyanBright: f("\x1B[106m", "\x1B[49m"),
|
|
946
|
-
bgWhiteBright: f("\x1B[107m", "\x1B[49m")
|
|
947
|
-
};
|
|
948
|
-
};
|
|
949
|
-
module.exports = createColors();
|
|
950
|
-
module.exports.createColors = createColors;
|
|
951
|
-
}) });
|
|
952
|
-
|
|
953
871
|
//#endregion
|
|
954
872
|
//#region src/lib/tui/console-renderer.ts
|
|
955
|
-
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
956
873
|
const CLEAR_LINE = "\x1B[2K\r";
|
|
957
|
-
function
|
|
874
|
+
function formatTime(date = /* @__PURE__ */ new Date()) {
|
|
875
|
+
const h = String(date.getHours()).padStart(2, "0");
|
|
876
|
+
const m = String(date.getMinutes()).padStart(2, "0");
|
|
877
|
+
const s = String(date.getSeconds()).padStart(2, "0");
|
|
878
|
+
return `${h}:${m}:${s}`;
|
|
879
|
+
}
|
|
880
|
+
function formatDuration(ms) {
|
|
958
881
|
if (ms < 1e3) return `${ms}ms`;
|
|
959
882
|
return `${(ms / 1e3).toFixed(1)}s`;
|
|
960
883
|
}
|
|
961
|
-
function formatNumber
|
|
884
|
+
function formatNumber(n) {
|
|
962
885
|
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
963
886
|
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
964
887
|
return String(n);
|
|
965
888
|
}
|
|
966
|
-
function formatTokens
|
|
889
|
+
function formatTokens(input, output) {
|
|
967
890
|
if (input === void 0 || output === void 0) return "-";
|
|
968
|
-
return `${formatNumber
|
|
891
|
+
return `${formatNumber(input)}/${formatNumber(output)}`;
|
|
969
892
|
}
|
|
970
893
|
/**
|
|
971
894
|
* Console renderer that shows request lifecycle with apt-get style footer
|
|
@@ -996,7 +919,7 @@ var ConsoleRenderer = class {
|
|
|
996
919
|
const activeCount = this.activeRequests.size;
|
|
997
920
|
if (activeCount === 0) return "";
|
|
998
921
|
const plural = activeCount === 1 ? "" : "s";
|
|
999
|
-
return
|
|
922
|
+
return pc.dim(`[....] ${activeCount} request${plural} in progress...`);
|
|
1000
923
|
}
|
|
1001
924
|
/**
|
|
1002
925
|
* Render footer in-place on current line (no newline)
|
|
@@ -1030,16 +953,17 @@ var ConsoleRenderer = class {
|
|
|
1030
953
|
*/
|
|
1031
954
|
printLog(message, isGray = false) {
|
|
1032
955
|
this.clearFooterForLog();
|
|
1033
|
-
if (isGray) consola.log(
|
|
956
|
+
if (isGray) consola.log(pc.dim(message));
|
|
1034
957
|
else consola.log(message);
|
|
1035
958
|
this.renderFooter();
|
|
1036
959
|
}
|
|
1037
960
|
onRequestStart(request) {
|
|
1038
961
|
this.activeRequests.set(request.id, request);
|
|
1039
962
|
if (this.showActive) {
|
|
963
|
+
const time = formatTime();
|
|
1040
964
|
const modelInfo = request.model ? ` ${request.model}` : "";
|
|
1041
965
|
const queueInfo = request.queuePosition !== void 0 && request.queuePosition > 0 ? ` [q#${request.queuePosition}]` : "";
|
|
1042
|
-
const message =
|
|
966
|
+
const message = `${time} [....] ${request.method} ${request.path}${modelInfo}${queueInfo}`;
|
|
1043
967
|
this.printLog(message, request.isHistoryAccess);
|
|
1044
968
|
}
|
|
1045
969
|
}
|
|
@@ -1048,21 +972,23 @@ var ConsoleRenderer = class {
|
|
|
1048
972
|
if (!request) return;
|
|
1049
973
|
Object.assign(request, update);
|
|
1050
974
|
if (this.showActive && update.status === "streaming") {
|
|
975
|
+
const time = formatTime();
|
|
1051
976
|
const modelInfo = request.model ? ` ${request.model}` : "";
|
|
1052
|
-
const message =
|
|
977
|
+
const message = `${time} [<-->] ${request.method} ${request.path}${modelInfo} streaming...`;
|
|
1053
978
|
this.printLog(message, request.isHistoryAccess);
|
|
1054
979
|
}
|
|
1055
980
|
}
|
|
1056
981
|
onRequestComplete(request) {
|
|
1057
982
|
this.activeRequests.delete(request.id);
|
|
983
|
+
const time = formatTime();
|
|
1058
984
|
const status = request.statusCode ?? 0;
|
|
1059
|
-
const duration = formatDuration
|
|
1060
|
-
const tokens = request.model ? formatTokens
|
|
985
|
+
const duration = formatDuration(request.durationMs ?? 0);
|
|
986
|
+
const tokens = request.model ? formatTokens(request.inputTokens, request.outputTokens) : "";
|
|
1061
987
|
const modelInfo = request.model ? ` ${request.model}` : "";
|
|
1062
988
|
const isError = request.status === "error" || status >= 400;
|
|
1063
989
|
const prefix = isError ? "[FAIL]" : "[ OK ]";
|
|
1064
990
|
const tokensPart = tokens ? ` ${tokens}` : "";
|
|
1065
|
-
let content = `${prefix} ${request.method} ${request.path} ${status} ${duration}${tokensPart}${modelInfo}`;
|
|
991
|
+
let content = `${time} ${prefix} ${request.method} ${request.path} ${status} ${duration}${tokensPart}${modelInfo}`;
|
|
1066
992
|
if (isError) {
|
|
1067
993
|
const errorInfo = request.error ? `: ${request.error}` : "";
|
|
1068
994
|
content += errorInfo;
|
|
@@ -1078,337 +1004,6 @@ var ConsoleRenderer = class {
|
|
|
1078
1004
|
}
|
|
1079
1005
|
};
|
|
1080
1006
|
|
|
1081
|
-
//#endregion
|
|
1082
|
-
//#region src/lib/tui/fullscreen-renderer.tsx
|
|
1083
|
-
const tuiState = {
|
|
1084
|
-
activeRequests: /* @__PURE__ */ new Map(),
|
|
1085
|
-
completedRequests: [],
|
|
1086
|
-
errorRequests: []
|
|
1087
|
-
};
|
|
1088
|
-
const listeners = [];
|
|
1089
|
-
function notifyListeners() {
|
|
1090
|
-
for (const listener of listeners) listener();
|
|
1091
|
-
}
|
|
1092
|
-
function formatDuration(ms) {
|
|
1093
|
-
if (ms < 1e3) return `${ms}ms`;
|
|
1094
|
-
return `${(ms / 1e3).toFixed(1)}s`;
|
|
1095
|
-
}
|
|
1096
|
-
function formatNumber(n) {
|
|
1097
|
-
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
1098
|
-
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
1099
|
-
return String(n);
|
|
1100
|
-
}
|
|
1101
|
-
function formatTokens(input, output) {
|
|
1102
|
-
if (input === void 0 || output === void 0) return "-";
|
|
1103
|
-
return `${formatNumber(input)}/${formatNumber(output)}`;
|
|
1104
|
-
}
|
|
1105
|
-
function getElapsedTime(startTime) {
|
|
1106
|
-
return formatDuration(Date.now() - startTime);
|
|
1107
|
-
}
|
|
1108
|
-
function TabHeader({ currentTab, counts }) {
|
|
1109
|
-
const tabs = [
|
|
1110
|
-
{
|
|
1111
|
-
key: "active",
|
|
1112
|
-
label: "Active",
|
|
1113
|
-
count: counts.active
|
|
1114
|
-
},
|
|
1115
|
-
{
|
|
1116
|
-
key: "completed",
|
|
1117
|
-
label: "Completed",
|
|
1118
|
-
count: counts.completed
|
|
1119
|
-
},
|
|
1120
|
-
{
|
|
1121
|
-
key: "errors",
|
|
1122
|
-
label: "Errors",
|
|
1123
|
-
count: counts.errors
|
|
1124
|
-
}
|
|
1125
|
-
];
|
|
1126
|
-
return /* @__PURE__ */ jsxs(Box, {
|
|
1127
|
-
borderStyle: "single",
|
|
1128
|
-
paddingX: 1,
|
|
1129
|
-
children: [tabs.map((tab, idx) => /* @__PURE__ */ jsxs(React.Fragment, { children: [idx > 0 && /* @__PURE__ */ jsx(Text, { children: " │ " }), /* @__PURE__ */ jsxs(Text, {
|
|
1130
|
-
bold: currentTab === tab.key,
|
|
1131
|
-
color: currentTab === tab.key ? "cyan" : void 0,
|
|
1132
|
-
inverse: currentTab === tab.key,
|
|
1133
|
-
children: [
|
|
1134
|
-
" ",
|
|
1135
|
-
"[",
|
|
1136
|
-
idx + 1,
|
|
1137
|
-
"] ",
|
|
1138
|
-
tab.label,
|
|
1139
|
-
" (",
|
|
1140
|
-
tab.count,
|
|
1141
|
-
")",
|
|
1142
|
-
" "
|
|
1143
|
-
]
|
|
1144
|
-
})] }, tab.key)), /* @__PURE__ */ jsx(Text, {
|
|
1145
|
-
dimColor: true,
|
|
1146
|
-
children: " │ Press 1/2/3 to switch tabs, q to quit"
|
|
1147
|
-
})]
|
|
1148
|
-
});
|
|
1149
|
-
}
|
|
1150
|
-
function getStatusColor(status) {
|
|
1151
|
-
if (status === "streaming") return "yellow";
|
|
1152
|
-
if (status === "queued") return "gray";
|
|
1153
|
-
return "blue";
|
|
1154
|
-
}
|
|
1155
|
-
function getStatusIcon(status) {
|
|
1156
|
-
if (status === "streaming") return "⟳";
|
|
1157
|
-
if (status === "queued") return "◷";
|
|
1158
|
-
return "●";
|
|
1159
|
-
}
|
|
1160
|
-
function ActiveRequestRow({ request }) {
|
|
1161
|
-
const [, setTick] = useState(0);
|
|
1162
|
-
useEffect(() => {
|
|
1163
|
-
const interval = setInterval(() => setTick((t) => t + 1), 1e3);
|
|
1164
|
-
return () => clearInterval(interval);
|
|
1165
|
-
}, []);
|
|
1166
|
-
const statusColor = getStatusColor(request.status);
|
|
1167
|
-
const statusIcon = getStatusIcon(request.status);
|
|
1168
|
-
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
1169
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
1170
|
-
color: statusColor,
|
|
1171
|
-
children: [statusIcon, " "]
|
|
1172
|
-
}),
|
|
1173
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1174
|
-
bold: true,
|
|
1175
|
-
children: request.method
|
|
1176
|
-
}),
|
|
1177
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1178
|
-
" ",
|
|
1179
|
-
request.path,
|
|
1180
|
-
" "
|
|
1181
|
-
] }),
|
|
1182
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
1183
|
-
dimColor: true,
|
|
1184
|
-
children: [getElapsedTime(request.startTime), " "]
|
|
1185
|
-
}),
|
|
1186
|
-
request.queuePosition !== void 0 && request.queuePosition > 0 && /* @__PURE__ */ jsxs(Text, {
|
|
1187
|
-
color: "gray",
|
|
1188
|
-
children: [
|
|
1189
|
-
"[queue #",
|
|
1190
|
-
request.queuePosition,
|
|
1191
|
-
"] "
|
|
1192
|
-
]
|
|
1193
|
-
}),
|
|
1194
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1195
|
-
color: "magenta",
|
|
1196
|
-
children: request.model
|
|
1197
|
-
})
|
|
1198
|
-
] });
|
|
1199
|
-
}
|
|
1200
|
-
function CompletedRequestRow({ request }) {
|
|
1201
|
-
const isError = request.status === "error" || (request.statusCode ?? 0) >= 400;
|
|
1202
|
-
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
1203
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
1204
|
-
color: isError ? "red" : "green",
|
|
1205
|
-
children: [isError ? "✗" : "✓", " "]
|
|
1206
|
-
}),
|
|
1207
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1208
|
-
bold: true,
|
|
1209
|
-
children: request.method
|
|
1210
|
-
}),
|
|
1211
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1212
|
-
" ",
|
|
1213
|
-
request.path,
|
|
1214
|
-
" "
|
|
1215
|
-
] }),
|
|
1216
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
1217
|
-
color: isError ? "red" : "green",
|
|
1218
|
-
children: [request.statusCode ?? "-", " "]
|
|
1219
|
-
}),
|
|
1220
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
1221
|
-
dimColor: true,
|
|
1222
|
-
children: [formatDuration(request.durationMs ?? 0), " "]
|
|
1223
|
-
}),
|
|
1224
|
-
/* @__PURE__ */ jsxs(Text, { children: [formatTokens(request.inputTokens, request.outputTokens), " "] }),
|
|
1225
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1226
|
-
color: "magenta",
|
|
1227
|
-
children: request.model
|
|
1228
|
-
})
|
|
1229
|
-
] });
|
|
1230
|
-
}
|
|
1231
|
-
function ErrorRequestRow({ request }) {
|
|
1232
|
-
return /* @__PURE__ */ jsxs(Box, {
|
|
1233
|
-
flexDirection: "column",
|
|
1234
|
-
children: [/* @__PURE__ */ jsxs(Box, { children: [
|
|
1235
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1236
|
-
color: "red",
|
|
1237
|
-
children: "✗ "
|
|
1238
|
-
}),
|
|
1239
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1240
|
-
bold: true,
|
|
1241
|
-
children: request.method
|
|
1242
|
-
}),
|
|
1243
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1244
|
-
" ",
|
|
1245
|
-
request.path,
|
|
1246
|
-
" "
|
|
1247
|
-
] }),
|
|
1248
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
1249
|
-
color: "red",
|
|
1250
|
-
children: [request.statusCode ?? "-", " "]
|
|
1251
|
-
}),
|
|
1252
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
1253
|
-
dimColor: true,
|
|
1254
|
-
children: [formatDuration(request.durationMs ?? 0), " "]
|
|
1255
|
-
}),
|
|
1256
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1257
|
-
color: "magenta",
|
|
1258
|
-
children: request.model
|
|
1259
|
-
})
|
|
1260
|
-
] }), request.error && /* @__PURE__ */ jsx(Box, {
|
|
1261
|
-
marginLeft: 2,
|
|
1262
|
-
children: /* @__PURE__ */ jsxs(Text, {
|
|
1263
|
-
color: "red",
|
|
1264
|
-
dimColor: true,
|
|
1265
|
-
children: ["└─ ", request.error]
|
|
1266
|
-
})
|
|
1267
|
-
})]
|
|
1268
|
-
});
|
|
1269
|
-
}
|
|
1270
|
-
function ContentPanel({ currentTab, activeList, completedList, errorList, contentHeight }) {
|
|
1271
|
-
if (currentTab === "active") {
|
|
1272
|
-
if (activeList.length === 0) return /* @__PURE__ */ jsx(Text, {
|
|
1273
|
-
dimColor: true,
|
|
1274
|
-
children: "No active requests"
|
|
1275
|
-
});
|
|
1276
|
-
return /* @__PURE__ */ jsx(Fragment, { children: activeList.slice(0, contentHeight).map((req) => /* @__PURE__ */ jsx(ActiveRequestRow, { request: req }, req.id)) });
|
|
1277
|
-
}
|
|
1278
|
-
if (currentTab === "completed") {
|
|
1279
|
-
if (completedList.length === 0) return /* @__PURE__ */ jsx(Text, {
|
|
1280
|
-
dimColor: true,
|
|
1281
|
-
children: "No completed requests"
|
|
1282
|
-
});
|
|
1283
|
-
return /* @__PURE__ */ jsx(Fragment, { children: completedList.slice(-contentHeight).reverse().map((req) => /* @__PURE__ */ jsx(CompletedRequestRow, { request: req }, req.id)) });
|
|
1284
|
-
}
|
|
1285
|
-
if (errorList.length === 0) return /* @__PURE__ */ jsx(Text, {
|
|
1286
|
-
dimColor: true,
|
|
1287
|
-
children: "No errors"
|
|
1288
|
-
});
|
|
1289
|
-
return /* @__PURE__ */ jsx(Fragment, { children: errorList.slice(-contentHeight).reverse().map((req) => /* @__PURE__ */ jsx(ErrorRequestRow, { request: req }, req.id)) });
|
|
1290
|
-
}
|
|
1291
|
-
function TuiApp() {
|
|
1292
|
-
const [currentTab, setCurrentTab] = useState("active");
|
|
1293
|
-
const [, forceUpdate] = useState(0);
|
|
1294
|
-
const { stdout } = useStdout();
|
|
1295
|
-
useEffect(() => {
|
|
1296
|
-
const listener = () => forceUpdate((n) => n + 1);
|
|
1297
|
-
listeners.push(listener);
|
|
1298
|
-
return () => {
|
|
1299
|
-
const idx = listeners.indexOf(listener);
|
|
1300
|
-
if (idx !== -1) listeners.splice(idx, 1);
|
|
1301
|
-
};
|
|
1302
|
-
}, []);
|
|
1303
|
-
useInput((input, key) => {
|
|
1304
|
-
switch (input) {
|
|
1305
|
-
case "1":
|
|
1306
|
-
setCurrentTab("active");
|
|
1307
|
-
break;
|
|
1308
|
-
case "2":
|
|
1309
|
-
setCurrentTab("completed");
|
|
1310
|
-
break;
|
|
1311
|
-
case "3":
|
|
1312
|
-
setCurrentTab("errors");
|
|
1313
|
-
break;
|
|
1314
|
-
default: if (input === "q" || key.ctrl && input === "c") process.exit(0);
|
|
1315
|
-
}
|
|
1316
|
-
});
|
|
1317
|
-
const activeList = Array.from(tuiState.activeRequests.values());
|
|
1318
|
-
const completedList = tuiState.completedRequests;
|
|
1319
|
-
const errorList = tuiState.errorRequests;
|
|
1320
|
-
const counts = {
|
|
1321
|
-
active: activeList.length,
|
|
1322
|
-
completed: completedList.length,
|
|
1323
|
-
errors: errorList.length
|
|
1324
|
-
};
|
|
1325
|
-
const terminalHeight = stdout.rows || 24;
|
|
1326
|
-
const contentHeight = terminalHeight - 3 - 1 - 2;
|
|
1327
|
-
return /* @__PURE__ */ jsxs(Box, {
|
|
1328
|
-
flexDirection: "column",
|
|
1329
|
-
height: terminalHeight,
|
|
1330
|
-
children: [
|
|
1331
|
-
/* @__PURE__ */ jsx(TabHeader, {
|
|
1332
|
-
currentTab,
|
|
1333
|
-
counts
|
|
1334
|
-
}),
|
|
1335
|
-
/* @__PURE__ */ jsx(Box, {
|
|
1336
|
-
flexDirection: "column",
|
|
1337
|
-
height: contentHeight,
|
|
1338
|
-
borderStyle: "single",
|
|
1339
|
-
paddingX: 1,
|
|
1340
|
-
overflow: "hidden",
|
|
1341
|
-
children: /* @__PURE__ */ jsx(ContentPanel, {
|
|
1342
|
-
currentTab,
|
|
1343
|
-
activeList,
|
|
1344
|
-
completedList,
|
|
1345
|
-
errorList,
|
|
1346
|
-
contentHeight
|
|
1347
|
-
})
|
|
1348
|
-
}),
|
|
1349
|
-
/* @__PURE__ */ jsx(Box, {
|
|
1350
|
-
paddingX: 1,
|
|
1351
|
-
children: /* @__PURE__ */ jsxs(Text, {
|
|
1352
|
-
dimColor: true,
|
|
1353
|
-
children: [
|
|
1354
|
-
"copilot-api │ Active: ",
|
|
1355
|
-
counts.active,
|
|
1356
|
-
" │ Completed: ",
|
|
1357
|
-
counts.completed,
|
|
1358
|
-
" ",
|
|
1359
|
-
"│ Errors: ",
|
|
1360
|
-
counts.errors
|
|
1361
|
-
]
|
|
1362
|
-
})
|
|
1363
|
-
})
|
|
1364
|
-
]
|
|
1365
|
-
});
|
|
1366
|
-
}
|
|
1367
|
-
/**
|
|
1368
|
-
* Fullscreen TUI renderer using Ink
|
|
1369
|
-
* Provides interactive terminal interface with tabs
|
|
1370
|
-
*/
|
|
1371
|
-
var FullscreenRenderer = class {
|
|
1372
|
-
inkInstance = null;
|
|
1373
|
-
maxHistory = 100;
|
|
1374
|
-
constructor(options) {
|
|
1375
|
-
if (options?.maxHistory !== void 0) this.maxHistory = options.maxHistory;
|
|
1376
|
-
}
|
|
1377
|
-
start() {
|
|
1378
|
-
if (this.inkInstance) return;
|
|
1379
|
-
this.inkInstance = render(/* @__PURE__ */ jsx(TuiApp, {}), {});
|
|
1380
|
-
}
|
|
1381
|
-
onRequestStart(request) {
|
|
1382
|
-
tuiState.activeRequests.set(request.id, { ...request });
|
|
1383
|
-
notifyListeners();
|
|
1384
|
-
}
|
|
1385
|
-
onRequestUpdate(id, update) {
|
|
1386
|
-
const request = tuiState.activeRequests.get(id);
|
|
1387
|
-
if (!request) return;
|
|
1388
|
-
Object.assign(request, update);
|
|
1389
|
-
notifyListeners();
|
|
1390
|
-
}
|
|
1391
|
-
onRequestComplete(request) {
|
|
1392
|
-
tuiState.activeRequests.delete(request.id);
|
|
1393
|
-
if (request.status === "error" || (request.statusCode ?? 0) >= 400) {
|
|
1394
|
-
tuiState.errorRequests.push({ ...request });
|
|
1395
|
-
while (tuiState.errorRequests.length > this.maxHistory) tuiState.errorRequests.shift();
|
|
1396
|
-
}
|
|
1397
|
-
tuiState.completedRequests.push({ ...request });
|
|
1398
|
-
while (tuiState.completedRequests.length > this.maxHistory) tuiState.completedRequests.shift();
|
|
1399
|
-
notifyListeners();
|
|
1400
|
-
}
|
|
1401
|
-
destroy() {
|
|
1402
|
-
if (this.inkInstance) {
|
|
1403
|
-
this.inkInstance.unmount();
|
|
1404
|
-
this.inkInstance = null;
|
|
1405
|
-
}
|
|
1406
|
-
tuiState.activeRequests.clear();
|
|
1407
|
-
tuiState.completedRequests = [];
|
|
1408
|
-
tuiState.errorRequests = [];
|
|
1409
|
-
}
|
|
1410
|
-
};
|
|
1411
|
-
|
|
1412
1007
|
//#endregion
|
|
1413
1008
|
//#region src/lib/tui/tracker.ts
|
|
1414
1009
|
function generateId() {
|
|
@@ -1572,16 +1167,9 @@ function tuiLogger() {
|
|
|
1572
1167
|
//#region src/lib/tui/index.ts
|
|
1573
1168
|
/**
|
|
1574
1169
|
* Initialize the TUI system
|
|
1575
|
-
* @param options.mode - "console" for simple log output (default), "fullscreen" for interactive TUI
|
|
1576
1170
|
*/
|
|
1577
1171
|
function initTui(options) {
|
|
1578
|
-
|
|
1579
|
-
const mode = options?.mode ?? "console";
|
|
1580
|
-
if (enabled) if (mode === "fullscreen") {
|
|
1581
|
-
const renderer = new FullscreenRenderer({ maxHistory: options?.historySize ?? 100 });
|
|
1582
|
-
requestTracker.setRenderer(renderer);
|
|
1583
|
-
renderer.start();
|
|
1584
|
-
} else {
|
|
1172
|
+
if (options?.enabled ?? process.stdout.isTTY) {
|
|
1585
1173
|
const renderer = new ConsoleRenderer();
|
|
1586
1174
|
requestTracker.setRenderer(renderer);
|
|
1587
1175
|
}
|
|
@@ -1675,7 +1263,7 @@ const getEncodeChatFunction = async (encoding) => {
|
|
|
1675
1263
|
* Get tokenizer type from model information
|
|
1676
1264
|
*/
|
|
1677
1265
|
const getTokenizerFromModel = (model) => {
|
|
1678
|
-
return model.capabilities
|
|
1266
|
+
return model.capabilities?.tokenizer || "o200k_base";
|
|
1679
1267
|
};
|
|
1680
1268
|
/**
|
|
1681
1269
|
* Get model-specific constants for token calculation.
|
|
@@ -1813,7 +1401,7 @@ const DEFAULT_CONFIG = {
|
|
|
1813
1401
|
*/
|
|
1814
1402
|
async function checkNeedsCompaction(payload, model, safetyMarginPercent = 10) {
|
|
1815
1403
|
const currentTokens = (await getTokenCount(payload, model)).input;
|
|
1816
|
-
const rawLimit = model.capabilities
|
|
1404
|
+
const rawLimit = model.capabilities?.limits?.max_prompt_tokens ?? 128e3;
|
|
1817
1405
|
const limit = Math.floor(rawLimit * (1 - safetyMarginPercent / 100));
|
|
1818
1406
|
return {
|
|
1819
1407
|
needed: currentTokens > limit,
|
|
@@ -1892,7 +1480,7 @@ async function autoCompact(payload, model, config = {}) {
|
|
|
1892
1480
|
...config
|
|
1893
1481
|
};
|
|
1894
1482
|
const originalTokens = (await getTokenCount(payload, model)).input;
|
|
1895
|
-
const rawLimit = model.capabilities
|
|
1483
|
+
const rawLimit = model.capabilities?.limits?.max_prompt_tokens ?? 128e3;
|
|
1896
1484
|
const limit = Math.floor(rawLimit * (1 - cfg.safetyMarginPercent / 100));
|
|
1897
1485
|
if (originalTokens <= limit) return {
|
|
1898
1486
|
payload,
|
|
@@ -2051,6 +1639,9 @@ const createChatCompletions = async (payload) => {
|
|
|
2051
1639
|
|
|
2052
1640
|
//#endregion
|
|
2053
1641
|
//#region src/routes/chat-completions/handler.ts
|
|
1642
|
+
function getModelMaxOutputTokens(model) {
|
|
1643
|
+
return model?.capabilities?.limits?.max_output_tokens;
|
|
1644
|
+
}
|
|
2054
1645
|
async function handleCompletion$1(c) {
|
|
2055
1646
|
const originalPayload = await c.req.json();
|
|
2056
1647
|
consola.debug("Request payload:", JSON.stringify(originalPayload).slice(-400));
|
|
@@ -2078,7 +1669,7 @@ async function handleCompletion$1(c) {
|
|
|
2078
1669
|
if (compactResult) ctx.compactResult = compactResult;
|
|
2079
1670
|
const payload = isNullish(finalPayload.max_tokens) ? {
|
|
2080
1671
|
...finalPayload,
|
|
2081
|
-
max_tokens: selectedModel
|
|
1672
|
+
max_tokens: getModelMaxOutputTokens(selectedModel)
|
|
2082
1673
|
} : finalPayload;
|
|
2083
1674
|
if (isNullish(originalPayload.max_tokens)) consola.debug("Set max_tokens to:", JSON.stringify(payload.max_tokens));
|
|
2084
1675
|
if (state.manualApprove) await awaitApproval();
|
|
@@ -4460,20 +4051,20 @@ modelRoutes.get("/", async (c) => {
|
|
|
4460
4051
|
created_at: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
4461
4052
|
owned_by: model.vendor,
|
|
4462
4053
|
display_name: model.name,
|
|
4463
|
-
capabilities: {
|
|
4054
|
+
capabilities: model.capabilities ? {
|
|
4464
4055
|
family: model.capabilities.family,
|
|
4465
4056
|
type: model.capabilities.type,
|
|
4466
4057
|
tokenizer: model.capabilities.tokenizer,
|
|
4467
4058
|
limits: {
|
|
4468
|
-
max_context_window_tokens: model.capabilities.limits
|
|
4469
|
-
max_output_tokens: model.capabilities.limits
|
|
4470
|
-
max_prompt_tokens: model.capabilities.limits
|
|
4059
|
+
max_context_window_tokens: model.capabilities.limits?.max_context_window_tokens,
|
|
4060
|
+
max_output_tokens: model.capabilities.limits?.max_output_tokens,
|
|
4061
|
+
max_prompt_tokens: model.capabilities.limits?.max_prompt_tokens
|
|
4471
4062
|
},
|
|
4472
4063
|
supports: {
|
|
4473
|
-
tool_calls: model.capabilities.supports
|
|
4474
|
-
parallel_tool_calls: model.capabilities.supports
|
|
4064
|
+
tool_calls: model.capabilities.supports?.tool_calls,
|
|
4065
|
+
parallel_tool_calls: model.capabilities.supports?.parallel_tool_calls
|
|
4475
4066
|
}
|
|
4476
|
-
}
|
|
4067
|
+
} : void 0
|
|
4477
4068
|
}));
|
|
4478
4069
|
return c.json({
|
|
4479
4070
|
object: "list",
|
|
@@ -4539,6 +4130,14 @@ server.route("/history", historyRoutes);
|
|
|
4539
4130
|
|
|
4540
4131
|
//#endregion
|
|
4541
4132
|
//#region src/start.ts
|
|
4133
|
+
function formatModelInfo(model) {
|
|
4134
|
+
const limits = model.capabilities?.limits;
|
|
4135
|
+
const contextK = limits?.max_prompt_tokens ? `${Math.round(limits.max_prompt_tokens / 1e3)}k` : "?";
|
|
4136
|
+
const outputK = limits?.max_output_tokens ? `${Math.round(limits.max_output_tokens / 1e3)}k` : "?";
|
|
4137
|
+
const features = [model.capabilities?.supports?.tool_calls && "tools", model.preview && "preview"].filter(Boolean).join(", ");
|
|
4138
|
+
const featureStr = features ? ` (${features})` : "";
|
|
4139
|
+
return ` - ${model.id.padEnd(28)} context: ${contextK.padStart(5)}, output: ${outputK.padStart(4)}${featureStr}`;
|
|
4140
|
+
}
|
|
4542
4141
|
async function runServer(options) {
|
|
4543
4142
|
if (options.proxyEnv) initProxyFromEnv();
|
|
4544
4143
|
if (options.verbose) {
|
|
@@ -4558,10 +4157,7 @@ async function runServer(options) {
|
|
|
4558
4157
|
const limitText = options.historyLimit === 0 ? "unlimited" : `max ${options.historyLimit}`;
|
|
4559
4158
|
consola.info(`History recording enabled (${limitText} entries)`);
|
|
4560
4159
|
}
|
|
4561
|
-
initTui({
|
|
4562
|
-
enabled: true,
|
|
4563
|
-
mode: options.tui
|
|
4564
|
-
});
|
|
4160
|
+
initTui({ enabled: true });
|
|
4565
4161
|
await ensurePaths();
|
|
4566
4162
|
await cacheVSCodeVersion();
|
|
4567
4163
|
if (options.githubToken) {
|
|
@@ -4570,7 +4166,7 @@ async function runServer(options) {
|
|
|
4570
4166
|
} else await setupGitHubToken();
|
|
4571
4167
|
await setupCopilotToken();
|
|
4572
4168
|
await cacheModels();
|
|
4573
|
-
consola.info(`Available models
|
|
4169
|
+
consola.info(`Available models:\n${state.models?.data.map((m) => formatModelInfo(m)).join("\n")}`);
|
|
4574
4170
|
const serverUrl = `http://${options.host ?? "localhost"}:${options.port}`;
|
|
4575
4171
|
if (options.claudeCode) {
|
|
4576
4172
|
invariant(state.models, "Models should be loaded by now");
|
|
@@ -4683,11 +4279,6 @@ const start = defineCommand({
|
|
|
4683
4279
|
default: "1000",
|
|
4684
4280
|
description: "Maximum number of history entries to keep in memory (0 = unlimited)"
|
|
4685
4281
|
},
|
|
4686
|
-
tui: {
|
|
4687
|
-
type: "string",
|
|
4688
|
-
default: "console",
|
|
4689
|
-
description: "TUI mode: 'console' for simple log output, 'fullscreen' for interactive terminal UI with tabs"
|
|
4690
|
-
},
|
|
4691
4282
|
"auto-compact": {
|
|
4692
4283
|
type: "boolean",
|
|
4693
4284
|
default: false,
|
|
@@ -4711,7 +4302,6 @@ const start = defineCommand({
|
|
|
4711
4302
|
proxyEnv: args["proxy-env"],
|
|
4712
4303
|
history: args.history,
|
|
4713
4304
|
historyLimit: Number.parseInt(args["history-limit"], 10),
|
|
4714
|
-
tui: args.tui,
|
|
4715
4305
|
autoCompact: args["auto-compact"]
|
|
4716
4306
|
});
|
|
4717
4307
|
}
|
|
@@ -4719,7 +4309,7 @@ const start = defineCommand({
|
|
|
4719
4309
|
|
|
4720
4310
|
//#endregion
|
|
4721
4311
|
//#region src/main.ts
|
|
4722
|
-
consola.options.formatOptions.date =
|
|
4312
|
+
consola.options.formatOptions.date = false;
|
|
4723
4313
|
const main = defineCommand({
|
|
4724
4314
|
meta: {
|
|
4725
4315
|
name: "copilot-api",
|