copilot-api-plus 1.0.5 → 1.0.7

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
@@ -1,7 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { PATHS, cacheModels, cacheVSCodeVersion, clearGithubToken, ensurePaths, isNullish, setupCopilotToken, setupGitHubToken, sleep } from "./token-CRn8c1A7.js";
3
- import { GITHUB_API_BASE_URL, copilotBaseUrl, copilotHeaders, githubHeaders, state } from "./get-user-DalX7epg.js";
4
- import { HTTPError, forwardError } from "./error-Cmeg4mmB.js";
2
+ import { PATHS, ensurePaths } from "./paths-Ch0ixSo2.js";
3
+ import { state } from "./state-DAw5jMjc.js";
4
+ import { GITHUB_API_BASE_URL, copilotBaseUrl, copilotHeaders, githubHeaders } from "./get-user-DgPgvnrS.js";
5
+ import { HTTPError, forwardError } from "./error-CvU5otz-.js";
6
+ import { cacheModels, cacheVSCodeVersion, clearGithubToken, isNullish, setupCopilotToken, setupGitHubToken, sleep } from "./token-DYeOMeid.js";
7
+ import { clearZenAuth, getZenAuthPath } from "./auth-C5zV8JbW.js";
5
8
  import { defineCommand, runMain } from "citty";
6
9
  import consola from "consola";
7
10
  import fs from "node:fs/promises";
@@ -177,19 +180,68 @@ const debug = defineCommand({
177
180
 
178
181
  //#endregion
179
182
  //#region src/logout.ts
180
- async function runLogout() {
183
+ async function runLogout(options) {
181
184
  await ensurePaths();
182
- await clearGithubToken();
183
- consola.success("Logged out successfully");
184
- consola.info(`Token file location: ${PATHS.GITHUB_TOKEN_PATH}`);
185
+ if (options.all) {
186
+ await clearGithubToken();
187
+ await clearZenAuth();
188
+ consola.success("Logged out from all services");
189
+ consola.info(`GitHub token: ${PATHS.GITHUB_TOKEN_PATH}`);
190
+ consola.info(`Zen API key: ${getZenAuthPath()}`);
191
+ return;
192
+ }
193
+ if (options.zen) {
194
+ await clearZenAuth();
195
+ consola.success("Logged out from OpenCode Zen");
196
+ consola.info(`Zen API key location: ${getZenAuthPath()}`);
197
+ return;
198
+ }
199
+ const choice = await consola.prompt("Which credentials do you want to clear?", {
200
+ type: "select",
201
+ options: [
202
+ "GitHub Copilot token",
203
+ "OpenCode Zen API key",
204
+ "Both (all credentials)"
205
+ ]
206
+ });
207
+ if (choice === "GitHub Copilot token") {
208
+ await clearGithubToken();
209
+ consola.success("Logged out from GitHub Copilot");
210
+ consola.info(`Token file location: ${PATHS.GITHUB_TOKEN_PATH}`);
211
+ } else if (choice === "OpenCode Zen API key") {
212
+ await clearZenAuth();
213
+ consola.success("Logged out from OpenCode Zen");
214
+ consola.info(`Zen API key location: ${getZenAuthPath()}`);
215
+ } else if (choice === "Both (all credentials)") {
216
+ await clearGithubToken();
217
+ await clearZenAuth();
218
+ consola.success("Logged out from all services");
219
+ }
185
220
  }
186
221
  const logout = defineCommand({
187
222
  meta: {
188
223
  name: "logout",
189
- description: "Clear stored GitHub token and logout"
224
+ description: "Clear stored credentials and logout"
190
225
  },
191
- run() {
192
- return runLogout();
226
+ args: {
227
+ zen: {
228
+ alias: "z",
229
+ type: "boolean",
230
+ default: false,
231
+ description: "Clear only OpenCode Zen API key"
232
+ },
233
+ all: {
234
+ alias: "a",
235
+ type: "boolean",
236
+ default: false,
237
+ description: "Clear all credentials (GitHub and Zen)"
238
+ }
239
+ },
240
+ run({ args }) {
241
+ return runLogout({
242
+ zen: args.zen,
243
+ all: args.all
244
+ });
193
245
  }
194
246
  });
195
247
 
@@ -688,6 +740,14 @@ function translateModelName(model) {
688
740
  if (supportedModels.includes(modelWithDot)) return modelWithDot;
689
741
  const modelWithDash = model.replace(/(\d+)\.(\d+)/, "$1-$2");
690
742
  if (supportedModels.includes(modelWithDash)) return modelWithDash;
743
+ for (const [oldFormat, newFormat] of Object.entries({
744
+ "claude-3-5-sonnet": "claude-sonnet-4.5",
745
+ "claude-3-sonnet": "claude-sonnet-4",
746
+ "claude-3-5-opus": "claude-opus-4.5",
747
+ "claude-3-opus": "claude-opus-4.5",
748
+ "claude-3-5-haiku": "claude-haiku-4.5",
749
+ "claude-3-haiku": "claude-haiku-4.5"
750
+ })) if (modelBase.startsWith(oldFormat) && supportedModels.includes(newFormat)) return newFormat;
691
751
  const modelFamily = model.includes("opus") ? "opus" : model.includes("sonnet") ? "sonnet" : model.includes("haiku") ? "haiku" : null;
692
752
  if (modelFamily) {
693
753
  const familyModel = supportedModels.find((m) => m.includes(modelFamily));
@@ -1021,11 +1081,8 @@ function translateChunkToAnthropicEvents(chunk, state$1) {
1021
1081
  async function handleCompletion(c) {
1022
1082
  await checkRateLimit(state);
1023
1083
  const anthropicPayload = await c.req.json();
1024
- consola.info(`[Messages] Received model: "${anthropicPayload.model}"`);
1025
1084
  consola.debug("Anthropic request payload:", JSON.stringify(anthropicPayload));
1026
1085
  const openAIPayload = translateToOpenAI(anthropicPayload);
1027
- consola.info(`[Messages] Translated to: "${openAIPayload.model}"`);
1028
- consola.info(`[Messages] Available models: ${state.models?.data.map((m) => m.id).join(", ") ?? "none"}`);
1029
1086
  consola.debug("Translated OpenAI request payload:", JSON.stringify(openAIPayload));
1030
1087
  if (state.manualApprove) await awaitApproval();
1031
1088
  const response = await createChatCompletions(openAIPayload);
@@ -1132,6 +1189,142 @@ usageRoute.get("/", async (c) => {
1132
1189
  }
1133
1190
  });
1134
1191
 
1192
+ //#endregion
1193
+ //#region src/services/zen/create-chat-completions.ts
1194
+ /**
1195
+ * Create chat completions via OpenCode Zen
1196
+ */
1197
+ async function createZenChatCompletions(request, signal) {
1198
+ const apiKey = state.zenApiKey;
1199
+ if (!apiKey) throw new Error("Zen API key not configured");
1200
+ consola.debug(`Zen chat completions request for model: ${request.model}`);
1201
+ const response = await fetch("https://opencode.ai/zen/v1/chat/completions", {
1202
+ method: "POST",
1203
+ headers: {
1204
+ "Content-Type": "application/json",
1205
+ Authorization: `Bearer ${apiKey}`
1206
+ },
1207
+ body: JSON.stringify(request),
1208
+ signal
1209
+ });
1210
+ if (!response.ok) {
1211
+ const errorText = await response.text();
1212
+ consola.error(`Zen API error: ${response.status} ${errorText}`);
1213
+ throw new Error(`Zen API error: ${response.status} ${errorText}`);
1214
+ }
1215
+ return response;
1216
+ }
1217
+
1218
+ //#endregion
1219
+ //#region src/routes/zen/chat-completions/route.ts
1220
+ const zenCompletionRoutes = new Hono();
1221
+ zenCompletionRoutes.post("/", async (c) => {
1222
+ if (!state.zenMode || !state.zenApiKey) return c.json({ error: "Zen mode is not enabled. Start with --zen flag." }, 400);
1223
+ try {
1224
+ const body = await c.req.json();
1225
+ consola.debug("Zen chat completion request:", body.model);
1226
+ const response = await createZenChatCompletions(body);
1227
+ if (body.stream) {
1228
+ const headers = new Headers();
1229
+ headers.set("Content-Type", "text/event-stream");
1230
+ headers.set("Cache-Control", "no-cache");
1231
+ headers.set("Connection", "keep-alive");
1232
+ return new Response(response.body, {
1233
+ status: response.status,
1234
+ headers
1235
+ });
1236
+ }
1237
+ const data = await response.json();
1238
+ return c.json(data);
1239
+ } catch (error) {
1240
+ consola.error("Zen chat completion error:", error);
1241
+ return c.json({ error: {
1242
+ message: error instanceof Error ? error.message : "Unknown error",
1243
+ type: "zen_error"
1244
+ } }, 500);
1245
+ }
1246
+ });
1247
+
1248
+ //#endregion
1249
+ //#region src/services/zen/create-messages.ts
1250
+ /**
1251
+ * Create messages via OpenCode Zen (Anthropic format)
1252
+ */
1253
+ async function createZenMessages(request, signal) {
1254
+ const apiKey = state.zenApiKey;
1255
+ if (!apiKey) throw new Error("Zen API key not configured");
1256
+ consola.debug(`Zen messages request for model: ${request.model}`);
1257
+ const response = await fetch("https://opencode.ai/zen/v1/messages", {
1258
+ method: "POST",
1259
+ headers: {
1260
+ "Content-Type": "application/json",
1261
+ "x-api-key": apiKey,
1262
+ "anthropic-version": "2023-06-01"
1263
+ },
1264
+ body: JSON.stringify(request),
1265
+ signal
1266
+ });
1267
+ if (!response.ok) {
1268
+ const errorText = await response.text();
1269
+ consola.error(`Zen Messages API error: ${response.status} ${errorText}`);
1270
+ throw new Error(`Zen Messages API error: ${response.status} ${errorText}`);
1271
+ }
1272
+ return response;
1273
+ }
1274
+
1275
+ //#endregion
1276
+ //#region src/routes/zen/messages/route.ts
1277
+ const zenMessageRoutes = new Hono();
1278
+ zenMessageRoutes.post("/", async (c) => {
1279
+ if (!state.zenMode || !state.zenApiKey) return c.json({ error: "Zen mode is not enabled. Start with --zen flag." }, 400);
1280
+ try {
1281
+ const body = await c.req.json();
1282
+ consola.debug("Zen message request:", body.model);
1283
+ const response = await createZenMessages(body);
1284
+ if (body.stream) {
1285
+ const headers = new Headers();
1286
+ headers.set("Content-Type", "text/event-stream");
1287
+ headers.set("Cache-Control", "no-cache");
1288
+ headers.set("Connection", "keep-alive");
1289
+ return new Response(response.body, {
1290
+ status: response.status,
1291
+ headers
1292
+ });
1293
+ }
1294
+ const data = await response.json();
1295
+ return c.json(data);
1296
+ } catch (error) {
1297
+ consola.error("Zen message error:", error);
1298
+ return c.json({
1299
+ type: "error",
1300
+ error: {
1301
+ type: "zen_error",
1302
+ message: error instanceof Error ? error.message : "Unknown error"
1303
+ }
1304
+ }, 500);
1305
+ }
1306
+ });
1307
+
1308
+ //#endregion
1309
+ //#region src/routes/zen/models/route.ts
1310
+ const zenModelRoutes = new Hono();
1311
+ zenModelRoutes.get("/", async (c) => {
1312
+ if (!state.zenMode || !state.zenApiKey) return c.json({ error: "Zen mode is not enabled. Start with --zen flag." }, 400);
1313
+ try {
1314
+ if (state.zenModels) return c.json(state.zenModels);
1315
+ const { getZenModels } = await import("./get-models-Hlxa1hWY.js");
1316
+ const models = await getZenModels();
1317
+ state.zenModels = models;
1318
+ return c.json(models);
1319
+ } catch (error) {
1320
+ consola.error("Zen models error:", error);
1321
+ return c.json({ error: {
1322
+ message: error instanceof Error ? error.message : "Unknown error",
1323
+ type: "zen_error"
1324
+ } }, 500);
1325
+ }
1326
+ });
1327
+
1135
1328
  //#endregion
1136
1329
  //#region src/server.ts
1137
1330
  const server = new Hono();
@@ -1139,15 +1332,33 @@ server.use(logger());
1139
1332
  server.use(cors());
1140
1333
  server.use(apiKeyAuthMiddleware);
1141
1334
  server.get("/", (c) => c.text("Server running"));
1142
- server.route("/chat/completions", completionRoutes);
1143
- server.route("/models", modelRoutes);
1335
+ server.route("/chat/completions", new Hono().all("/*", async (c, next) => {
1336
+ if (state.zenMode) return zenCompletionRoutes.fetch(c.req.raw, c.env);
1337
+ return completionRoutes.fetch(c.req.raw, c.env);
1338
+ }));
1339
+ server.route("/models", new Hono().all("/*", async (c, next) => {
1340
+ if (state.zenMode) return zenModelRoutes.fetch(c.req.raw, c.env);
1341
+ return modelRoutes.fetch(c.req.raw, c.env);
1342
+ }));
1144
1343
  server.route("/embeddings", embeddingRoutes);
1145
1344
  server.route("/usage", usageRoute);
1146
1345
  server.route("/token", tokenRoute);
1147
- server.route("/v1/chat/completions", completionRoutes);
1148
- server.route("/v1/models", modelRoutes);
1346
+ server.route("/v1/chat/completions", new Hono().all("/*", async (c, next) => {
1347
+ if (state.zenMode) return zenCompletionRoutes.fetch(c.req.raw, c.env);
1348
+ return completionRoutes.fetch(c.req.raw, c.env);
1349
+ }));
1350
+ server.route("/v1/models", new Hono().all("/*", async (c, next) => {
1351
+ if (state.zenMode) return zenModelRoutes.fetch(c.req.raw, c.env);
1352
+ return modelRoutes.fetch(c.req.raw, c.env);
1353
+ }));
1149
1354
  server.route("/v1/embeddings", embeddingRoutes);
1150
- server.route("/v1/messages", messageRoutes);
1355
+ server.route("/v1/messages", new Hono().all("/*", async (c, next) => {
1356
+ if (state.zenMode) return zenMessageRoutes.fetch(c.req.raw, c.env);
1357
+ return messageRoutes.fetch(c.req.raw, c.env);
1358
+ }));
1359
+ server.route("/zen/v1/chat/completions", zenCompletionRoutes);
1360
+ server.route("/zen/v1/models", zenModelRoutes);
1361
+ server.route("/zen/v1/messages", zenMessageRoutes);
1151
1362
 
1152
1363
  //#endregion
1153
1364
  //#region src/start.ts
@@ -1171,6 +1382,8 @@ server.route("/v1/messages", messageRoutes);
1171
1382
  * - showToken: Expose GitHub/Copilot tokens in responses for debugging
1172
1383
  * - proxyEnv: Initialize proxy settings from environment variables
1173
1384
  * - apiKeys: Optional list of API keys to enable API key authentication
1385
+ * - zen: Enable OpenCode Zen mode (proxy to Zen instead of GitHub Copilot)
1386
+ * - zenApiKey: OpenCode Zen API key (optional; if omitted will prompt for setup)
1174
1387
  */
1175
1388
  async function runServer(options) {
1176
1389
  if (options.proxyEnv) initProxyFromEnv();
@@ -1187,43 +1400,63 @@ async function runServer(options) {
1187
1400
  state.apiKeys = options.apiKeys;
1188
1401
  if (state.apiKeys && state.apiKeys.length > 0) consola.info(`API key authentication enabled with ${state.apiKeys.length} key(s)`);
1189
1402
  await ensurePaths();
1190
- await cacheVSCodeVersion();
1191
- if (options.githubToken) {
1192
- state.githubToken = options.githubToken;
1193
- consola.info("Using provided GitHub token");
1403
+ if (options.zen) {
1404
+ consola.info("OpenCode Zen mode enabled");
1405
+ state.zenMode = true;
1406
+ if (options.zenApiKey) {
1407
+ state.zenApiKey = options.zenApiKey;
1408
+ consola.info("Using provided Zen API key");
1409
+ } else {
1410
+ const { setupZenApiKey, loadZenAuth } = await import("./auth-KlL1W4gV.js");
1411
+ const existingAuth = await loadZenAuth();
1412
+ if (existingAuth) {
1413
+ state.zenApiKey = existingAuth.apiKey;
1414
+ consola.info("Using existing Zen API key");
1415
+ } else state.zenApiKey = await setupZenApiKey();
1416
+ }
1417
+ const { cacheZenModels } = await import("./get-models-Hlxa1hWY.js");
1418
+ await cacheZenModels();
1419
+ consola.info(`Available Zen models: \n${state.zenModels?.data.map((model) => `- ${model.id}`).join("\n")}`);
1420
+ } else {
1421
+ await cacheVSCodeVersion();
1422
+ if (options.githubToken) {
1423
+ state.githubToken = options.githubToken;
1424
+ consola.info("Using provided GitHub token");
1425
+ try {
1426
+ const { getGitHubUser } = await import("./get-user-M3sQS0U8.js");
1427
+ const user = await getGitHubUser();
1428
+ consola.info(`Logged in as ${user.login}`);
1429
+ } catch (error) {
1430
+ consola.error("Provided GitHub token is invalid");
1431
+ throw error;
1432
+ }
1433
+ } else await setupGitHubToken();
1194
1434
  try {
1195
- const { getGitHubUser } = await import("./get-user-BQgLIPYd.js");
1196
- const user = await getGitHubUser();
1197
- consola.info(`Logged in as ${user.login}`);
1435
+ await setupCopilotToken();
1198
1436
  } catch (error) {
1199
- consola.error("Provided GitHub token is invalid");
1437
+ const { HTTPError: HTTPError$1 } = await import("./error-CsShqJjE.js");
1438
+ if (error instanceof HTTPError$1 && error.response.status === 401) {
1439
+ consola.error("Failed to get Copilot token - GitHub token may be invalid or Copilot access revoked");
1440
+ const { clearGithubToken: clearGithubToken$1 } = await import("./token-BssxOyqn.js");
1441
+ await clearGithubToken$1();
1442
+ consola.info("Please restart to re-authenticate");
1443
+ }
1200
1444
  throw error;
1201
1445
  }
1202
- } else await setupGitHubToken();
1203
- try {
1204
- await setupCopilotToken();
1205
- } catch (error) {
1206
- const { HTTPError: HTTPError$1 } = await import("./error-Ba4dbGYj.js");
1207
- if (error instanceof HTTPError$1 && error.response.status === 401) {
1208
- consola.error("Failed to get Copilot token - GitHub token may be invalid or Copilot access revoked");
1209
- const { clearGithubToken: clearGithubToken$1 } = await import("./token-DP4tvNfm.js");
1210
- await clearGithubToken$1();
1211
- consola.info("Please restart to re-authenticate");
1212
- }
1213
- throw error;
1446
+ await cacheModels();
1447
+ consola.info(`Available models: \n${state.models?.data.map((model) => `- ${model.id}`).join("\n")}`);
1214
1448
  }
1215
- await cacheModels();
1216
- consola.info(`Available models: \n${state.models?.data.map((model) => `- ${model.id}`).join("\n")}`);
1217
1449
  const serverUrl = `http://localhost:${options.port}`;
1218
1450
  if (options.claudeCode) {
1219
- invariant(state.models, "Models should be loaded by now");
1451
+ const models = state.zenMode ? state.zenModels : state.models;
1452
+ invariant(models, "Models should be loaded by now");
1220
1453
  const selectedModel = await consola.prompt("Select a model to use with Claude Code", {
1221
1454
  type: "select",
1222
- options: state.models.data.map((model) => model.id)
1455
+ options: models.data.map((model) => model.id)
1223
1456
  });
1224
1457
  const selectedSmallModel = await consola.prompt("Select a small model to use with Claude Code", {
1225
1458
  type: "select",
1226
- options: state.models.data.map((model) => model.id)
1459
+ options: models.data.map((model) => model.id)
1227
1460
  });
1228
1461
  const command = generateEnvScript({
1229
1462
  ANTHROPIC_BASE_URL: serverUrl,
@@ -1313,6 +1546,16 @@ const start = defineCommand({
1313
1546
  "api-key": {
1314
1547
  type: "string",
1315
1548
  description: "API keys for authentication"
1549
+ },
1550
+ zen: {
1551
+ alias: "z",
1552
+ type: "boolean",
1553
+ default: false,
1554
+ description: "Enable OpenCode Zen mode (proxy to Zen instead of GitHub Copilot)"
1555
+ },
1556
+ "zen-api-key": {
1557
+ type: "string",
1558
+ description: "OpenCode Zen API key (get from https://opencode.ai/zen)"
1316
1559
  }
1317
1560
  },
1318
1561
  run({ args }) {
@@ -1332,7 +1575,9 @@ const start = defineCommand({
1332
1575
  claudeCode: args["claude-code"],
1333
1576
  showToken: args["show-token"],
1334
1577
  proxyEnv: args["proxy-env"],
1335
- apiKeys
1578
+ apiKeys,
1579
+ zen: args.zen,
1580
+ zenApiKey: args["zen-api-key"]
1336
1581
  });
1337
1582
  }
1338
1583
  });