copilot-api-plus 1.0.6 → 1.0.8

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
 
@@ -1137,6 +1189,142 @@ usageRoute.get("/", async (c) => {
1137
1189
  }
1138
1190
  });
1139
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
+
1140
1328
  //#endregion
1141
1329
  //#region src/server.ts
1142
1330
  const server = new Hono();
@@ -1144,15 +1332,33 @@ server.use(logger());
1144
1332
  server.use(cors());
1145
1333
  server.use(apiKeyAuthMiddleware);
1146
1334
  server.get("/", (c) => c.text("Server running"));
1147
- server.route("/chat/completions", completionRoutes);
1148
- 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
+ }));
1149
1343
  server.route("/embeddings", embeddingRoutes);
1150
1344
  server.route("/usage", usageRoute);
1151
1345
  server.route("/token", tokenRoute);
1152
- server.route("/v1/chat/completions", completionRoutes);
1153
- 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
+ }));
1154
1354
  server.route("/v1/embeddings", embeddingRoutes);
1155
- 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);
1156
1362
 
1157
1363
  //#endregion
1158
1364
  //#region src/start.ts
@@ -1176,6 +1382,8 @@ server.route("/v1/messages", messageRoutes);
1176
1382
  * - showToken: Expose GitHub/Copilot tokens in responses for debugging
1177
1383
  * - proxyEnv: Initialize proxy settings from environment variables
1178
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)
1179
1387
  */
1180
1388
  async function runServer(options) {
1181
1389
  if (options.proxyEnv) initProxyFromEnv();
@@ -1192,43 +1400,63 @@ async function runServer(options) {
1192
1400
  state.apiKeys = options.apiKeys;
1193
1401
  if (state.apiKeys && state.apiKeys.length > 0) consola.info(`API key authentication enabled with ${state.apiKeys.length} key(s)`);
1194
1402
  await ensurePaths();
1195
- await cacheVSCodeVersion();
1196
- if (options.githubToken) {
1197
- state.githubToken = options.githubToken;
1198
- 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();
1199
1434
  try {
1200
- const { getGitHubUser } = await import("./get-user-BQgLIPYd.js");
1201
- const user = await getGitHubUser();
1202
- consola.info(`Logged in as ${user.login}`);
1435
+ await setupCopilotToken();
1203
1436
  } catch (error) {
1204
- 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
+ }
1205
1444
  throw error;
1206
1445
  }
1207
- } else await setupGitHubToken();
1208
- try {
1209
- await setupCopilotToken();
1210
- } catch (error) {
1211
- const { HTTPError: HTTPError$1 } = await import("./error-Ba4dbGYj.js");
1212
- if (error instanceof HTTPError$1 && error.response.status === 401) {
1213
- consola.error("Failed to get Copilot token - GitHub token may be invalid or Copilot access revoked");
1214
- const { clearGithubToken: clearGithubToken$1 } = await import("./token-DP4tvNfm.js");
1215
- await clearGithubToken$1();
1216
- consola.info("Please restart to re-authenticate");
1217
- }
1218
- throw error;
1446
+ await cacheModels();
1447
+ consola.info(`Available models: \n${state.models?.data.map((model) => `- ${model.id}`).join("\n")}`);
1219
1448
  }
1220
- await cacheModels();
1221
- consola.info(`Available models: \n${state.models?.data.map((model) => `- ${model.id}`).join("\n")}`);
1222
1449
  const serverUrl = `http://localhost:${options.port}`;
1223
1450
  if (options.claudeCode) {
1224
- 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");
1225
1453
  const selectedModel = await consola.prompt("Select a model to use with Claude Code", {
1226
1454
  type: "select",
1227
- options: state.models.data.map((model) => model.id)
1455
+ options: models.data.map((model) => model.id)
1228
1456
  });
1229
1457
  const selectedSmallModel = await consola.prompt("Select a small model to use with Claude Code", {
1230
1458
  type: "select",
1231
- options: state.models.data.map((model) => model.id)
1459
+ options: models.data.map((model) => model.id)
1232
1460
  });
1233
1461
  const command = generateEnvScript({
1234
1462
  ANTHROPIC_BASE_URL: serverUrl,
@@ -1318,6 +1546,16 @@ const start = defineCommand({
1318
1546
  "api-key": {
1319
1547
  type: "string",
1320
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)"
1321
1559
  }
1322
1560
  },
1323
1561
  run({ args }) {
@@ -1337,7 +1575,9 @@ const start = defineCommand({
1337
1575
  claudeCode: args["claude-code"],
1338
1576
  showToken: args["show-token"],
1339
1577
  proxyEnv: args["proxy-env"],
1340
- apiKeys
1578
+ apiKeys,
1579
+ zen: args.zen,
1580
+ zenApiKey: args["zen-api-key"]
1341
1581
  });
1342
1582
  }
1343
1583
  });