copilot-api-plus 1.2.19 → 1.2.20

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,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { C as GITHUB_BASE_URL, D as standardHeaders, E as copilotHeaders, T as copilotBaseUrl, _ as findModel, a as getAccountDispatcher, b as sleep, c as notifyStreamStart, d as PATHS, f as ensurePaths, g as cacheVSCodeVersion, h as cacheModels, l as resetAccountConnections, m as forwardError, o as initProxyFromEnv, p as HTTPError, r as getCopilotUsage, s as notifyStreamEnd, t as accountManager, u as resetConnections, v as isNullish, w as GITHUB_CLIENT_ID, x as state, y as rootCause } from "./account-manager-BLD3jHgL.js";
3
- import { a as stopCopilotTokenRefresh, i as setupGitHubToken, n as refreshCopilotToken, o as pollAccessToken, r as setupCopilotToken, s as getDeviceCode, t as clearGithubToken } from "./token-V-OSvQfl.js";
2
+ import { C as GITHUB_BASE_URL, D as standardHeaders, E as copilotHeaders, T as copilotBaseUrl, _ as findModel, a as getAccountDispatcher, b as sleep, c as notifyStreamStart, d as PATHS, f as ensurePaths, g as cacheVSCodeVersion, h as cacheModels, l as resetAccountConnections, m as forwardError, o as initProxyFromEnv, p as HTTPError, r as getCopilotUsage, s as notifyStreamEnd, t as accountManager, u as resetConnections, v as isNullish, w as GITHUB_CLIENT_ID, x as state, y as rootCause } from "./account-manager-B9daQhPM.js";
3
+ import { a as stopCopilotTokenRefresh, i as setupGitHubToken, n as refreshCopilotToken, o as pollAccessToken, r as setupCopilotToken, s as getDeviceCode, t as clearGithubToken } from "./token-9T4XDHX4.js";
4
4
  import { createRequire } from "node:module";
5
5
  import { defineCommand, runMain } from "citty";
6
6
  import consola from "consola";
@@ -1351,6 +1351,16 @@ accountRoutes.post("/", async (c) => {
1351
1351
  if (!body.githubToken || !body.label) return c.json({ error: "githubToken and label are required" }, 400);
1352
1352
  const account = await accountManager.addAccount(body.githubToken, body.label, body.accountType);
1353
1353
  if (body.proxy) {
1354
+ try {
1355
+ const proxyUrl = new URL(body.proxy);
1356
+ if (![
1357
+ "http:",
1358
+ "https:",
1359
+ "socks5:"
1360
+ ].includes(proxyUrl.protocol)) return c.json({ error: "proxy must use http://, https://, or socks5:// protocol" }, 400);
1361
+ } catch {
1362
+ return c.json({ error: "proxy must be a valid URL" }, 400);
1363
+ }
1354
1364
  account.proxy = body.proxy;
1355
1365
  await accountManager.saveAccounts();
1356
1366
  }
@@ -1393,6 +1403,33 @@ accountRoutes.put("/:id/status", async (c) => {
1393
1403
  return c.json({ error: "Failed to update account status" }, 500);
1394
1404
  }
1395
1405
  });
1406
+ accountRoutes.put("/:id/proxy", async (c) => {
1407
+ try {
1408
+ const id = c.req.param("id");
1409
+ const body = await c.req.json();
1410
+ const account = accountManager.getAccountById(id);
1411
+ if (!account) return c.json({ error: "Account not found" }, 404);
1412
+ if (body.proxy) {
1413
+ try {
1414
+ const proxyUrl = new URL(body.proxy);
1415
+ if (![
1416
+ "http:",
1417
+ "https:",
1418
+ "socks5:"
1419
+ ].includes(proxyUrl.protocol)) return c.json({ error: "proxy must use http://, https://, or socks5:// protocol" }, 400);
1420
+ } catch {
1421
+ return c.json({ error: "proxy must be a valid URL" }, 400);
1422
+ }
1423
+ account.proxy = body.proxy;
1424
+ } else account.proxy = void 0;
1425
+ await accountManager.saveAccounts();
1426
+ return c.json({ account: sanitiseAccount(account) });
1427
+ } catch (error) {
1428
+ consola.warn(`Error updating account proxy: ${rootCause(error)}`);
1429
+ consola.debug("Error updating account proxy:", error);
1430
+ return c.json({ error: "Failed to update account proxy" }, 500);
1431
+ }
1432
+ });
1396
1433
  accountRoutes.post("/:id/refresh", async (c) => {
1397
1434
  try {
1398
1435
  const id = c.req.param("id");
@@ -1630,15 +1667,6 @@ async function checkRateLimit(state) {
1630
1667
  * ~120s to start streaming, so we give a generous timeout for headers.
1631
1668
  */
1632
1669
  const FETCH_TIMEOUT_MS = 12e4;
1633
- /**
1634
- * Retry delays in ms. Empty = no retries.
1635
- *
1636
- * IMPORTANT: Retries are DISABLED because each attempt to Copilot consumes
1637
- * a credit, and the caller (e.g. Claude Code) already retries at the
1638
- * application level. Our retry + Claude Code's retry created a request
1639
- * cascade that caused account bans (367 requests in 52 minutes).
1640
- */
1641
- const RETRY_DELAYS = [];
1642
1670
  /** Minimum interval (ms) between requests on the same account. */
1643
1671
  const MIN_SAME_ACCOUNT_INTERVAL_MS = 1e3;
1644
1672
  /** Random jitter range (ms) added when switching between accounts. */
@@ -1670,37 +1698,28 @@ async function fetchWithTimeout(url, init, { timeoutMs = FETCH_TIMEOUT_MS, accou
1670
1698
  }
1671
1699
  }
1672
1700
  /**
1673
- * Retry loop for fetch: retries on network errors with exponential back-off.
1701
+ * Single-attempt fetch with connection pool reset on network errors.
1702
+ *
1703
+ * Retries are intentionally disabled — each Copilot request consumes a
1704
+ * credit, and the caller (e.g. Claude Code) already retries at the
1705
+ * application level. Our retry + caller retry created a request cascade
1706
+ * that caused account bans (367 requests in 52 minutes).
1674
1707
  *
1675
- * Returns `{ response }` on success.
1676
- * Throws the last network error if all retries are exhausted.
1708
+ * On network failure (NOT timeout), the pooled connections are destroyed
1709
+ * so that the caller's next attempt gets a fresh socket instantly.
1677
1710
  */
1678
1711
  async function fetchWithRetry(url, buildInit, { accountId, accountProxy } = {}) {
1679
- let lastError;
1680
- const maxAttempts = RETRY_DELAYS.length + 1;
1681
- for (let attempt = 0; attempt < maxAttempts; attempt++) try {
1682
- const timeout = FETCH_TIMEOUT_MS;
1712
+ try {
1683
1713
  return await fetchWithTimeout(url, buildInit(), {
1684
- timeoutMs: timeout,
1714
+ timeoutMs: FETCH_TIMEOUT_MS,
1685
1715
  accountId,
1686
1716
  accountProxy
1687
1717
  });
1688
1718
  } catch (error) {
1689
- lastError = error;
1690
- const msg = error instanceof Error ? error.message : String(error);
1691
- if (msg.includes("timed out")) {
1692
- consola.warn(`Request timed out on attempt ${attempt + 1}/${maxAttempts} — not retrying (credit likely consumed):`, msg);
1693
- break;
1694
- }
1695
- if (attempt === 0) if (accountId) resetAccountConnections(accountId);
1719
+ if (!(error instanceof Error ? error.message : String(error)).includes("timed out")) if (accountId) resetAccountConnections(accountId);
1696
1720
  else resetConnections();
1697
- if (attempt < maxAttempts - 1) {
1698
- const delay = RETRY_DELAYS[attempt];
1699
- consola.warn(`Network error on attempt ${attempt + 1}/${maxAttempts}, retrying in ${delay}ms:`, error instanceof Error ? error.message : error);
1700
- await new Promise((r) => setTimeout(r, delay));
1701
- }
1721
+ throw error;
1702
1722
  }
1703
- throw lastError instanceof Error ? lastError : /* @__PURE__ */ new Error("Network request failed");
1704
1723
  }
1705
1724
  /**
1706
1725
  * Wraps an AsyncGenerator so that `releaseSlot` is called when the generator
@@ -1819,14 +1838,14 @@ const createChatCompletions = async (payload) => {
1819
1838
  if (wasInjected && errMsg.includes("Unrecognized request argument")) {
1820
1839
  reasoningUnsupportedModels.add(resolvedModel);
1821
1840
  consola.info(`Model "${resolvedModel}" does not support reasoning_effort — disabled for future requests`);
1822
- return retryWithoutReasoning(routedPayload, releaseSlot);
1841
+ return retryWithModifiedPayload(routedPayload, releaseSlot);
1823
1842
  }
1824
1843
  if (errMsg.includes("is not supported by model")) {
1825
1844
  const currentEffort = thinkingPayload.reasoning_effort;
1826
1845
  if (currentEffort && currentEffort !== "medium" && currentEffort !== "low") {
1827
1846
  reasoningEffortCap.set(resolvedModel, "medium");
1828
1847
  consola.info(`Model "${resolvedModel}" rejected reasoning_effort="${currentEffort}" — downgrading to "medium" for future requests`);
1829
- return retryWithDowngradedReasoning({
1848
+ return retryWithModifiedPayload({
1830
1849
  ...routedPayload,
1831
1850
  reasoning_effort: "medium"
1832
1851
  }, releaseSlot);
@@ -1838,28 +1857,11 @@ const createChatCompletions = async (payload) => {
1838
1857
  }
1839
1858
  };
1840
1859
  /**
1841
- * Retry a request without reasoning_effort after the model rejected it.
1860
+ * Retry a request after modifying the payload (e.g. stripping or
1861
+ * downgrading reasoning_effort).
1842
1862
  * Handles slot release for both streaming and non-streaming responses.
1843
1863
  */
1844
- async function retryWithoutReasoning(payload, releaseSlot) {
1845
- try {
1846
- const result = await dispatchRequest(payload);
1847
- if (Symbol.asyncIterator in result) {
1848
- const accountInfo = result.__accountInfo;
1849
- return wrapGeneratorWithRelease(result, releaseSlot, accountInfo);
1850
- }
1851
- releaseSlot();
1852
- return result;
1853
- } catch (retryError) {
1854
- releaseSlot();
1855
- throw retryError;
1856
- }
1857
- }
1858
- /**
1859
- * Retry a request with a downgraded reasoning_effort after the model
1860
- * rejected the higher value (e.g. "high" → "medium").
1861
- */
1862
- async function retryWithDowngradedReasoning(payload, releaseSlot) {
1864
+ async function retryWithModifiedPayload(payload, releaseSlot) {
1863
1865
  try {
1864
1866
  const result = await dispatchRequest(payload);
1865
1867
  if (Symbol.asyncIterator in result) {
@@ -1881,7 +1883,7 @@ function dispatchRequest(payload) {
1881
1883
  }
1882
1884
  async function createWithSingleAccount(payload) {
1883
1885
  if (!state.copilotToken) throw new Error("Copilot token not found");
1884
- const enableVision = payload.messages.some((x) => typeof x.content !== "string" && x.content?.some((x) => x.type === "image_url"));
1886
+ const enableVision = payload.messages.some((msg) => typeof msg.content !== "string" && msg.content?.some((part) => part.type === "image_url"));
1885
1887
  const isAgentCall = payload.messages.some((msg) => ["assistant", "tool"].includes(msg.role));
1886
1888
  const buildHeaders = () => ({
1887
1889
  ...copilotHeaders(state, enableVision),
@@ -1937,7 +1939,7 @@ async function tryRefreshAndRetry(account, payload, tokenSource) {
1937
1939
  try {
1938
1940
  await accountManager.refreshAccountToken(account);
1939
1941
  tokenSource.copilotToken = account.copilotToken;
1940
- const result = await doFetch(payload, tokenSource);
1942
+ const result = await doFetch(payload, tokenSource, account.id);
1941
1943
  accountManager.markAccountSuccess(account.id);
1942
1944
  return result;
1943
1945
  } catch {
@@ -2040,7 +2042,7 @@ async function createWithMultiAccount(payload) {
2040
2042
  * construction / retry / error‐surfacing logic in one place.
2041
2043
  */
2042
2044
  async function doFetch(payload, source, accountId) {
2043
- const enableVision = payload.messages.some((x) => typeof x.content !== "string" && x.content?.some((x) => x.type === "image_url"));
2045
+ const enableVision = payload.messages.some((msg) => typeof msg.content !== "string" && msg.content?.some((part) => part.type === "image_url"));
2044
2046
  const isAgentCall = payload.messages.some((msg) => ["assistant", "tool"].includes(msg.role));
2045
2047
  const buildHeaders = () => ({
2046
2048
  ...copilotHeaders(source, enableVision),
@@ -3085,7 +3087,7 @@ async function validateGitHubToken(token) {
3085
3087
  state.githubToken = token;
3086
3088
  consola.info("Using provided GitHub token");
3087
3089
  try {
3088
- const { getGitHubUser } = await import("./get-user-BSYESgez.js");
3090
+ const { getGitHubUser } = await import("./get-user-q-uqgjND.js");
3089
3091
  const user = await getGitHubUser();
3090
3092
  consola.info(`Logged in as ${user.login}`);
3091
3093
  } catch (error) {
@@ -3136,10 +3138,10 @@ async function runServer(options) {
3136
3138
  try {
3137
3139
  await setupCopilotToken();
3138
3140
  } catch (error) {
3139
- const { HTTPError } = await import("./error-BDOdv5Up.js");
3141
+ const { HTTPError } = await import("./error-oHPe3O3W.js");
3140
3142
  if (error instanceof HTTPError && error.response.status === 401) {
3141
3143
  consola.error("Failed to get Copilot token - GitHub token may be invalid or Copilot access revoked");
3142
- const { clearGithubToken } = await import("./token-C9gxxgzi.js");
3144
+ const { clearGithubToken } = await import("./token-byWuxeZE.js");
3143
3145
  await clearGithubToken();
3144
3146
  consola.info("Please restart to re-authenticate");
3145
3147
  }