@hsupu/copilot-api 0.7.1 → 0.7.3
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 +45 -376
- package/dist/main.js.map +1 -1
- package/package.json +2 -2
package/dist/main.js
CHANGED
|
@@ -12,9 +12,6 @@ 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 { Box, Text, render, useInput, useStdout } from "ink";
|
|
16
|
-
import React, { useEffect, useState } from "react";
|
|
17
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
18
15
|
import { Hono } from "hono";
|
|
19
16
|
import { cors } from "hono/cors";
|
|
20
17
|
import { streamSSE } from "hono/streaming";
|
|
@@ -954,18 +951,24 @@ var require_picocolors = /* @__PURE__ */ __commonJS({ "node_modules/picocolors/p
|
|
|
954
951
|
//#region src/lib/tui/console-renderer.ts
|
|
955
952
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
956
953
|
const CLEAR_LINE = "\x1B[2K\r";
|
|
957
|
-
function
|
|
954
|
+
function formatTime(date = /* @__PURE__ */ new Date()) {
|
|
955
|
+
const h = String(date.getHours()).padStart(2, "0");
|
|
956
|
+
const m = String(date.getMinutes()).padStart(2, "0");
|
|
957
|
+
const s = String(date.getSeconds()).padStart(2, "0");
|
|
958
|
+
return `${h}:${m}:${s}`;
|
|
959
|
+
}
|
|
960
|
+
function formatDuration(ms) {
|
|
958
961
|
if (ms < 1e3) return `${ms}ms`;
|
|
959
962
|
return `${(ms / 1e3).toFixed(1)}s`;
|
|
960
963
|
}
|
|
961
|
-
function formatNumber
|
|
964
|
+
function formatNumber(n) {
|
|
962
965
|
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
963
966
|
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
964
967
|
return String(n);
|
|
965
968
|
}
|
|
966
|
-
function formatTokens
|
|
969
|
+
function formatTokens(input, output) {
|
|
967
970
|
if (input === void 0 || output === void 0) return "-";
|
|
968
|
-
return `${formatNumber
|
|
971
|
+
return `${formatNumber(input)}/${formatNumber(output)}`;
|
|
969
972
|
}
|
|
970
973
|
/**
|
|
971
974
|
* Console renderer that shows request lifecycle with apt-get style footer
|
|
@@ -1037,9 +1040,10 @@ var ConsoleRenderer = class {
|
|
|
1037
1040
|
onRequestStart(request) {
|
|
1038
1041
|
this.activeRequests.set(request.id, request);
|
|
1039
1042
|
if (this.showActive) {
|
|
1043
|
+
const time = formatTime();
|
|
1040
1044
|
const modelInfo = request.model ? ` ${request.model}` : "";
|
|
1041
1045
|
const queueInfo = request.queuePosition !== void 0 && request.queuePosition > 0 ? ` [q#${request.queuePosition}]` : "";
|
|
1042
|
-
const message =
|
|
1046
|
+
const message = `${time} [....] ${request.method} ${request.path}${modelInfo}${queueInfo}`;
|
|
1043
1047
|
this.printLog(message, request.isHistoryAccess);
|
|
1044
1048
|
}
|
|
1045
1049
|
}
|
|
@@ -1048,21 +1052,23 @@ var ConsoleRenderer = class {
|
|
|
1048
1052
|
if (!request) return;
|
|
1049
1053
|
Object.assign(request, update);
|
|
1050
1054
|
if (this.showActive && update.status === "streaming") {
|
|
1055
|
+
const time = formatTime();
|
|
1051
1056
|
const modelInfo = request.model ? ` ${request.model}` : "";
|
|
1052
|
-
const message =
|
|
1057
|
+
const message = `${time} [<-->] ${request.method} ${request.path}${modelInfo} streaming...`;
|
|
1053
1058
|
this.printLog(message, request.isHistoryAccess);
|
|
1054
1059
|
}
|
|
1055
1060
|
}
|
|
1056
1061
|
onRequestComplete(request) {
|
|
1057
1062
|
this.activeRequests.delete(request.id);
|
|
1063
|
+
const time = formatTime();
|
|
1058
1064
|
const status = request.statusCode ?? 0;
|
|
1059
|
-
const duration = formatDuration
|
|
1060
|
-
const tokens = request.model ? formatTokens
|
|
1065
|
+
const duration = formatDuration(request.durationMs ?? 0);
|
|
1066
|
+
const tokens = request.model ? formatTokens(request.inputTokens, request.outputTokens) : "";
|
|
1061
1067
|
const modelInfo = request.model ? ` ${request.model}` : "";
|
|
1062
1068
|
const isError = request.status === "error" || status >= 400;
|
|
1063
1069
|
const prefix = isError ? "[FAIL]" : "[ OK ]";
|
|
1064
1070
|
const tokensPart = tokens ? ` ${tokens}` : "";
|
|
1065
|
-
let content = `${prefix} ${request.method} ${request.path} ${status} ${duration}${tokensPart}${modelInfo}`;
|
|
1071
|
+
let content = `${time} ${prefix} ${request.method} ${request.path} ${status} ${duration}${tokensPart}${modelInfo}`;
|
|
1066
1072
|
if (isError) {
|
|
1067
1073
|
const errorInfo = request.error ? `: ${request.error}` : "";
|
|
1068
1074
|
content += errorInfo;
|
|
@@ -1078,337 +1084,6 @@ var ConsoleRenderer = class {
|
|
|
1078
1084
|
}
|
|
1079
1085
|
};
|
|
1080
1086
|
|
|
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
1087
|
//#endregion
|
|
1413
1088
|
//#region src/lib/tui/tracker.ts
|
|
1414
1089
|
function generateId() {
|
|
@@ -1572,16 +1247,9 @@ function tuiLogger() {
|
|
|
1572
1247
|
//#region src/lib/tui/index.ts
|
|
1573
1248
|
/**
|
|
1574
1249
|
* Initialize the TUI system
|
|
1575
|
-
* @param options.mode - "console" for simple log output (default), "fullscreen" for interactive TUI
|
|
1576
1250
|
*/
|
|
1577
1251
|
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 {
|
|
1252
|
+
if (options?.enabled ?? process.stdout.isTTY) {
|
|
1585
1253
|
const renderer = new ConsoleRenderer();
|
|
1586
1254
|
requestTracker.setRenderer(renderer);
|
|
1587
1255
|
}
|
|
@@ -1675,7 +1343,7 @@ const getEncodeChatFunction = async (encoding) => {
|
|
|
1675
1343
|
* Get tokenizer type from model information
|
|
1676
1344
|
*/
|
|
1677
1345
|
const getTokenizerFromModel = (model) => {
|
|
1678
|
-
return model.capabilities
|
|
1346
|
+
return model.capabilities?.tokenizer || "o200k_base";
|
|
1679
1347
|
};
|
|
1680
1348
|
/**
|
|
1681
1349
|
* Get model-specific constants for token calculation.
|
|
@@ -1813,7 +1481,7 @@ const DEFAULT_CONFIG = {
|
|
|
1813
1481
|
*/
|
|
1814
1482
|
async function checkNeedsCompaction(payload, model, safetyMarginPercent = 10) {
|
|
1815
1483
|
const currentTokens = (await getTokenCount(payload, model)).input;
|
|
1816
|
-
const rawLimit = model.capabilities
|
|
1484
|
+
const rawLimit = model.capabilities?.limits?.max_prompt_tokens ?? 128e3;
|
|
1817
1485
|
const limit = Math.floor(rawLimit * (1 - safetyMarginPercent / 100));
|
|
1818
1486
|
return {
|
|
1819
1487
|
needed: currentTokens > limit,
|
|
@@ -1892,7 +1560,7 @@ async function autoCompact(payload, model, config = {}) {
|
|
|
1892
1560
|
...config
|
|
1893
1561
|
};
|
|
1894
1562
|
const originalTokens = (await getTokenCount(payload, model)).input;
|
|
1895
|
-
const rawLimit = model.capabilities
|
|
1563
|
+
const rawLimit = model.capabilities?.limits?.max_prompt_tokens ?? 128e3;
|
|
1896
1564
|
const limit = Math.floor(rawLimit * (1 - cfg.safetyMarginPercent / 100));
|
|
1897
1565
|
if (originalTokens <= limit) return {
|
|
1898
1566
|
payload,
|
|
@@ -2051,11 +1719,14 @@ const createChatCompletions = async (payload) => {
|
|
|
2051
1719
|
|
|
2052
1720
|
//#endregion
|
|
2053
1721
|
//#region src/routes/chat-completions/handler.ts
|
|
1722
|
+
function getModelMaxOutputTokens(model) {
|
|
1723
|
+
return model?.capabilities?.limits?.max_output_tokens;
|
|
1724
|
+
}
|
|
2054
1725
|
async function handleCompletion$1(c) {
|
|
2055
|
-
const startTime = Date.now();
|
|
2056
1726
|
const originalPayload = await c.req.json();
|
|
2057
1727
|
consola.debug("Request payload:", JSON.stringify(originalPayload).slice(-400));
|
|
2058
1728
|
const trackingId = c.get("trackingId");
|
|
1729
|
+
const startTime = (trackingId ? requestTracker.getRequest(trackingId) : void 0)?.startTime ?? Date.now();
|
|
2059
1730
|
updateTrackerModel$1(trackingId, originalPayload.model);
|
|
2060
1731
|
const ctx = {
|
|
2061
1732
|
historyId: recordRequest("openai", {
|
|
@@ -2078,7 +1749,7 @@ async function handleCompletion$1(c) {
|
|
|
2078
1749
|
if (compactResult) ctx.compactResult = compactResult;
|
|
2079
1750
|
const payload = isNullish(finalPayload.max_tokens) ? {
|
|
2080
1751
|
...finalPayload,
|
|
2081
|
-
max_tokens: selectedModel
|
|
1752
|
+
max_tokens: getModelMaxOutputTokens(selectedModel)
|
|
2082
1753
|
} : finalPayload;
|
|
2083
1754
|
if (isNullish(originalPayload.max_tokens)) consola.debug("Set max_tokens to:", JSON.stringify(payload.max_tokens));
|
|
2084
1755
|
if (state.manualApprove) await awaitApproval();
|
|
@@ -4017,10 +3688,10 @@ function translateErrorToAnthropicErrorEvent() {
|
|
|
4017
3688
|
//#endregion
|
|
4018
3689
|
//#region src/routes/messages/handler.ts
|
|
4019
3690
|
async function handleCompletion(c) {
|
|
4020
|
-
const startTime = Date.now();
|
|
4021
3691
|
const anthropicPayload = await c.req.json();
|
|
4022
3692
|
consola.debug("Anthropic request payload:", JSON.stringify(anthropicPayload));
|
|
4023
3693
|
const trackingId = c.get("trackingId");
|
|
3694
|
+
const startTime = (trackingId ? requestTracker.getRequest(trackingId) : void 0)?.startTime ?? Date.now();
|
|
4024
3695
|
updateTrackerModel(trackingId, anthropicPayload.model);
|
|
4025
3696
|
const ctx = {
|
|
4026
3697
|
historyId: recordRequest("anthropic", {
|
|
@@ -4460,20 +4131,20 @@ modelRoutes.get("/", async (c) => {
|
|
|
4460
4131
|
created_at: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
4461
4132
|
owned_by: model.vendor,
|
|
4462
4133
|
display_name: model.name,
|
|
4463
|
-
capabilities: {
|
|
4134
|
+
capabilities: model.capabilities ? {
|
|
4464
4135
|
family: model.capabilities.family,
|
|
4465
4136
|
type: model.capabilities.type,
|
|
4466
4137
|
tokenizer: model.capabilities.tokenizer,
|
|
4467
4138
|
limits: {
|
|
4468
|
-
max_context_window_tokens: model.capabilities.limits
|
|
4469
|
-
max_output_tokens: model.capabilities.limits
|
|
4470
|
-
max_prompt_tokens: model.capabilities.limits
|
|
4139
|
+
max_context_window_tokens: model.capabilities.limits?.max_context_window_tokens,
|
|
4140
|
+
max_output_tokens: model.capabilities.limits?.max_output_tokens,
|
|
4141
|
+
max_prompt_tokens: model.capabilities.limits?.max_prompt_tokens
|
|
4471
4142
|
},
|
|
4472
4143
|
supports: {
|
|
4473
|
-
tool_calls: model.capabilities.supports
|
|
4474
|
-
parallel_tool_calls: model.capabilities.supports
|
|
4144
|
+
tool_calls: model.capabilities.supports?.tool_calls,
|
|
4145
|
+
parallel_tool_calls: model.capabilities.supports?.parallel_tool_calls
|
|
4475
4146
|
}
|
|
4476
|
-
}
|
|
4147
|
+
} : void 0
|
|
4477
4148
|
}));
|
|
4478
4149
|
return c.json({
|
|
4479
4150
|
object: "list",
|
|
@@ -4539,6 +4210,14 @@ server.route("/history", historyRoutes);
|
|
|
4539
4210
|
|
|
4540
4211
|
//#endregion
|
|
4541
4212
|
//#region src/start.ts
|
|
4213
|
+
function formatModelInfo(model) {
|
|
4214
|
+
const limits = model.capabilities?.limits;
|
|
4215
|
+
const contextK = limits?.max_prompt_tokens ? `${Math.round(limits.max_prompt_tokens / 1e3)}k` : "?";
|
|
4216
|
+
const outputK = limits?.max_output_tokens ? `${Math.round(limits.max_output_tokens / 1e3)}k` : "?";
|
|
4217
|
+
const features = [model.capabilities?.supports?.tool_calls && "tools", model.preview && "preview"].filter(Boolean).join(", ");
|
|
4218
|
+
const featureStr = features ? ` (${features})` : "";
|
|
4219
|
+
return ` - ${model.id.padEnd(28)} context: ${contextK.padStart(5)}, output: ${outputK.padStart(4)}${featureStr}`;
|
|
4220
|
+
}
|
|
4542
4221
|
async function runServer(options) {
|
|
4543
4222
|
if (options.proxyEnv) initProxyFromEnv();
|
|
4544
4223
|
if (options.verbose) {
|
|
@@ -4558,10 +4237,7 @@ async function runServer(options) {
|
|
|
4558
4237
|
const limitText = options.historyLimit === 0 ? "unlimited" : `max ${options.historyLimit}`;
|
|
4559
4238
|
consola.info(`History recording enabled (${limitText} entries)`);
|
|
4560
4239
|
}
|
|
4561
|
-
initTui({
|
|
4562
|
-
enabled: true,
|
|
4563
|
-
mode: options.tui
|
|
4564
|
-
});
|
|
4240
|
+
initTui({ enabled: true });
|
|
4565
4241
|
await ensurePaths();
|
|
4566
4242
|
await cacheVSCodeVersion();
|
|
4567
4243
|
if (options.githubToken) {
|
|
@@ -4570,7 +4246,7 @@ async function runServer(options) {
|
|
|
4570
4246
|
} else await setupGitHubToken();
|
|
4571
4247
|
await setupCopilotToken();
|
|
4572
4248
|
await cacheModels();
|
|
4573
|
-
consola.info(`Available models
|
|
4249
|
+
consola.info(`Available models:\n${state.models?.data.map((m) => formatModelInfo(m)).join("\n")}`);
|
|
4574
4250
|
const serverUrl = `http://${options.host ?? "localhost"}:${options.port}`;
|
|
4575
4251
|
if (options.claudeCode) {
|
|
4576
4252
|
invariant(state.models, "Models should be loaded by now");
|
|
@@ -4683,11 +4359,6 @@ const start = defineCommand({
|
|
|
4683
4359
|
default: "1000",
|
|
4684
4360
|
description: "Maximum number of history entries to keep in memory (0 = unlimited)"
|
|
4685
4361
|
},
|
|
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
4362
|
"auto-compact": {
|
|
4692
4363
|
type: "boolean",
|
|
4693
4364
|
default: false,
|
|
@@ -4711,7 +4382,6 @@ const start = defineCommand({
|
|
|
4711
4382
|
proxyEnv: args["proxy-env"],
|
|
4712
4383
|
history: args.history,
|
|
4713
4384
|
historyLimit: Number.parseInt(args["history-limit"], 10),
|
|
4714
|
-
tui: args.tui,
|
|
4715
4385
|
autoCompact: args["auto-compact"]
|
|
4716
4386
|
});
|
|
4717
4387
|
}
|
|
@@ -4719,7 +4389,6 @@ const start = defineCommand({
|
|
|
4719
4389
|
|
|
4720
4390
|
//#endregion
|
|
4721
4391
|
//#region src/main.ts
|
|
4722
|
-
consola.options.formatOptions.date = true;
|
|
4723
4392
|
const main = defineCommand({
|
|
4724
4393
|
meta: {
|
|
4725
4394
|
name: "copilot-api",
|