@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 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 formatDuration$1(ms) {
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$1(n) {
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$1(input, output) {
969
+ function formatTokens(input, output) {
967
970
  if (input === void 0 || output === void 0) return "-";
968
- return `${formatNumber$1(input)}/${formatNumber$1(output)}`;
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 = `[....] ${request.method} ${request.path}${modelInfo}${queueInfo}`;
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 = `[<-->] ${request.method} ${request.path}${modelInfo} streaming...`;
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$1(request.durationMs ?? 0);
1060
- const tokens = request.model ? formatTokens$1(request.inputTokens, request.outputTokens) : "";
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
- const enabled = options?.enabled ?? process.stdout.isTTY;
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.tokenizer || "o200k_base";
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.limits.max_prompt_tokens ?? 128e3;
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.limits.max_prompt_tokens ?? 128e3;
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?.capabilities.limits.max_output_tokens
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.max_context_window_tokens,
4469
- max_output_tokens: model.capabilities.limits.max_output_tokens,
4470
- max_prompt_tokens: model.capabilities.limits.max_prompt_tokens
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.tool_calls,
4474
- parallel_tool_calls: model.capabilities.supports.parallel_tool_calls
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: \n${state.models?.data.map((model) => `- ${model.id}`).join("\n")}`);
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",