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/README.en.md +6 -9
- package/README.md +5 -8
- package/dist/{account-manager-BLD3jHgL.js → account-manager-B9daQhPM.js} +13 -4
- package/dist/{account-manager-BLD3jHgL.js.map → account-manager-B9daQhPM.js.map} +1 -1
- package/dist/error-oHPe3O3W.js +2 -0
- package/dist/get-user-q-uqgjND.js +2 -0
- package/dist/main.js +62 -60
- package/dist/main.js.map +1 -1
- package/dist/{token-V-OSvQfl.js → token-9T4XDHX4.js} +2 -2
- package/dist/{token-V-OSvQfl.js.map → token-9T4XDHX4.js.map} +1 -1
- package/dist/token-byWuxeZE.js +3 -0
- package/package.json +1 -1
- package/dist/error-BDOdv5Up.js +0 -2
- package/dist/get-user-BSYESgez.js +0 -2
- package/dist/token-C9gxxgzi.js +0 -3
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-
|
|
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-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
1676
|
-
*
|
|
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
|
-
|
|
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:
|
|
1714
|
+
timeoutMs: FETCH_TIMEOUT_MS,
|
|
1685
1715
|
accountId,
|
|
1686
1716
|
accountProxy
|
|
1687
1717
|
});
|
|
1688
1718
|
} catch (error) {
|
|
1689
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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((
|
|
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((
|
|
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-
|
|
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-
|
|
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-
|
|
3144
|
+
const { clearGithubToken } = await import("./token-byWuxeZE.js");
|
|
3143
3145
|
await clearGithubToken();
|
|
3144
3146
|
consola.info("Please restart to re-authenticate");
|
|
3145
3147
|
}
|