billion-context 0.1.30 → 0.1.32
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.md +19 -30
- package/README.zh-CN.md +15 -25
- package/dist/index.js +1421 -1052
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1141,14 +1141,14 @@ var require_util = __commonJS({
|
|
|
1141
1141
|
}
|
|
1142
1142
|
const port = url.port != null ? url.port : url.protocol === "https:" ? 443 : 80;
|
|
1143
1143
|
let origin = url.origin != null ? url.origin : `${url.protocol || ""}//${url.hostname || ""}:${port}`;
|
|
1144
|
-
let
|
|
1144
|
+
let path8 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
|
|
1145
1145
|
if (origin[origin.length - 1] === "/") {
|
|
1146
1146
|
origin = origin.slice(0, origin.length - 1);
|
|
1147
1147
|
}
|
|
1148
|
-
if (
|
|
1149
|
-
|
|
1148
|
+
if (path8 && path8[0] !== "/") {
|
|
1149
|
+
path8 = `/${path8}`;
|
|
1150
1150
|
}
|
|
1151
|
-
return new URL(`${origin}${
|
|
1151
|
+
return new URL(`${origin}${path8}`);
|
|
1152
1152
|
}
|
|
1153
1153
|
if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {
|
|
1154
1154
|
throw new InvalidArgumentError("Invalid URL protocol: the URL must start with `http:` or `https:`.");
|
|
@@ -1969,9 +1969,9 @@ var require_diagnostics = __commonJS({
|
|
|
1969
1969
|
"undici:client:sendHeaders",
|
|
1970
1970
|
(evt) => {
|
|
1971
1971
|
const {
|
|
1972
|
-
request: { method, path:
|
|
1972
|
+
request: { method, path: path8, origin }
|
|
1973
1973
|
} = evt;
|
|
1974
|
-
debugLog("sending request to %s %s%s", method, origin,
|
|
1974
|
+
debugLog("sending request to %s %s%s", method, origin, path8);
|
|
1975
1975
|
}
|
|
1976
1976
|
);
|
|
1977
1977
|
}
|
|
@@ -1989,14 +1989,14 @@ var require_diagnostics = __commonJS({
|
|
|
1989
1989
|
"undici:request:headers",
|
|
1990
1990
|
(evt) => {
|
|
1991
1991
|
const {
|
|
1992
|
-
request: { method, path:
|
|
1992
|
+
request: { method, path: path8, origin },
|
|
1993
1993
|
response: { statusCode }
|
|
1994
1994
|
} = evt;
|
|
1995
1995
|
debugLog(
|
|
1996
1996
|
"received response to %s %s%s - HTTP %d",
|
|
1997
1997
|
method,
|
|
1998
1998
|
origin,
|
|
1999
|
-
|
|
1999
|
+
path8,
|
|
2000
2000
|
statusCode
|
|
2001
2001
|
);
|
|
2002
2002
|
}
|
|
@@ -2005,23 +2005,23 @@ var require_diagnostics = __commonJS({
|
|
|
2005
2005
|
"undici:request:trailers",
|
|
2006
2006
|
(evt) => {
|
|
2007
2007
|
const {
|
|
2008
|
-
request: { method, path:
|
|
2008
|
+
request: { method, path: path8, origin }
|
|
2009
2009
|
} = evt;
|
|
2010
|
-
debugLog("trailers received from %s %s%s", method, origin,
|
|
2010
|
+
debugLog("trailers received from %s %s%s", method, origin, path8);
|
|
2011
2011
|
}
|
|
2012
2012
|
);
|
|
2013
2013
|
diagnosticsChannel.subscribe(
|
|
2014
2014
|
"undici:request:error",
|
|
2015
2015
|
(evt) => {
|
|
2016
2016
|
const {
|
|
2017
|
-
request: { method, path:
|
|
2017
|
+
request: { method, path: path8, origin },
|
|
2018
2018
|
error
|
|
2019
2019
|
} = evt;
|
|
2020
2020
|
debugLog(
|
|
2021
2021
|
"request to %s %s%s errored - %s",
|
|
2022
2022
|
method,
|
|
2023
2023
|
origin,
|
|
2024
|
-
|
|
2024
|
+
path8,
|
|
2025
2025
|
error.message
|
|
2026
2026
|
);
|
|
2027
2027
|
}
|
|
@@ -2136,7 +2136,7 @@ var require_request = __commonJS({
|
|
|
2136
2136
|
var kHandler = /* @__PURE__ */ Symbol("handler");
|
|
2137
2137
|
var Request = class {
|
|
2138
2138
|
constructor(origin, {
|
|
2139
|
-
path:
|
|
2139
|
+
path: path8,
|
|
2140
2140
|
method,
|
|
2141
2141
|
body,
|
|
2142
2142
|
headers,
|
|
@@ -2153,11 +2153,11 @@ var require_request = __commonJS({
|
|
|
2153
2153
|
maxRedirections,
|
|
2154
2154
|
typeOfService
|
|
2155
2155
|
}, handler) {
|
|
2156
|
-
if (typeof
|
|
2156
|
+
if (typeof path8 !== "string") {
|
|
2157
2157
|
throw new InvalidArgumentError("path must be a string");
|
|
2158
|
-
} else if (
|
|
2158
|
+
} else if (path8[0] !== "/" && !(path8.startsWith("http://") || path8.startsWith("https://")) && method !== "CONNECT") {
|
|
2159
2159
|
throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
|
|
2160
|
-
} else if (invalidPathRegex.test(
|
|
2160
|
+
} else if (invalidPathRegex.test(path8)) {
|
|
2161
2161
|
throw new InvalidArgumentError("invalid request path");
|
|
2162
2162
|
}
|
|
2163
2163
|
if (typeof method !== "string") {
|
|
@@ -2232,7 +2232,7 @@ var require_request = __commonJS({
|
|
|
2232
2232
|
this.completed = false;
|
|
2233
2233
|
this.aborted = false;
|
|
2234
2234
|
this.upgrade = upgrade || null;
|
|
2235
|
-
this.path = query ? serializePathWithQuery(
|
|
2235
|
+
this.path = query ? serializePathWithQuery(path8, query) : path8;
|
|
2236
2236
|
this.origin = origin;
|
|
2237
2237
|
this.protocol = getProtocolFromUrlString(origin);
|
|
2238
2238
|
this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent;
|
|
@@ -7415,7 +7415,7 @@ var require_client_h1 = __commonJS({
|
|
|
7415
7415
|
return method !== "GET" && method !== "HEAD" && method !== "OPTIONS" && method !== "TRACE" && method !== "CONNECT";
|
|
7416
7416
|
}
|
|
7417
7417
|
function writeH1(client, request) {
|
|
7418
|
-
const { method, path:
|
|
7418
|
+
const { method, path: path8, host, upgrade, blocking, reset } = request;
|
|
7419
7419
|
let { body, headers, contentLength } = request;
|
|
7420
7420
|
const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH" || method === "QUERY" || method === "PROPFIND" || method === "PROPPATCH";
|
|
7421
7421
|
if (util.isFormDataLike(body)) {
|
|
@@ -7493,7 +7493,7 @@ var require_client_h1 = __commonJS({
|
|
|
7493
7493
|
if (socket.setTypeOfService) {
|
|
7494
7494
|
socket.setTypeOfService(request.typeOfService);
|
|
7495
7495
|
}
|
|
7496
|
-
let header = `${method} ${
|
|
7496
|
+
let header = `${method} ${path8} HTTP/1.1\r
|
|
7497
7497
|
`;
|
|
7498
7498
|
if (typeof host === "string") {
|
|
7499
7499
|
header += `host: ${host}\r
|
|
@@ -8146,7 +8146,7 @@ var require_client_h2 = __commonJS({
|
|
|
8146
8146
|
function writeH2(client, request) {
|
|
8147
8147
|
const requestTimeout = request.bodyTimeout ?? client[kBodyTimeout];
|
|
8148
8148
|
const session = client[kHTTP2Session];
|
|
8149
|
-
const { method, path:
|
|
8149
|
+
const { method, path: path8, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request;
|
|
8150
8150
|
let { body } = request;
|
|
8151
8151
|
if (upgrade != null && upgrade !== "websocket") {
|
|
8152
8152
|
util.errorRequest(client, request, new InvalidArgumentError(`Custom upgrade "${upgrade}" not supported over HTTP/2`));
|
|
@@ -8214,7 +8214,7 @@ var require_client_h2 = __commonJS({
|
|
|
8214
8214
|
}
|
|
8215
8215
|
headers[HTTP2_HEADER_METHOD] = "CONNECT";
|
|
8216
8216
|
headers[HTTP2_HEADER_PROTOCOL] = "websocket";
|
|
8217
|
-
headers[HTTP2_HEADER_PATH] =
|
|
8217
|
+
headers[HTTP2_HEADER_PATH] = path8;
|
|
8218
8218
|
if (protocol === "ws:" || protocol === "wss:") {
|
|
8219
8219
|
headers[HTTP2_HEADER_SCHEME] = protocol === "ws:" ? "http" : "https";
|
|
8220
8220
|
} else {
|
|
@@ -8255,7 +8255,7 @@ var require_client_h2 = __commonJS({
|
|
|
8255
8255
|
stream2.setTimeout(requestTimeout);
|
|
8256
8256
|
return true;
|
|
8257
8257
|
}
|
|
8258
|
-
headers[HTTP2_HEADER_PATH] =
|
|
8258
|
+
headers[HTTP2_HEADER_PATH] = path8;
|
|
8259
8259
|
headers[HTTP2_HEADER_SCHEME] = protocol === "http:" ? "http" : "https";
|
|
8260
8260
|
const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
|
|
8261
8261
|
if (body && typeof body.read === "function") {
|
|
@@ -10598,10 +10598,10 @@ var require_proxy_agent = __commonJS({
|
|
|
10598
10598
|
};
|
|
10599
10599
|
const {
|
|
10600
10600
|
origin,
|
|
10601
|
-
path:
|
|
10601
|
+
path: path8 = "/",
|
|
10602
10602
|
headers = {}
|
|
10603
10603
|
} = opts;
|
|
10604
|
-
opts.path = origin +
|
|
10604
|
+
opts.path = origin + path8;
|
|
10605
10605
|
if (!("host" in headers) && !("Host" in headers)) {
|
|
10606
10606
|
const { host } = new URL(origin);
|
|
10607
10607
|
headers.host = host;
|
|
@@ -12684,20 +12684,20 @@ var require_mock_utils = __commonJS({
|
|
|
12684
12684
|
}
|
|
12685
12685
|
return normalizedQp;
|
|
12686
12686
|
}
|
|
12687
|
-
function safeUrl(
|
|
12688
|
-
if (typeof
|
|
12689
|
-
return
|
|
12687
|
+
function safeUrl(path8) {
|
|
12688
|
+
if (typeof path8 !== "string") {
|
|
12689
|
+
return path8;
|
|
12690
12690
|
}
|
|
12691
|
-
const pathSegments =
|
|
12691
|
+
const pathSegments = path8.split("?", 3);
|
|
12692
12692
|
if (pathSegments.length !== 2) {
|
|
12693
|
-
return
|
|
12693
|
+
return path8;
|
|
12694
12694
|
}
|
|
12695
12695
|
const qp = new URLSearchParams(pathSegments.pop());
|
|
12696
12696
|
qp.sort();
|
|
12697
12697
|
return [...pathSegments, qp.toString()].join("?");
|
|
12698
12698
|
}
|
|
12699
|
-
function matchKey(mockDispatch2, { path:
|
|
12700
|
-
const pathMatch = matchValue(mockDispatch2.path,
|
|
12699
|
+
function matchKey(mockDispatch2, { path: path8, method, body, headers }) {
|
|
12700
|
+
const pathMatch = matchValue(mockDispatch2.path, path8);
|
|
12701
12701
|
const methodMatch = matchValue(mockDispatch2.method, method);
|
|
12702
12702
|
const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
|
|
12703
12703
|
const headersMatch = matchHeaders(mockDispatch2, headers);
|
|
@@ -12722,8 +12722,8 @@ var require_mock_utils = __commonJS({
|
|
|
12722
12722
|
const basePath = key.query ? serializePathWithQuery(key.path, key.query) : key.path;
|
|
12723
12723
|
const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
|
|
12724
12724
|
const resolvedPathWithoutTrailingSlash = removeTrailingSlash(resolvedPath);
|
|
12725
|
-
let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path:
|
|
12726
|
-
return ignoreTrailingSlash ? matchValue(removeTrailingSlash(safeUrl(
|
|
12725
|
+
let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path8, ignoreTrailingSlash }) => {
|
|
12726
|
+
return ignoreTrailingSlash ? matchValue(removeTrailingSlash(safeUrl(path8)), resolvedPathWithoutTrailingSlash) : matchValue(safeUrl(path8), resolvedPath);
|
|
12727
12727
|
});
|
|
12728
12728
|
if (matchedMockDispatches.length === 0) {
|
|
12729
12729
|
throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
|
|
@@ -12762,19 +12762,19 @@ var require_mock_utils = __commonJS({
|
|
|
12762
12762
|
mockDispatches.splice(index, 1);
|
|
12763
12763
|
}
|
|
12764
12764
|
}
|
|
12765
|
-
function removeTrailingSlash(
|
|
12766
|
-
while (
|
|
12767
|
-
|
|
12765
|
+
function removeTrailingSlash(path8) {
|
|
12766
|
+
while (path8.endsWith("/")) {
|
|
12767
|
+
path8 = path8.slice(0, -1);
|
|
12768
12768
|
}
|
|
12769
|
-
if (
|
|
12770
|
-
|
|
12769
|
+
if (path8.length === 0) {
|
|
12770
|
+
path8 = "/";
|
|
12771
12771
|
}
|
|
12772
|
-
return
|
|
12772
|
+
return path8;
|
|
12773
12773
|
}
|
|
12774
12774
|
function buildKey(opts) {
|
|
12775
|
-
const { path:
|
|
12775
|
+
const { path: path8, method, body, headers, query } = opts;
|
|
12776
12776
|
return {
|
|
12777
|
-
path:
|
|
12777
|
+
path: path8,
|
|
12778
12778
|
method,
|
|
12779
12779
|
body,
|
|
12780
12780
|
headers,
|
|
@@ -13464,10 +13464,10 @@ var require_pending_interceptors_formatter = __commonJS({
|
|
|
13464
13464
|
}
|
|
13465
13465
|
format(pendingInterceptors) {
|
|
13466
13466
|
const withPrettyHeaders = pendingInterceptors.map(
|
|
13467
|
-
({ method, path:
|
|
13467
|
+
({ method, path: path8, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
|
|
13468
13468
|
Method: method,
|
|
13469
13469
|
Origin: origin,
|
|
13470
|
-
Path:
|
|
13470
|
+
Path: path8,
|
|
13471
13471
|
"Status code": statusCode,
|
|
13472
13472
|
Persistent: persist ? PERSISTENT : NOT_PERSISTENT,
|
|
13473
13473
|
Invocations: timesInvoked,
|
|
@@ -13549,9 +13549,9 @@ var require_mock_agent = __commonJS({
|
|
|
13549
13549
|
const acceptNonStandardSearchParameters = this[kMockAgentAcceptsNonStandardSearchParameters];
|
|
13550
13550
|
const dispatchOpts = { ...opts };
|
|
13551
13551
|
if (acceptNonStandardSearchParameters && dispatchOpts.path) {
|
|
13552
|
-
const [
|
|
13552
|
+
const [path8, searchParams] = dispatchOpts.path.split("?");
|
|
13553
13553
|
const normalizedSearchParams = normalizeSearchParams(searchParams, acceptNonStandardSearchParameters);
|
|
13554
|
-
dispatchOpts.path = `${
|
|
13554
|
+
dispatchOpts.path = `${path8}?${normalizedSearchParams}`;
|
|
13555
13555
|
}
|
|
13556
13556
|
return this[kAgent].dispatch(dispatchOpts, handler);
|
|
13557
13557
|
}
|
|
@@ -13952,12 +13952,12 @@ var require_snapshot_recorder = __commonJS({
|
|
|
13952
13952
|
* @return {Promise<void>} - Resolves when snapshots are loaded
|
|
13953
13953
|
*/
|
|
13954
13954
|
async loadSnapshots(filePath) {
|
|
13955
|
-
const
|
|
13956
|
-
if (!
|
|
13955
|
+
const path8 = filePath || this.#snapshotPath;
|
|
13956
|
+
if (!path8) {
|
|
13957
13957
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
13958
13958
|
}
|
|
13959
13959
|
try {
|
|
13960
|
-
const data = await readFile3(resolve(
|
|
13960
|
+
const data = await readFile3(resolve(path8), "utf8");
|
|
13961
13961
|
const parsed = JSON.parse(data);
|
|
13962
13962
|
if (Array.isArray(parsed)) {
|
|
13963
13963
|
this.#snapshots.clear();
|
|
@@ -13971,7 +13971,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
13971
13971
|
if (error.code === "ENOENT") {
|
|
13972
13972
|
this.#snapshots.clear();
|
|
13973
13973
|
} else {
|
|
13974
|
-
throw new UndiciError(`Failed to load snapshots from ${
|
|
13974
|
+
throw new UndiciError(`Failed to load snapshots from ${path8}`, { cause: error });
|
|
13975
13975
|
}
|
|
13976
13976
|
}
|
|
13977
13977
|
}
|
|
@@ -13982,11 +13982,11 @@ var require_snapshot_recorder = __commonJS({
|
|
|
13982
13982
|
* @returns {Promise<void>} - Resolves when snapshots are saved
|
|
13983
13983
|
*/
|
|
13984
13984
|
async saveSnapshots(filePath) {
|
|
13985
|
-
const
|
|
13986
|
-
if (!
|
|
13985
|
+
const path8 = filePath || this.#snapshotPath;
|
|
13986
|
+
if (!path8) {
|
|
13987
13987
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
13988
13988
|
}
|
|
13989
|
-
const resolvedPath = resolve(
|
|
13989
|
+
const resolvedPath = resolve(path8);
|
|
13990
13990
|
await mkdir3(dirname6(resolvedPath), { recursive: true });
|
|
13991
13991
|
const data = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
|
|
13992
13992
|
hash,
|
|
@@ -14618,15 +14618,15 @@ var require_redirect_handler = __commonJS({
|
|
|
14618
14618
|
return;
|
|
14619
14619
|
}
|
|
14620
14620
|
const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)));
|
|
14621
|
-
const
|
|
14622
|
-
const redirectUrlString = `${origin}${
|
|
14621
|
+
const path8 = search ? `${pathname}${search}` : pathname;
|
|
14622
|
+
const redirectUrlString = `${origin}${path8}`;
|
|
14623
14623
|
for (const historyUrl of this.history) {
|
|
14624
14624
|
if (historyUrl.toString() === redirectUrlString) {
|
|
14625
14625
|
throw new InvalidArgumentError(`Redirect loop detected. Cannot redirect to ${origin}. This typically happens when using a Client or Pool with cross-origin redirects. Use an Agent for cross-origin redirects.`);
|
|
14626
14626
|
}
|
|
14627
14627
|
}
|
|
14628
14628
|
this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin);
|
|
14629
|
-
this.opts.path =
|
|
14629
|
+
this.opts.path = path8;
|
|
14630
14630
|
this.opts.origin = origin;
|
|
14631
14631
|
this.opts.query = null;
|
|
14632
14632
|
}
|
|
@@ -16395,10 +16395,10 @@ var require_cache_handler = __commonJS({
|
|
|
16395
16395
|
}
|
|
16396
16396
|
return locationUrl.pathname + locationUrl.search;
|
|
16397
16397
|
}
|
|
16398
|
-
function deleteCachedUri(store, cacheKey,
|
|
16398
|
+
function deleteCachedUri(store, cacheKey, path8) {
|
|
16399
16399
|
deleteCachedValue(store, {
|
|
16400
16400
|
...cacheKey,
|
|
16401
|
-
path:
|
|
16401
|
+
path: path8
|
|
16402
16402
|
});
|
|
16403
16403
|
for (let i = 0; i < util.safeHTTPMethods.length; i++) {
|
|
16404
16404
|
const method = util.safeHTTPMethods[i];
|
|
@@ -16406,7 +16406,7 @@ var require_cache_handler = __commonJS({
|
|
|
16406
16406
|
deleteCachedValue(store, {
|
|
16407
16407
|
...cacheKey,
|
|
16408
16408
|
method,
|
|
16409
|
-
path:
|
|
16409
|
+
path: path8
|
|
16410
16410
|
});
|
|
16411
16411
|
}
|
|
16412
16412
|
}
|
|
@@ -16417,9 +16417,9 @@ var require_cache_handler = __commonJS({
|
|
|
16417
16417
|
}
|
|
16418
16418
|
const values = Array.isArray(headerValue2) ? headerValue2 : [headerValue2];
|
|
16419
16419
|
for (let i = 0; i < values.length; i++) {
|
|
16420
|
-
const
|
|
16421
|
-
if (
|
|
16422
|
-
deleteCachedUri(store, cacheKey,
|
|
16420
|
+
const path8 = getSameOriginPath(cacheKey, values[i]);
|
|
16421
|
+
if (path8 !== void 0) {
|
|
16422
|
+
deleteCachedUri(store, cacheKey, path8);
|
|
16423
16423
|
}
|
|
16424
16424
|
}
|
|
16425
16425
|
}
|
|
@@ -21297,11 +21297,11 @@ var require_fetch = __commonJS({
|
|
|
21297
21297
|
function dispatch({ body }) {
|
|
21298
21298
|
const url = requestCurrentURL(request);
|
|
21299
21299
|
const agent = fetchParams.controller.dispatcher;
|
|
21300
|
-
const
|
|
21300
|
+
const path8 = url.pathname + url.search;
|
|
21301
21301
|
const hasTrailingQuestionMark = url.search.length === 0 && url.href[url.href.length - url.hash.length - 1] === "?";
|
|
21302
21302
|
return new Promise((resolve, reject) => agent.dispatch(
|
|
21303
21303
|
{
|
|
21304
|
-
path: hasTrailingQuestionMark ? `${
|
|
21304
|
+
path: hasTrailingQuestionMark ? `${path8}?` : path8,
|
|
21305
21305
|
origin: url.origin,
|
|
21306
21306
|
method: request.method,
|
|
21307
21307
|
body: agent.isMockActive ? request.body && (request.body.source || request.body.stream) : body,
|
|
@@ -22248,9 +22248,9 @@ var require_util4 = __commonJS({
|
|
|
22248
22248
|
}
|
|
22249
22249
|
}
|
|
22250
22250
|
}
|
|
22251
|
-
function validateCookiePath(
|
|
22252
|
-
for (let i = 0; i <
|
|
22253
|
-
const code =
|
|
22251
|
+
function validateCookiePath(path8) {
|
|
22252
|
+
for (let i = 0; i < path8.length; ++i) {
|
|
22253
|
+
const code = path8.charCodeAt(i);
|
|
22254
22254
|
if (code < 32 || // exclude CTLs (0-31)
|
|
22255
22255
|
code > 126 || // exclude DEL and non-ascii
|
|
22256
22256
|
code === 59) {
|
|
@@ -25487,11 +25487,11 @@ var require_undici = __commonJS({
|
|
|
25487
25487
|
if (typeof opts.path !== "string") {
|
|
25488
25488
|
throw new InvalidArgumentError("invalid opts.path");
|
|
25489
25489
|
}
|
|
25490
|
-
let
|
|
25490
|
+
let path8 = opts.path;
|
|
25491
25491
|
if (!opts.path.startsWith("/")) {
|
|
25492
|
-
|
|
25492
|
+
path8 = `/${path8}`;
|
|
25493
25493
|
}
|
|
25494
|
-
url = new URL(util.parseOrigin(url).origin +
|
|
25494
|
+
url = new URL(util.parseOrigin(url).origin + path8);
|
|
25495
25495
|
} else {
|
|
25496
25496
|
if (!opts) {
|
|
25497
25497
|
opts = typeof url === "object" ? url : {};
|
|
@@ -30021,9 +30021,9 @@ var require_sha256 = __commonJS({
|
|
|
30021
30021
|
var forge2 = require_forge();
|
|
30022
30022
|
require_md();
|
|
30023
30023
|
require_util7();
|
|
30024
|
-
var
|
|
30025
|
-
forge2.md.sha256 = forge2.md.algorithms.sha256 =
|
|
30026
|
-
|
|
30024
|
+
var sha256 = module.exports = forge2.sha256 = forge2.sha256 || {};
|
|
30025
|
+
forge2.md.sha256 = forge2.md.algorithms.sha256 = sha256;
|
|
30026
|
+
sha256.create = function() {
|
|
30027
30027
|
if (!_initialized) {
|
|
30028
30028
|
_init();
|
|
30029
30029
|
}
|
|
@@ -46205,13 +46205,13 @@ function getUpstreamConnectionStatus() {
|
|
|
46205
46205
|
}
|
|
46206
46206
|
|
|
46207
46207
|
// src/config.ts
|
|
46208
|
-
function safeReadJson(
|
|
46208
|
+
function safeReadJson(path8) {
|
|
46209
46209
|
try {
|
|
46210
|
-
const raw = readFileSync(
|
|
46210
|
+
const raw = readFileSync(path8, "utf8").replace(/^\uFEFF/, "");
|
|
46211
46211
|
return JSON.parse(raw);
|
|
46212
46212
|
} catch (e) {
|
|
46213
46213
|
if (e.code !== "ENOENT") {
|
|
46214
|
-
log("error", `[acp-config] failed to parse ${
|
|
46214
|
+
log("error", `[acp-config] failed to parse ${path8}: ${String(e)}`);
|
|
46215
46215
|
}
|
|
46216
46216
|
return void 0;
|
|
46217
46217
|
}
|
|
@@ -46420,7 +46420,6 @@ function rejectLegacyRoute(key, value) {
|
|
|
46420
46420
|
// src/server.ts
|
|
46421
46421
|
import http from "http";
|
|
46422
46422
|
import fs3 from "fs";
|
|
46423
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
46424
46423
|
|
|
46425
46424
|
// src/registry.ts
|
|
46426
46425
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
@@ -47698,9 +47697,15 @@ async function flushAllSessions() {
|
|
|
47698
47697
|
var COMPRESS_TOOL_NAME = "compress";
|
|
47699
47698
|
var ACP_TEXT_OPEN = "<acp_compress>";
|
|
47700
47699
|
var ACP_TEXT_CLOSE = "</acp_compress>";
|
|
47700
|
+
var ACP_STATUS_OPEN = "<acp_status>";
|
|
47701
|
+
var ACP_STATUS_CLOSE = "</acp_status>";
|
|
47702
|
+
var ACP_SEARCH_OPEN = "<acp_search>";
|
|
47703
|
+
var ACP_SEARCH_CLOSE = "</acp_search>";
|
|
47704
|
+
var ACP_DECOMPRESS_OPEN = "<acp_decompress>";
|
|
47705
|
+
var ACP_DECOMPRESS_CLOSE = "</acp_decompress>";
|
|
47701
47706
|
var COMPRESS_TOOL = {
|
|
47702
47707
|
name: COMPRESS_TOOL_NAME,
|
|
47703
|
-
description: "Replace a contiguous range of older conversation with a detailed summary you write. Use when content is genuinely consumed. Batch form: content=[{startId,endId,summary,topic?}].",
|
|
47708
|
+
description: "Replace a contiguous range of older conversation with a detailed summary you write. Use when content is genuinely consumed. Batch form: content=[{startId,endId,summary,topic?}]. REQUIRED \u2014 compress without content is invalid.",
|
|
47704
47709
|
input_schema: {
|
|
47705
47710
|
type: "object",
|
|
47706
47711
|
properties: {
|
|
@@ -47719,23 +47724,23 @@ var COMPRESS_TOOL = {
|
|
|
47719
47724
|
required: ["startId", "endId", "summary"]
|
|
47720
47725
|
}
|
|
47721
47726
|
}
|
|
47722
|
-
}
|
|
47727
|
+
},
|
|
47728
|
+
required: ["content"]
|
|
47723
47729
|
}
|
|
47724
47730
|
};
|
|
47725
|
-
function parseCompressInput(input) {
|
|
47731
|
+
function parseCompressInput(input, callId) {
|
|
47726
47732
|
if (!input || typeof input !== "object") {
|
|
47727
47733
|
log("warn", `[acp-compress-input] rejected: not object (${typeof input})`);
|
|
47728
47734
|
return [];
|
|
47729
47735
|
}
|
|
47730
47736
|
const obj = input;
|
|
47731
|
-
if (Array.isArray(obj.content)) {
|
|
47732
|
-
const out = obj.content.map((r) => toRange(r)).filter((r) => r !== null);
|
|
47733
|
-
if (out.length === 0) log("warn", `[acp-compress-input] content array but 0 valid ranges. keys per item: ${obj.content.map((c) => Object.keys(c ?? {}).join(",")).join(" | ")}`);
|
|
47734
|
-
return out;
|
|
47735
|
-
}
|
|
47736
47737
|
const single = toRange(obj);
|
|
47737
|
-
|
|
47738
|
-
|
|
47738
|
+
const ranges = Array.isArray(obj.content) ? obj.content.map((r) => toRange(r)).filter((r) => r !== null) : single ? [single] : [];
|
|
47739
|
+
if (ranges.length === 0) {
|
|
47740
|
+
log("warn", `[acp-compress-input] parsed 0 valid ranges. top keys: ${Object.keys(obj).join(",")}`);
|
|
47741
|
+
}
|
|
47742
|
+
if (callId) for (const r of ranges) r.compressCallId = callId;
|
|
47743
|
+
return ranges;
|
|
47739
47744
|
}
|
|
47740
47745
|
function toRange(r) {
|
|
47741
47746
|
const startRef = pick(r, "startId", "startRef");
|
|
@@ -47764,7 +47769,7 @@ var COMPRESS_TOOL_OPENAI = {
|
|
|
47764
47769
|
topic: { type: "string", description: "Optional short title for the compressed range" },
|
|
47765
47770
|
content: {
|
|
47766
47771
|
type: "array",
|
|
47767
|
-
description: "One or more ranges to compress into separate summary blocks",
|
|
47772
|
+
description: "One or more ranges to compress into separate summary blocks. REQUIRED \u2014 compress without content is invalid.",
|
|
47768
47773
|
items: {
|
|
47769
47774
|
type: "object",
|
|
47770
47775
|
properties: {
|
|
@@ -47776,7 +47781,8 @@ var COMPRESS_TOOL_OPENAI = {
|
|
|
47776
47781
|
required: ["startId", "endId", "summary"]
|
|
47777
47782
|
}
|
|
47778
47783
|
}
|
|
47779
|
-
}
|
|
47784
|
+
},
|
|
47785
|
+
required: ["content"]
|
|
47780
47786
|
}
|
|
47781
47787
|
}
|
|
47782
47788
|
};
|
|
@@ -47826,7 +47832,29 @@ Rules for the trigger:
|
|
|
47826
47832
|
- JSON shape matches the compress tool: {"content":[{startId,endId,summary,topic?}]}. Batch multiple ranges in one trigger.
|
|
47827
47833
|
- After emitting the marker, STOP your turn. Do not continue with other text \u2014 the proxy will execute the compression and return the result, then you continue fresh.
|
|
47828
47834
|
- Do NOT wrap the marker in code fences, quotes, or commentary.
|
|
47829
|
-
- NEVER compress on short conversations or when context is small (well below the window limit). Only compress when context is genuinely large
|
|
47835
|
+
- NEVER compress on short conversations or when context is small (well below the window limit). Only compress when context is genuinely large.
|
|
47836
|
+
|
|
47837
|
+
ACP TOOLS (TEXT TRIGGERS)
|
|
47838
|
+
|
|
47839
|
+
Since host tools cannot coexist with a declared tools field, ALL ACP tools use text triggers. Emit the marker; the proxy intercepts and executes it; the marker is stripped from what the user sees.
|
|
47840
|
+
|
|
47841
|
+
1. acp_status \u2014 view context usage, compression state, and compressible ranges:
|
|
47842
|
+
${ACP_STATUS_OPEN}${ACP_STATUS_CLOSE}
|
|
47843
|
+
No payload needed. Use this FIRST when unsure about context state.
|
|
47844
|
+
|
|
47845
|
+
2. search_context \u2014 search compressed block summaries by keyword:
|
|
47846
|
+
${ACP_SEARCH_OPEN}{"query":"auth token refresh"}${ACP_SEARCH_CLOSE}
|
|
47847
|
+
Use when you need details that may have been compressed away.
|
|
47848
|
+
|
|
47849
|
+
3. decompress \u2014 restore compressed content for exact details:
|
|
47850
|
+
${ACP_DECOMPRESS_OPEN}{"blockId":"b5"}${ACP_DECOMPRESS_CLOSE}
|
|
47851
|
+
Optional: {"blockId":"b5","toFile":"/tmp/b5.txt"} to write to file instead.
|
|
47852
|
+
Optional: {"blockId":"b5","full":true} to restore all the way to original messages.
|
|
47853
|
+
|
|
47854
|
+
Rules for ALL triggers:
|
|
47855
|
+
- Output on its own, NO surrounding prose. Just the raw marker.
|
|
47856
|
+
- After emitting, STOP your turn. The proxy executes and returns the result.
|
|
47857
|
+
- Do NOT wrap in code fences, quotes, or commentary.`;
|
|
47830
47858
|
}
|
|
47831
47859
|
var DECOMPRESS_TOOL_NAME = "decompress";
|
|
47832
47860
|
var DECOMPRESS_TOOL_OPENAI = {
|
|
@@ -47936,6 +47964,14 @@ var PROXY_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
47936
47964
|
SEARCH_CONTEXT_TOOL_NAME,
|
|
47937
47965
|
ACP_STATUS_TOOL_NAME
|
|
47938
47966
|
]);
|
|
47967
|
+
var MUTATING_PROXY_TOOLS = /* @__PURE__ */ new Set([
|
|
47968
|
+
COMPRESS_TOOL_NAME,
|
|
47969
|
+
DECOMPRESS_TOOL_NAME
|
|
47970
|
+
]);
|
|
47971
|
+
var READONLY_PROXY_TOOLS = /* @__PURE__ */ new Set([
|
|
47972
|
+
SEARCH_CONTEXT_TOOL_NAME,
|
|
47973
|
+
ACP_STATUS_TOOL_NAME
|
|
47974
|
+
]);
|
|
47939
47975
|
|
|
47940
47976
|
// src/decompress-shared.ts
|
|
47941
47977
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
@@ -48098,564 +48134,66 @@ function rewriteJsonResponse(body, ctx) {
|
|
|
48098
48134
|
}
|
|
48099
48135
|
|
|
48100
48136
|
// src/web/index.ts
|
|
48101
|
-
import { readFileSync as
|
|
48137
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
48102
48138
|
import { dirname as dirname5, join as join3 } from "path";
|
|
48103
48139
|
import { fileURLToPath } from "url";
|
|
48104
48140
|
|
|
48105
48141
|
// src/web/client.ts
|
|
48106
48142
|
var WEB_CLIENT = `
|
|
48107
|
-
const byId=(id)=>document.getElementById(id);
|
|
48143
|
+
const byId=(id)=>document.getElementById(id);
|
|
48108
48144
|
const escapeHtml=(value)=>String(value??"").replace(/[&<>"']/g,(char)=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[char]);
|
|
48109
48145
|
function toast(message,error){const node=byId("toast");node.textContent=message;node.className="toast show"+(error?" err":"");setTimeout(()=>node.className="toast"+(error?" err":""),2600)}
|
|
48110
48146
|
function busy(button,on,label){if(!button)return;if(on){button.dataset.label=button.textContent;button.textContent=label||"\u5904\u7406\u4E2D\u2026";button.disabled=true;return}if(button.dataset.label){button.textContent=button.dataset.label;delete button.dataset.label}button.disabled=false}
|
|
48111
48147
|
async function json(url,options){const response=await fetch(url,options);const data=await response.json().catch(()=>({}));if(!response.ok)throw new Error(data.error||data.detail||("HTTP "+response.status));return data}
|
|
48112
|
-
function showPage(name){document.querySelectorAll(".page").forEach((node)=>node.classList.toggle("active",node.id==="page-"+name));document.querySelectorAll(".nav button").forEach((node)=>node.classList.toggle("active",node.dataset.page===name));if(name==="sessions")loadSessions();if(name==="
|
|
48148
|
+
function showPage(name){document.querySelectorAll(".page").forEach((node)=>node.classList.toggle("active",node.id==="page-"+name));document.querySelectorAll(".nav button").forEach((node)=>node.classList.toggle("active",node.dataset.page===name));if(name==="sessions")loadSessions();if(name==="upstream"){loadUpstream();loadOverrides()}}
|
|
48113
48149
|
document.querySelectorAll(".nav button").forEach((button)=>button.addEventListener("click",()=>showPage(button.dataset.page)));
|
|
48114
48150
|
async function loadConfig(){const data=await json("/__bili/config");byId("providers-json").value=JSON.stringify(data.providers||{},null,2);byId("proxy-url").value=data.upstreamProxy||"";document.querySelectorAll('input[name="proxy-mode"]').forEach((input)=>input.checked=input.value===(data.upstreamProxyMode||"direct"));return data}
|
|
48115
|
-
async function loadRouting(){try{const data=await json("/__bili/codex");routeState=data.state;const badge=byId("route-badge");const used=data.state==="enabled"&&data.lastRequestAt;badge.className="badge "+(data.state==="enabled"?(used?"ok":"warn"):data.state==="conflict"?"err":"");badge.textContent=data.state==="enabled"?(used?"\u4F7F\u7528\u4E2D":"\u7B49\u5F85\u9996\u6B21\u8BF7\u6C42"):data.state==="conflict"?"\u914D\u7F6E\u51B2\u7A81":"\u672A\u542F\u7528";byId("route-provider").textContent=data.providerId||(data.provider&&data.provider.id)||"\u2014";byId("route-real").textContent=data.originalBaseUrl||(data.provider&&data.provider.baseUrl)||"\u2014";byId("route-local").textContent=data.baseUrl||"\u2014";byId("route-detail").textContent=data.detail||(data.state==="enabled"&&!used?"\u82E5\u5F53\u524D Codex \u5C1A\u672A\u63A5\u5165\uFF0C\u53EF\u91CD\u65B0\u6253\u5F00 Codex\u3002":"");const button=byId("route-toggle");button.textContent=data.state==="disabled"?"\u542F\u7528 Codex \u8DEF\u7531":"\u6062\u590D\u539F\u914D\u7F6E";button.className="btn "+(data.state==="disabled"?"primary":"danger");button.disabled=false}catch(error){byId("route-detail").textContent=String(error)}}
|
|
48116
|
-
async function toggleRoute(){const button=byId("route-toggle");const action=routeState==="disabled"?"enable":"disable";busy(button,true,action==="enable"?"\u542F\u7528\u4E2D\u2026":"\u6062\u590D\u4E2D\u2026");try{const data=await json("/__bili/codex/"+action,{method:"POST"});busy(button,false);await loadRouting();toast(data.detail||(action==="enable"?"Codex \u8DEF\u7531\u5DF2\u542F\u7528":"\u539F\u914D\u7F6E\u5DF2\u6062\u590D"))}catch(error){toast(String(error),true)}finally{busy(button,false)}}
|
|
48117
|
-
async function copyRoute(){const button=byId("copy-route");busy(button,true,"\u590D\u5236\u4E2D\u2026");try{await navigator.clipboard.writeText(byId("route-local").textContent);toast("\u8DEF\u7531\u5730\u5740\u5DF2\u590D\u5236")}catch(error){toast(String(error),true)}finally{busy(button,false)}}
|
|
48118
48151
|
async function loadUpstream(){try{const data=await json("/__bili/upstream");const labels={"provider":"Provider \u5355\u72EC\u4EE3\u7406","provider-direct":"Provider \u76F4\u8FDE","bili-env":"BILI_UPSTREAM_PROXY","web-manual":"Web \u624B\u52A8\u4EE3\u7406","config":"\u914D\u7F6E\u6587\u4EF6\u4EE3\u7406","HTTPS_PROXY":"HTTPS_PROXY","HTTP_PROXY":"HTTP_PROXY","ALL_PROXY":"ALL_PROXY","windows-system":"Windows \u7CFB\u7EDF\u4EE3\u7406","windows-bypass":"Windows \u7ED5\u8FC7\u5217\u8868","no-proxy":"NO_PROXY","direct":"\u76F4\u8FDE"};byId("upstream-source").textContent=labels[data.source]||data.source||"\u76F4\u8FDE";byId("upstream-effective").textContent=data.proxy||"direct";byId("upstream-pac").textContent=data.autoConfigUrl||"\u2014";const state=byId("upstream-state");state.className="status "+(data.connected===true?"ok":data.connected===false?"err":"");state.textContent=data.connected===true?"CONNECT \u6B63\u5E38":data.connected===false?(data.error||"\u8FDE\u63A5\u5931\u8D25"):"\u5C1A\u672A\u89C2\u5BDF\u5230\u4E0A\u6E38\u8FDE\u63A5"}catch(error){byId("upstream-state").textContent=String(error)}}
|
|
48119
48152
|
async function saveUpstream(){const button=byId("save-upstream");busy(button,true,"\u4FDD\u5B58\u4E2D\u2026");const mode=document.querySelector('input[name="proxy-mode"]:checked').value;try{await json("/__bili/config",{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({upstreamProxyMode:mode,upstreamProxy:byId("proxy-url").value.trim()||null})});toast("\u4E0A\u6E38\u8BBE\u7F6E\u5DF2\u70ED\u66F4\u65B0");await loadUpstream()}catch(error){toast(String(error),true)}finally{busy(button,false)}}
|
|
48120
48153
|
async function testUpstream(){const button=byId("test-upstream");busy(button,true,"\u6D4B\u8BD5\u4E2D\u2026");try{const data=await json("/__bili/upstream/test",{method:"POST"});toast("\u8FDE\u63A5\u6210\u529F\uFF0CHTTP "+data.status);await loadUpstream()}catch(error){toast(String(error),true);await loadUpstream()}finally{busy(button,false)}}
|
|
48121
|
-
async function loadHistory(){try{const data=await json("/__bili/codex-history");byId("history-summary").textContent="\u5F53\u524D\u5206\u6876\uFF1A"+data.targetProviderId+"\\n\u68C0\u6D4B\u5230\u65E7\u7248 bili \u8DEF\u7531\u4F1A\u8BDD\uFF1A"+data.sessions+"\uFF08"+data.jsonlFiles+" \u4E2A JSONL\uFF0C"+data.stateRows+" \u6761 state DB\uFF09";byId("repair-history").disabled=data.sessions===0&&data.stateRows===0;if(data.stateDbError)byId("history-summary").textContent+="\\n"+data.stateDbError;return data}catch(error){byId("history-summary").textContent=String(error);throw error}}
|
|
48122
|
-
async function previewHistory(){const button=byId("preview-history");busy(button,true,"\u9884\u89C8\u4E2D\u2026");try{await loadHistory();toast("\u5386\u53F2\u8BB0\u5F55\u9884\u89C8\u5DF2\u5237\u65B0")}catch(error){toast(String(error),true)}finally{busy(button,false)}}
|
|
48123
|
-
async function repairHistory(){const button=byId("repair-history");if(!confirm("\u5C06\u5148\u5907\u4EFD\uFF0C\u518D\u628A\u786E\u8BA4\u5C5E\u4E8E\u65E7 bili \u8DEF\u7531\u7684\u4F1A\u8BDD\u8FC1\u79FB\u5230\u5F53\u524D provider\u3002\u7EE7\u7EED\uFF1F"))return;busy(button,true,"\u4FEE\u590D\u4E2D\u2026");let result;try{result=await json("/__bili/codex-history/repair",{method:"POST"});toast("\u5386\u53F2\u8BB0\u5F55\u5DF2\u4FEE\u590D\uFF0C\u5907\u4EFD\uFF1A"+result.backupPath)}catch(error){toast(String(error),true)}finally{busy(button,false)}if(result)await loadHistory().catch(()=>{})}
|
|
48124
48154
|
async function saveProviders(){const button=byId("save-providers");busy(button,true,"\u4FDD\u5B58\u4E2D\u2026");try{const providers=JSON.parse(byId("providers-json").value);await json("/__bili/config",{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({providers})});await json("/__bili/config/reload",{method:"POST"});toast("Provider \u914D\u7F6E\u5DF2\u70ED\u66F4\u65B0")}catch(error){toast(String(error),true)}finally{busy(button,false)}}
|
|
48125
48155
|
async function loadOverrides(){try{const data=await loadConfig();const providers=data.providers||{};const box=byId("upstream-overrides");const entries=Object.entries(providers);if(entries.length===0){box.innerHTML='<p class="status">\u6682\u65E0 Provider\u3002\u5728"\u9AD8\u7EA7\u8BBE\u7F6E"\u9875\u6DFB\u52A0 Provider \u540E\u53EF\u5728\u6B64\u6309 URL \u914D\u7F6E\u4EE3\u7406\u3002</p>';return}box.innerHTML='<table><thead><tr><th>Provider URL</th><th>\u4E0A\u6E38\u4EE3\u7406\uFF08\u7A7A=\u7EE7\u627F\u5168\u5C40\uFF09</th></tr></thead><tbody>'+entries.map(([url,route])=>'<tr><td class="mono">'+escapeHtml(url)+'</td><td><input class="mono override-proxy" data-url="'+escapeHtml(url)+'" value="'+escapeHtml((route&&route.proxy)||"")+'" placeholder="\u7EE7\u627F\u5168\u5C40"></td></tr>').join("")+'</tbody></table>'}catch(error){byId("upstream-overrides").textContent=String(error)}}
|
|
48126
48156
|
async function saveOverrides(){const button=byId("save-overrides");busy(button,true,"\u4FDD\u5B58\u4E2D\u2026");try{const data=await loadConfig();const providers=data.providers||{};document.querySelectorAll(".override-proxy").forEach((input)=>{const url=input.dataset.url;if(!url)return;if(!providers[url])providers[url]={};const val=input.value.trim();if(val)providers[url].proxy=val;else delete providers[url].proxy});await json("/__bili/config",{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({providers})});await json("/__bili/config/reload",{method:"POST"});toast("\u6309 URL \u8986\u76D6\u5DF2\u70ED\u66F4\u65B0");await loadOverrides()}catch(error){toast(String(error),true)}finally{busy(button,false)}}
|
|
48127
48157
|
async function loadSessions(){try{const data=await json("/__bili/stats");const rows=(data.sessions||[]).map((item)=>"<tr><td>"+escapeHtml(item.title||"\u2014")+"</td><td>"+escapeHtml(item.protocol||"\u2014")+"</td><td class=mono>"+escapeHtml(item.label||"\u2014")+"</td><td>"+escapeHtml(item.requests)+"</td><td>"+escapeHtml(item.contextTokens)+"</td><td>"+escapeHtml(new Date(item.lastSeen).toLocaleString())+"</td></tr>").join("");byId("sessions-body").innerHTML=rows||'<tr><td colspan="6">\u6682\u65E0\u4F1A\u8BDD</td></tr>'}catch(error){toast(String(error),true);throw error}}
|
|
48128
48158
|
async function refreshSessions(){const button=byId("refresh-sessions");busy(button,true,"\u5237\u65B0\u4E2D\u2026");try{await loadSessions();toast("\u4F1A\u8BDD\u5217\u8868\u5DF2\u5237\u65B0")}catch{}finally{busy(button,false)}}
|
|
48129
|
-
|
|
48130
|
-
Promise.all([loadConfig(),
|
|
48159
|
+
document.querySelectorAll(".copy-btn").forEach((btn)=>btn.addEventListener("click",async()=>{busy(btn,true,"\u590D\u5236\u4E2D\u2026");try{await navigator.clipboard.writeText(btn.dataset.copy||"");toast("\u5DF2\u590D\u5236")}catch(e){toast(String(e),true)}finally{busy(btn,false)}}));byId("save-upstream").addEventListener("click",saveUpstream);byId("test-upstream").addEventListener("click",testUpstream);byId("save-providers").addEventListener("click",saveProviders);byId("save-overrides").addEventListener("click",saveOverrides);byId("refresh-sessions").addEventListener("click",refreshSessions);
|
|
48160
|
+
Promise.all([loadConfig(),loadUpstream()]).catch((error)=>toast(String(error),true));setInterval(()=>{if(byId("page-sessions").classList.contains("active"))loadSessions().catch(()=>{})},5000);
|
|
48131
48161
|
`;
|
|
48132
48162
|
|
|
48133
48163
|
// src/web/styles.ts
|
|
48134
48164
|
var WEB_STYLES = `
|
|
48135
48165
|
:root{--bg:#0f1115;--panel:#171a21;--panel2:#1e222c;--line:#2b313d;--text:#e7eaf0;--muted:#8f98a8;--blue:#6ea8fe;--green:#55c98b;--yellow:#e9b949;--red:#ef6b73;--radius:10px}
|
|
48136
48166
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;min-height:100vh}.app{display:grid;grid-template-columns:220px 1fr;min-height:100vh}.sidebar{border-right:1px solid var(--line);padding:22px 14px;background:#12151b;position:sticky;top:0;height:100vh}.brand{font-size:18px;font-weight:700;padding:0 10px 22px}.brand span{color:var(--blue)}.version{display:block;color:var(--muted);font-size:11px;font-weight:400;margin-top:2px}.nav{display:grid;gap:4px}.nav button{appearance:none;border:0;background:transparent;color:var(--muted);text-align:left;padding:10px 12px;border-radius:7px;cursor:pointer;font:inherit}.nav button:hover,.nav button.active{background:var(--panel2);color:var(--text)}.main{padding:32px;max-width:1050px;width:100%}.page{display:none}.page.active{display:block}h1{font-size:23px;margin:0 0 4px}h2{font-size:15px;margin:0}.lead{color:var(--muted);margin:0 0 24px}.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:18px;margin-bottom:14px}.card-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px}.badge{border-radius:99px;padding:3px 9px;font-size:12px;background:var(--panel2);color:var(--muted)}.badge.ok{color:var(--green)}.badge.warn{color:var(--yellow)}.badge.err{color:var(--red)}.kv{display:grid;grid-template-columns:135px 1fr;gap:10px 14px;margin:0}.kv dt{color:var(--muted)}.kv dd{margin:0;word-break:break-all}.mono{font-family:"Cascadia Code",Consolas,monospace;font-size:12px}.actions{display:flex;gap:9px;flex-wrap:wrap;margin-top:18px}.btn{border:1px solid var(--line);background:var(--panel2);color:var(--text);border-radius:7px;padding:8px 13px;cursor:pointer;font:inherit}.btn:hover{border-color:#4b5668}.btn.primary{background:var(--blue);border-color:var(--blue);color:#0b1220;font-weight:600}.btn.danger{color:var(--red)}.btn:disabled{opacity:.55;cursor:wait}.field{display:grid;gap:6px;margin:12px 0}.field label{color:var(--muted);font-size:12px}input,textarea,select{width:100%;background:#101319;color:var(--text);border:1px solid var(--line);border-radius:7px;padding:9px 10px;font:inherit}textarea{min-height:250px;font-family:"Cascadia Code",Consolas,monospace;font-size:12px;resize:vertical}.modes{display:flex;gap:18px;margin:12px 0}.modes label{display:flex;gap:7px;align-items:center;color:var(--muted)}.modes input{width:auto}.notice{border-left:3px solid var(--yellow);padding:10px 12px;background:rgba(233,185,73,.08);color:#d8c185;margin-top:14px}.status{color:var(--muted);white-space:pre-wrap}.status.ok{color:var(--green)}.status.err{color:var(--red)}table{width:100%;border-collapse:collapse}th,td{text-align:left;border-bottom:1px solid var(--line);padding:9px 8px;font-size:12px}th{color:var(--muted);font-weight:500}.toast{position:fixed;right:24px;bottom:24px;background:var(--green);color:#07140d;padding:10px 15px;border-radius:8px;opacity:0;transform:translateY(8px);transition:.2s;pointer-events:none}.toast.show{opacity:1;transform:none}.toast.err{background:var(--red);color:#1b0608}
|
|
48137
|
-
.section-title{font-size:17px;margin:28px 0 4px}.hint{color:var(--muted);margin:0 0 14px;font-size:13px}.btn.small{padding:4px 10px;font-size:12px}
|
|
48167
|
+
.section-title{font-size:17px;margin:28px 0 4px}.hint{color:var(--muted);margin:0 0 14px;font-size:13px}.btn.small{padding:4px 10px;font-size:12px}.config-block{background:var(--panel2);border:1px solid var(--line);border-radius:6px;padding:10px 14px;margin:8px 0 12px;overflow-x:auto;font-size:13px}.config-block code{font-family:"Cascadia Code",Consolas,monospace;color:var(--text)}.fork-ribbon{position:fixed;top:0;right:0;background:#1a1a2e;color:#fff;padding:6px 16px;font-size:12px;text-decoration:none;border-radius:0 0 0 8px;z-index:100;opacity:.8;transition:.2s}.fork-ribbon:hover{opacity:1}
|
|
48138
48168
|
@media(max-width:760px){.app{grid-template-columns:1fr}.sidebar{height:auto;position:static;border-right:0;border-bottom:1px solid var(--line)}.nav{display:flex;overflow:auto}.main{padding:20px}.grid{grid-template-columns:1fr}.kv{grid-template-columns:110px 1fr}}
|
|
48139
48169
|
`;
|
|
48140
48170
|
|
|
48141
48171
|
// src/web/page.ts
|
|
48142
48172
|
function renderPage(origin, version2) {
|
|
48143
48173
|
return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>billion-context</title><style>${WEB_STYLES}</style></head><body>
|
|
48144
|
-
<div class="app"><aside class="sidebar"><div class="brand">billion<span>-context</span><
|
|
48174
|
+
<div class="app"><aside class="sidebar"><div class="brand"><div class="brand-title">billion<span>-context</span></div><div class="version">v${version2}</div></div><a class="fork-ribbon" href="https://github.com/ranxianglei/billion-context" target="_blank" rel="noopener">\u2605 Fork me on GitHub</a><nav class="nav"><button class="active" data-page="overview">\u6982\u89C8</button><button data-page="routing">\u63A5\u5165</button><button data-page="upstream">\u4E0A\u6E38</button><button data-page="sessions">\u4F1A\u8BDD</button><button data-page="settings">\u8BBE\u7F6E</button></nav></aside>
|
|
48145
48175
|
<main class="main"><section id="page-overview" class="page active"><h1>\u6982\u89C8</h1><p class="lead">\u672C\u5730\u8DEF\u7531\u4E0E\u4E0A\u6E38\u7F51\u7EDC\u76F8\u4E92\u72EC\u7ACB\uFF0CACP \u538B\u7F29\u8FD0\u884C\u5728\u4E24\u8005\u4E4B\u95F4\u3002</p><div class="grid"><div class="card"><h2>\u670D\u52A1\u5730\u5740</h2><p class="mono">${origin}</p></div><div class="card"><h2>\u5BA2\u6237\u7AEF\u63A5\u5165</h2><p class="status">\u5728"\u63A5\u5165"\u9875\u5C06\u5404\u5BA2\u6237\u7AEF\uFF08Codex / Claude Code / OpenCode / Pi \u7B49\uFF09\u7684\u6D41\u91CF\u8DEF\u7531\u5230 bili\u3002</p></div></div></section>
|
|
48146
|
-
<section id="page-routing" class="page"><h1>\u5BA2\u6237\u7AEF\u63A5\u5165</h1><p class="lead">\u4E24\u7C7B\u63A5\u5165\uFF1AA.
|
|
48147
|
-
<h2 class="section-title">A.
|
|
48148
|
-
<p class="hint">\u5728\u5BA2\u6237\u7AEF\u914D\u7F6E\u91CC\u628A\u539F baseURL \u524D\u52A0\u4E0A <span class="mono">${origin}/bili/</span> \u5373\u53EF\uFF0C\u5176\u4F59\u4E0D\u53D8\u3002
|
|
48176
|
+
<section id="page-routing" class="page"><h1>\u5BA2\u6237\u7AEF\u63A5\u5165</h1><p class="lead">\u4E24\u7C7B\u63A5\u5165\uFF1AA. \u63A8\u8350\u63A5\u5165\u65B9\u5F0F\uFF08<span class="mono">/bili/</span> \u524D\u7F00\uFF0C\u96F6\u914D\u7F6E\uFF09\uFF1BB. \u8BC1\u4E66\u63A5\u5165\u65B9\u5F0F\uFF08MITM\uFF0C\u9700\u4FE1\u4EFB CA \u8BC1\u4E66\uFF09\u3002</p>
|
|
48177
|
+
<h2 class="section-title">A. \u63A8\u8350\u63A5\u5165\u65B9\u5F0F\uFF08/bili/ \u524D\u7F00\uFF09</h2>
|
|
48178
|
+
<p class="hint">\u5728\u5BA2\u6237\u7AEF\u914D\u7F6E\u91CC\u628A\u539F baseURL \u524D\u52A0\u4E0A <span class="mono">${origin}/bili/</span> \u5373\u53EF\uFF0C\u5176\u4F59\u4E0D\u53D8\u3002</p>
|
|
48149
48179
|
<div class="card"><div class="card-head"><h2>OpenCode</h2><button class="btn small copy-btn" data-copy="${origin}/bili/https://open.bigmodel.cn/api/coding/paas/v4">\u590D\u5236</button></div><dl class="kv"><dt>\u914D\u7F6E\u6587\u4EF6</dt><dd class="mono">~/.config/opencode/opencode.json</dd><dt>baseURL</dt><dd class="mono">${origin}/bili/https://open.bigmodel.cn/api/coding/paas/v4</dd></dl></div>
|
|
48150
48180
|
<div class="card"><div class="card-head"><h2>Codex\uFF08API key\uFF09</h2><button class="btn small copy-btn" data-copy="${origin}/bili/https://api.openai.com/v1">\u590D\u5236</button></div><dl class="kv"><dt>\u914D\u7F6E\u6587\u4EF6</dt><dd class="mono">~/.codex/config.toml</dd><dt>base_url</dt><dd class="mono">${origin}/bili/https://api.openai.com/v1</dd></dl></div>
|
|
48151
|
-
<div class="card"><div class="card-head"><h2>
|
|
48181
|
+
<div class="card"><div class="card-head"><h2>Codex\uFF08ChatGPT \u767B\u5F55\uFF09</h2><button class="btn small copy-btn" data-copy="model_provider = "openai" openai_base_url = "${origin}/bili/https://chatgpt.com/backend-api/codex"">\u590D\u5236\u914D\u7F6E</button></div><dl class="kv"><dt>\u914D\u7F6E\u6587\u4EF6</dt><dd class="mono">~/.codex/config.toml</dd><dt>\u914D\u7F6E</dt><dd class="mono">model_provider = "openai"<br>openai_base_url = "${origin}/bili/https://chatgpt.com/backend-api/codex"</dd><dt>\u8BA4\u8BC1</dt><dd><span class="mono">codex login</span>\uFF08OAuth token \u900F\u4F20\uFF0C\u65E0\u9700\u8BC1\u4E66\uFF09</dd></dl></div>
|
|
48152
48182
|
<div class="card"><div class="card-head"><h2>Claude Code\uFF08API key\uFF09</h2><button class="btn small copy-btn" data-copy="${origin}/bili/https://api.anthropic.com">\u590D\u5236</button></div><dl class="kv"><dt>\u73AF\u5883\u53D8\u91CF</dt><dd class="mono">ANTHROPIC_BASE_URL=${origin}/bili/https://api.anthropic.com</dd></dl></div>
|
|
48153
|
-
<div class="card"><div class="card-head"><h2>\
|
|
48154
|
-
<
|
|
48155
|
-
<
|
|
48156
|
-
<
|
|
48157
|
-
<div class="card"><div class="card-head"><h2>
|
|
48158
|
-
<div class="card"><div class="card-head"><h2>ZCode\uFF08\u7F16\u7A0B\u5957\u9910\uFF09</h2><span class="badge">MITM</span></div><dl class="kv"><dt>\u65B9\u5F0F</dt><dd>MITM \u900F\u660E\u4EE3\u7406</dd><dt>\u8BBE\u7F6E</dt><dd>Settings \u2192 Network \u2192 HTTP Proxy = <span class="mono">${origin}</span></dd><dt>CA \u8BC1\u4E66</dt><dd class="mono">~/.local/share/billion-context/ca/root-ca.pem</dd></dl></div>
|
|
48159
|
-
<div class="card"><div class="card-head"><h2>Codex \u5386\u53F2\u8BB0\u5F55</h2></div><div id="history-summary" class="status">\u52A0\u8F7D\u4E2D</div><div class="actions"><button id="preview-history" class="btn">\u9884\u89C8</button><button id="repair-history" class="btn danger">\u4FEE\u590D\u65E7\u7248\u8DEF\u7531\u4F1A\u8BDD</button></div></div></section>
|
|
48183
|
+
<div class="card"><div class="card-head"><h2>Pi</h2><span class="badge">\u4E0D\u63A8\u8350</span><button class="btn small copy-btn" data-copy="${origin}/bili/https://api.anthropic.com">\u590D\u5236</button></div><dl class="kv"><dt>\u914D\u7F6E\u6587\u4EF6</dt><dd class="mono">~/.pi/agent/models.json</dd><dt>baseUrl</dt><dd class="mono">${origin}/bili/https://api.anthropic.com</dd></dl><p class="hint">\u26A0\uFE0F \u4E0D\u63A8\u8350\uFF1APi \u7684 session \u7BA1\u7406\u4E0E bili \u538B\u7F29\u72B6\u6001\u5B58\u5728\u51B2\u7A81\uFF0C\u8BE6\u89C1 README\u3002</p></div>
|
|
48184
|
+
<div class="card"><div class="card-head"><h2>\u5176\u4ED6\uFF08Cursor / Aider / Continue\uFF09</h2><button class="btn small copy-btn" data-copy="${origin}/bili/">\u590D\u5236</button></div><dl class="kv"><dt>\u89C4\u5219</dt><dd>\u4EFB\u610F baseURL \u524D\u52A0 <span class="mono">${origin}/bili/</span></dd><dt>\u793A\u4F8B</dt><dd class="mono">${origin}/bili/https://api.openai.com/v1</dd></dl></div>
|
|
48185
|
+
<h2 class="section-title">B. \u8BC1\u4E66\u63A5\u5165\u65B9\u5F0F\uFF08zcode \u7B49\u7C7B\u578B\uFF09</h2>
|
|
48186
|
+
<p class="hint">\u4E0D\u652F\u6301\u6539 URL \u7684\u5BA2\u6237\u7AEF\uFF08endpoint \u786C\u7F16\u7801\uFF09\u9700 MITM \u900F\u660E\u4EE3\u7406\uFF0C\u8BBE HTTPS_PROXY + \u4FE1\u4EFB CA \u8BC1\u4E66\u3002</p>
|
|
48187
|
+
<div class="card"><div class="card-head"><h2>ZCode\uFF08\u7F16\u7A0B\u5957\u9910\uFF09</h2><span class="badge">MITM</span></div><dl class="kv"><dt>\u65B9\u5F0F</dt><dd>MITM \u900F\u660E\u4EE3\u7406</dd><dt>\u8BBE\u7F6E</dt><dd>Settings \u2192 Network \u2192 HTTP Proxy = <span class="mono">${origin}</span></dd><dt>CA \u8BC1\u4E66</dt><dd class="mono">~/.local/share/billion-context/ca/root-ca.pem</dd></dl></div></section>
|
|
48160
48188
|
<section id="page-upstream" class="page"><h1>\u4E0A\u6E38\u7F51\u7EDC</h1><p class="lead">\u63A7\u5236 bili \u5982\u4F55\u8BBF\u95EE\u771F\u5B9E Provider\uFF0C\u4E0D\u4F1A\u6539\u53D8\u5BA2\u6237\u7AEF\u7684\u672C\u5730\u8DEF\u7531\u5730\u5740\u3002</p><div class="card"><div class="card-head"><h2>\u5168\u5C40\u4EE3\u7406</h2></div><div class="modes"><label><input type="radio" name="proxy-mode" value="direct" checked>\u76F4\u8FDE\uFF08\u9ED8\u8BA4\uFF09</label><label><input type="radio" name="proxy-mode" value="manual">\u624B\u52A8\u4EE3\u7406</label><label><input type="radio" name="proxy-mode" value="auto">\u81EA\u52A8\uFF08\u8DDF\u968F\u7CFB\u7EDF\uFF09</label></div><div class="field"><label>HTTP / HTTPS Proxy</label><input id="proxy-url" class="mono" placeholder="http://127.0.0.1:7897"></div><dl class="kv"><dt>\u5F53\u524D\u6765\u6E90</dt><dd id="upstream-source">\u76F4\u8FDE</dd><dt>\u6709\u6548\u4EE3\u7406</dt><dd id="upstream-effective" class="mono">direct</dd><dt>\u7CFB\u7EDF PAC</dt><dd id="upstream-pac" class="mono">\u2014</dd><dt>\u72B6\u6001</dt><dd id="upstream-state" class="status">\u5C1A\u672A\u6D4B\u8BD5</dd></dl><div class="actions"><button id="save-upstream" class="btn primary">\u4FDD\u5B58\u5E76\u70ED\u66F4\u65B0</button><button id="test-upstream" class="btn">\u6D4B\u8BD5\u8FDE\u63A5</button></div></div><div class="card"><div class="card-head"><h2>\u6309 URL \u8986\u76D6</h2></div><p class="status">\u4E3A\u7279\u5B9A Provider \u5355\u72EC\u8BBE\u7F6E\u4E0A\u6E38\u4EE3\u7406\uFF0C\u8986\u76D6\u5168\u5C40\u8BBE\u7F6E\u3002\u7559\u7A7A = \u7EE7\u627F\u5168\u5C40\u3002</p><div id="upstream-overrides" class="status">\u52A0\u8F7D\u4E2D</div><div class="actions"><button id="save-overrides" class="btn primary">\u4FDD\u5B58\u8986\u76D6\u5E76\u70ED\u66F4\u65B0</button></div></div></section>
|
|
48161
48189
|
<section id="page-sessions" class="page"><div class="card-head"><div><h1>\u4F1A\u8BDD</h1><p class="lead">ACP \u538B\u7F29\u72B6\u6001\u4E0E\u4E0A\u6E38\u7528\u91CF\u3002</p></div><button id="refresh-sessions" class="btn">\u5237\u65B0</button></div><div class="card"><table><thead><tr><th>\u6807\u9898</th><th>\u534F\u8BAE</th><th>\u6807\u8BC6</th><th>\u8BF7\u6C42</th><th>\u4E0A\u4E0B\u6587</th><th>\u6700\u540E\u6D3B\u52A8</th></tr></thead><tbody id="sessions-body"></tbody></table></div></section>
|
|
48162
48190
|
<section id="page-settings" class="page"><h1>\u9AD8\u7EA7\u8BBE\u7F6E</h1><p class="lead">\u76F4\u63A5\u7F16\u8F91 providers JSON\uFF08\u6DFB\u52A0\u65B0 Provider\u3001\u914D\u7F6E\u6A21\u578B\u4E0A\u4E0B\u6587\u7A97\u53E3\u3001\u6309 URL \u4EE3\u7406\u7B49\uFF09\uFF1B\u4FDD\u5B58\u540E\u7ACB\u5373\u70ED\u66F4\u65B0\u3002</p><div class="card"><div class="field"><label>providers JSON</label><textarea id="providers-json">{}</textarea></div><div class="actions"><button id="save-providers" class="btn primary">\u4FDD\u5B58\u5E76\u5E94\u7528</button></div></div></section></main></div><div id="toast" class="toast"></div><script>${WEB_CLIENT}</script></body></html>`;
|
|
48163
48191
|
}
|
|
48164
48192
|
|
|
48165
48193
|
// src/web/api.ts
|
|
48166
|
-
import { closeSync, fsyncSync, mkdirSync as
|
|
48194
|
+
import { closeSync, fsyncSync, mkdirSync as mkdirSync5, openSync, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
48167
48195
|
import { dirname as dirname4 } from "path";
|
|
48168
|
-
import { randomUUID
|
|
48169
|
-
|
|
48170
|
-
// src/codex-history.ts
|
|
48171
|
-
import {
|
|
48172
|
-
copyFileSync,
|
|
48173
|
-
existsSync as existsSync5,
|
|
48174
|
-
mkdirSync as mkdirSync5,
|
|
48175
|
-
readFileSync as readFileSync4,
|
|
48176
|
-
readdirSync,
|
|
48177
|
-
renameSync as renameSync3,
|
|
48178
|
-
rmSync,
|
|
48179
|
-
statSync as statSync2,
|
|
48180
|
-
writeFileSync as writeFileSync4
|
|
48181
|
-
} from "fs";
|
|
48182
|
-
import path6 from "path";
|
|
48183
|
-
import { createHash as createHash3, randomUUID } from "crypto";
|
|
48184
|
-
import { homedir as homedir3 } from "os";
|
|
48185
|
-
|
|
48186
|
-
// src/codex-provider.ts
|
|
48187
|
-
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
48188
|
-
import { homedir as homedir2 } from "os";
|
|
48189
|
-
import path5 from "path";
|
|
48190
|
-
var CODEX_DEFAULT_PROVIDER_ID = "openai";
|
|
48191
|
-
var BUILTIN_PROVIDERS = {
|
|
48192
|
-
openai: {
|
|
48193
|
-
name: "OpenAI",
|
|
48194
|
-
baseUrl: "https://chatgpt.com/backend-api/codex",
|
|
48195
|
-
wireApi: "responses",
|
|
48196
|
-
requiresOpenaiAuth: true,
|
|
48197
|
-
supportsWebsockets: true
|
|
48198
|
-
}
|
|
48199
|
-
};
|
|
48200
|
-
function linesOf(text) {
|
|
48201
|
-
const lines = [];
|
|
48202
|
-
const re2 = /[^\r\n]*(?:\r\n|\r|\n|$)/g;
|
|
48203
|
-
for (; ; ) {
|
|
48204
|
-
const match = re2.exec(text);
|
|
48205
|
-
if (!match || match[0] === "") break;
|
|
48206
|
-
const raw = match[0];
|
|
48207
|
-
const eol = raw.match(/(\r\n|\r|\n)$/)?.[1] ?? "";
|
|
48208
|
-
const content = eol ? raw.slice(0, -eol.length) : raw;
|
|
48209
|
-
lines.push({
|
|
48210
|
-
start: match.index,
|
|
48211
|
-
contentEnd: match.index + content.length,
|
|
48212
|
-
end: match.index + raw.length,
|
|
48213
|
-
content,
|
|
48214
|
-
eol
|
|
48215
|
-
});
|
|
48216
|
-
if (match.index + raw.length >= text.length) break;
|
|
48217
|
-
}
|
|
48218
|
-
return lines;
|
|
48219
|
-
}
|
|
48220
|
-
function preferredEol(text) {
|
|
48221
|
-
return text.match(/\r\n|\r|\n/)?.[0] ?? (process.platform === "win32" ? "\r\n" : "\n");
|
|
48222
|
-
}
|
|
48223
|
-
function parseTomlString(raw) {
|
|
48224
|
-
if (raw.startsWith('"') && raw.endsWith('"')) {
|
|
48225
|
-
try {
|
|
48226
|
-
const parsed = JSON.parse(raw);
|
|
48227
|
-
return typeof parsed === "string" ? parsed : void 0;
|
|
48228
|
-
} catch {
|
|
48229
|
-
return void 0;
|
|
48230
|
-
}
|
|
48231
|
-
}
|
|
48232
|
-
if (raw.startsWith("'") && raw.endsWith("'")) return raw.slice(1, -1);
|
|
48233
|
-
return void 0;
|
|
48234
|
-
}
|
|
48235
|
-
function parseProviderId(raw) {
|
|
48236
|
-
const trimmed = raw.trim();
|
|
48237
|
-
const quoted = parseTomlString(trimmed);
|
|
48238
|
-
if (quoted !== void 0) return quoted;
|
|
48239
|
-
return /^[A-Za-z0-9_-]+$/.test(trimmed) ? trimmed : void 0;
|
|
48240
|
-
}
|
|
48241
|
-
function sections(text) {
|
|
48242
|
-
const found = [];
|
|
48243
|
-
const lines = linesOf(text);
|
|
48244
|
-
for (let index = 0; index < lines.length; index++) {
|
|
48245
|
-
const line = lines[index];
|
|
48246
|
-
const match = line.content.match(/^\s*\[\s*model_providers\s*\.\s*("(?:\\.|[^"\\])*"|'[^']*'|[A-Za-z0-9_-]+)\s*\]\s*(?:#.*)?$/);
|
|
48247
|
-
if (!match) continue;
|
|
48248
|
-
const id = parseProviderId(match[1]);
|
|
48249
|
-
if (!id) continue;
|
|
48250
|
-
let end = text.length;
|
|
48251
|
-
for (let next = index + 1; next < lines.length; next++) {
|
|
48252
|
-
if (/^\s*\[/.test(lines[next].content)) {
|
|
48253
|
-
end = lines[next].start;
|
|
48254
|
-
break;
|
|
48255
|
-
}
|
|
48256
|
-
}
|
|
48257
|
-
found.push({ id, headerStart: line.start, bodyStart: line.end, end });
|
|
48258
|
-
}
|
|
48259
|
-
return found;
|
|
48260
|
-
}
|
|
48261
|
-
function assignmentInRange(text, key, start, end) {
|
|
48262
|
-
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
48263
|
-
const pattern = new RegExp(`^(\\s*${escaped}\\s*=\\s*)("(?:\\\\.|[^"\\\\])*"|'[^']*'|true|false)(\\s*(?:#.*)?)$`);
|
|
48264
|
-
for (const line of linesOf(text)) {
|
|
48265
|
-
if (line.start < start || line.start >= end) continue;
|
|
48266
|
-
const match = line.content.match(pattern);
|
|
48267
|
-
if (!match) continue;
|
|
48268
|
-
const raw = match[2];
|
|
48269
|
-
const value = raw === "true" ? true : raw === "false" ? false : parseTomlString(raw);
|
|
48270
|
-
if (value === void 0) continue;
|
|
48271
|
-
const valueStart = line.start + match[1].length;
|
|
48272
|
-
return {
|
|
48273
|
-
value,
|
|
48274
|
-
encoded: raw,
|
|
48275
|
-
start: valueStart,
|
|
48276
|
-
end: valueStart + raw.length,
|
|
48277
|
-
lineStart: line.start,
|
|
48278
|
-
lineEnd: line.end
|
|
48279
|
-
};
|
|
48280
|
-
}
|
|
48281
|
-
return void 0;
|
|
48282
|
-
}
|
|
48283
|
-
function rootAssignment(text, key) {
|
|
48284
|
-
const firstSection = linesOf(text).find((line) => /^\s*\[/.test(line.content));
|
|
48285
|
-
return assignmentInRange(text, key, 0, firstSection?.start ?? text.length);
|
|
48286
|
-
}
|
|
48287
|
-
function sectionFor(text, providerId) {
|
|
48288
|
-
return sections(text).find((section) => section.id === providerId);
|
|
48289
|
-
}
|
|
48290
|
-
function fieldFor(text, providerId, key) {
|
|
48291
|
-
const section = sectionFor(text, providerId);
|
|
48292
|
-
return section ? assignmentInRange(text, key, section.bodyStart, section.end) : void 0;
|
|
48293
|
-
}
|
|
48294
|
-
function encodeValue(value) {
|
|
48295
|
-
return typeof value === "boolean" ? String(value) : JSON.stringify(value);
|
|
48296
|
-
}
|
|
48297
|
-
function codexConfigFile(env = process.env) {
|
|
48298
|
-
const codexHome2 = env.CODEX_HOME?.trim();
|
|
48299
|
-
return path5.join(codexHome2 ? path5.resolve(codexHome2) : path5.join(homedir2(), ".codex"), "config.toml");
|
|
48300
|
-
}
|
|
48301
|
-
function readCodexConfig(configPath = codexConfigFile()) {
|
|
48302
|
-
return existsSync4(configPath) ? readFileSync3(configPath, "utf8") : "";
|
|
48303
|
-
}
|
|
48304
|
-
function resolveActiveCodexProvider(configPath = codexConfigFile()) {
|
|
48305
|
-
const text = readCodexConfig(configPath);
|
|
48306
|
-
const providerAssignment = rootAssignment(text, "model_provider");
|
|
48307
|
-
if (providerAssignment && typeof providerAssignment.value !== "string") {
|
|
48308
|
-
throw new Error("Codex model_provider is not a string");
|
|
48309
|
-
}
|
|
48310
|
-
const explicit = typeof providerAssignment?.value === "string";
|
|
48311
|
-
const id = explicit ? providerAssignment.value : CODEX_DEFAULT_PROVIDER_ID;
|
|
48312
|
-
const section = sectionFor(text, id);
|
|
48313
|
-
const builtin = BUILTIN_PROVIDERS[id];
|
|
48314
|
-
if (!section && !builtin) {
|
|
48315
|
-
throw new Error(`cannot safely resolve active Codex provider "${id}": [model_providers.${id}] is missing`);
|
|
48316
|
-
}
|
|
48317
|
-
const stringField = (key, fallback) => {
|
|
48318
|
-
const assignment = fieldFor(text, id, key);
|
|
48319
|
-
if (assignment && typeof assignment.value !== "string") {
|
|
48320
|
-
throw new Error(`Codex provider "${id}" has a non-string ${key}`);
|
|
48321
|
-
}
|
|
48322
|
-
return typeof assignment?.value === "string" ? assignment.value : fallback;
|
|
48323
|
-
};
|
|
48324
|
-
const boolField = (key, fallback) => {
|
|
48325
|
-
const assignment = fieldFor(text, id, key);
|
|
48326
|
-
if (assignment && typeof assignment.value !== "boolean") {
|
|
48327
|
-
throw new Error(`Codex provider "${id}" has a non-boolean ${key}`);
|
|
48328
|
-
}
|
|
48329
|
-
return typeof assignment?.value === "boolean" ? assignment.value : fallback;
|
|
48330
|
-
};
|
|
48331
|
-
const baseUrl = stringField("base_url", builtin?.baseUrl)?.replace(/\/+$/, "");
|
|
48332
|
-
const wireApi = stringField("wire_api", builtin?.wireApi);
|
|
48333
|
-
const requiresOpenaiAuth = boolField("requires_openai_auth", builtin?.requiresOpenaiAuth ?? false);
|
|
48334
|
-
const supportsWebsockets = boolField("supports_websockets", builtin?.supportsWebsockets ?? false);
|
|
48335
|
-
if (!baseUrl || !wireApi) {
|
|
48336
|
-
throw new Error(`cannot safely resolve active Codex provider "${id}": base_url/wire_api fields are incomplete`);
|
|
48337
|
-
}
|
|
48338
|
-
let parsed;
|
|
48339
|
-
try {
|
|
48340
|
-
parsed = new URL(baseUrl);
|
|
48341
|
-
} catch {
|
|
48342
|
-
throw new Error(`Codex provider "${id}" has an invalid base_url`);
|
|
48343
|
-
}
|
|
48344
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
48345
|
-
throw new Error(`Codex provider "${id}" base_url must use http:// or https://`);
|
|
48346
|
-
}
|
|
48347
|
-
if (parsed.search || parsed.hash) {
|
|
48348
|
-
throw new Error(`Codex provider "${id}" base_url cannot contain a query string or fragment`);
|
|
48349
|
-
}
|
|
48350
|
-
return {
|
|
48351
|
-
id,
|
|
48352
|
-
explicit,
|
|
48353
|
-
sectionExists: !!section,
|
|
48354
|
-
name: stringField("name", builtin?.name),
|
|
48355
|
-
baseUrl,
|
|
48356
|
-
wireApi,
|
|
48357
|
-
requiresOpenaiAuth,
|
|
48358
|
-
supportsWebsockets,
|
|
48359
|
-
configPath
|
|
48360
|
-
};
|
|
48361
|
-
}
|
|
48362
|
-
function getCodexProviderField(text, providerId, key) {
|
|
48363
|
-
return fieldFor(text, providerId, key)?.value;
|
|
48364
|
-
}
|
|
48365
|
-
function getCodexProviderFieldSnapshot(text, providerId, key) {
|
|
48366
|
-
const field = fieldFor(text, providerId, key);
|
|
48367
|
-
return field ? { value: field.value, encoded: field.encoded } : void 0;
|
|
48368
|
-
}
|
|
48369
|
-
function setCodexProviderField(text, providerId, key, value) {
|
|
48370
|
-
const existing = fieldFor(text, providerId, key);
|
|
48371
|
-
if (existing) {
|
|
48372
|
-
return {
|
|
48373
|
-
text: text.slice(0, existing.start) + encodeValue(value) + text.slice(existing.end),
|
|
48374
|
-
added: false
|
|
48375
|
-
};
|
|
48376
|
-
}
|
|
48377
|
-
const section = sectionFor(text, providerId);
|
|
48378
|
-
if (!section) throw new Error(`Codex provider section "${providerId}" is missing`);
|
|
48379
|
-
const eol = preferredEol(text);
|
|
48380
|
-
const prefix = section.end > 0 && !/(?:\r\n|\r|\n)$/.test(text.slice(0, section.end)) ? eol : "";
|
|
48381
|
-
const line = `${key} = ${encodeValue(value)}${eol}`;
|
|
48382
|
-
return { text: text.slice(0, section.end) + prefix + line + text.slice(section.end), added: true };
|
|
48383
|
-
}
|
|
48384
|
-
function restoreCodexProviderField(text, providerId, key, snapshot) {
|
|
48385
|
-
const existing = fieldFor(text, providerId, key);
|
|
48386
|
-
if (!existing) throw new Error(`Codex provider field "${providerId}.${key}" is missing`);
|
|
48387
|
-
return text.slice(0, existing.start) + snapshot.encoded + text.slice(existing.end);
|
|
48388
|
-
}
|
|
48389
|
-
function removeCodexProviderField(text, providerId, key) {
|
|
48390
|
-
const existing = fieldFor(text, providerId, key);
|
|
48391
|
-
return existing ? text.slice(0, existing.lineStart) + text.slice(existing.lineEnd) : text;
|
|
48392
|
-
}
|
|
48393
|
-
function appendCodexProviderSection(text, providerId, fields) {
|
|
48394
|
-
if (sectionFor(text, providerId)) throw new Error(`Codex provider section "${providerId}" already exists`);
|
|
48395
|
-
const eol = preferredEol(text);
|
|
48396
|
-
const separator = text.length === 0 ? "" : /(?:\r\n|\r|\n)$/.test(text) ? eol : eol + eol;
|
|
48397
|
-
const body = Object.entries(fields).map(([key, value]) => `${key} = ${encodeValue(value)}`).join(eol);
|
|
48398
|
-
return `${text}${separator}[model_providers.${providerId}]${eol}${body}${eol}`;
|
|
48399
|
-
}
|
|
48400
|
-
function removeCodexProviderSectionIfEmpty(text, providerId) {
|
|
48401
|
-
const section = sectionFor(text, providerId);
|
|
48402
|
-
if (!section) return text;
|
|
48403
|
-
const body = text.slice(section.bodyStart, section.end);
|
|
48404
|
-
const meaningful = body.split(/\r\n|\r|\n/).some((line) => {
|
|
48405
|
-
const trimmed = line.trim();
|
|
48406
|
-
return trimmed.length > 0;
|
|
48407
|
-
});
|
|
48408
|
-
if (meaningful) return text;
|
|
48409
|
-
return text.slice(0, section.headerStart) + text.slice(section.end);
|
|
48410
|
-
}
|
|
48411
|
-
function removeCodexProviderSectionWithPrefixIfEmpty(text, providerId, prefixLength) {
|
|
48412
|
-
const section = sectionFor(text, providerId);
|
|
48413
|
-
if (!section) return text;
|
|
48414
|
-
const withoutSection = removeCodexProviderSectionIfEmpty(text, providerId);
|
|
48415
|
-
if (withoutSection === text) return text;
|
|
48416
|
-
const start = Math.max(0, section.headerStart - prefixLength);
|
|
48417
|
-
return text.slice(0, start) + text.slice(section.end);
|
|
48418
|
-
}
|
|
48419
|
-
|
|
48420
|
-
// src/codex-history.ts
|
|
48421
|
-
var LEGACY_BILI_CODEX_PROVIDER_IDS = ["billion-context-codex", "bili_chatgpt"];
|
|
48422
|
-
function codexHome(configPath) {
|
|
48423
|
-
return path6.dirname(configPath);
|
|
48424
|
-
}
|
|
48425
|
-
function collectJsonlFiles(root, current = root, depth = 0) {
|
|
48426
|
-
if (depth > 10 || !existsSync5(current)) return [];
|
|
48427
|
-
const out = [];
|
|
48428
|
-
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
48429
|
-
const filePath = path6.join(current, entry.name);
|
|
48430
|
-
if (entry.isDirectory()) out.push(...collectJsonlFiles(root, filePath, depth + 1));
|
|
48431
|
-
else if (entry.isFile() && entry.name.endsWith(".jsonl")) out.push(filePath);
|
|
48432
|
-
}
|
|
48433
|
-
return out;
|
|
48434
|
-
}
|
|
48435
|
-
function legacySessionMeta(line) {
|
|
48436
|
-
if (!line.includes('"session_meta"') || !line.includes('"model_provider"')) return false;
|
|
48437
|
-
try {
|
|
48438
|
-
const value = JSON.parse(line);
|
|
48439
|
-
if (value.type !== "session_meta" || !value.payload || typeof value.payload !== "object") return false;
|
|
48440
|
-
const provider = value.payload.model_provider;
|
|
48441
|
-
return typeof provider === "string" && LEGACY_BILI_CODEX_PROVIDER_IDS.includes(provider);
|
|
48442
|
-
} catch {
|
|
48443
|
-
return false;
|
|
48444
|
-
}
|
|
48445
|
-
}
|
|
48446
|
-
function sessionFiles(configPath) {
|
|
48447
|
-
const root = codexHome(configPath);
|
|
48448
|
-
const files = [
|
|
48449
|
-
...collectJsonlFiles(path6.join(root, "sessions")),
|
|
48450
|
-
...collectJsonlFiles(path6.join(root, "archived_sessions"))
|
|
48451
|
-
];
|
|
48452
|
-
return files.flatMap((filePath) => {
|
|
48453
|
-
const before = statSync2(filePath);
|
|
48454
|
-
const matches = readFileSync4(filePath, "utf8").split(/\r\n|\r|\n/).filter(legacySessionMeta).length;
|
|
48455
|
-
const after = statSync2(filePath);
|
|
48456
|
-
if (before.size !== after.size || before.mtimeMs !== after.mtimeMs) {
|
|
48457
|
-
throw new Error(`Codex session file changed while being inspected: ${filePath}`);
|
|
48458
|
-
}
|
|
48459
|
-
return matches > 0 ? [{
|
|
48460
|
-
path: filePath,
|
|
48461
|
-
relative: path6.relative(root, filePath),
|
|
48462
|
-
matches,
|
|
48463
|
-
size: after.size,
|
|
48464
|
-
mtimeMs: after.mtimeMs,
|
|
48465
|
-
mode: after.mode
|
|
48466
|
-
}] : [];
|
|
48467
|
-
});
|
|
48468
|
-
}
|
|
48469
|
-
function parseTopLevelString(configText, key) {
|
|
48470
|
-
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
48471
|
-
for (const line of configText.split(/\r\n|\r|\n/)) {
|
|
48472
|
-
if (/^\s*\[/.test(line)) break;
|
|
48473
|
-
const match = line.match(new RegExp(`^\\s*${escaped}\\s*=\\s*("(?:\\\\.|[^"\\\\])*"|'[^']*')\\s*(?:#.*)?$`));
|
|
48474
|
-
if (!match) continue;
|
|
48475
|
-
if (match[1].startsWith("'")) return match[1].slice(1, -1);
|
|
48476
|
-
try {
|
|
48477
|
-
const value = JSON.parse(match[1]);
|
|
48478
|
-
return typeof value === "string" ? value : void 0;
|
|
48479
|
-
} catch {
|
|
48480
|
-
return void 0;
|
|
48481
|
-
}
|
|
48482
|
-
}
|
|
48483
|
-
return void 0;
|
|
48484
|
-
}
|
|
48485
|
-
function resolveUserPath(value) {
|
|
48486
|
-
if (value === "~") return homedir3();
|
|
48487
|
-
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
48488
|
-
return path6.join(homedir3(), value.slice(2));
|
|
48489
|
-
}
|
|
48490
|
-
return path6.resolve(value);
|
|
48491
|
-
}
|
|
48492
|
-
function stateDbPaths(configPath) {
|
|
48493
|
-
const home = codexHome(configPath);
|
|
48494
|
-
const paths = [path6.join(home, "state_5.sqlite")];
|
|
48495
|
-
const configText = readCodexConfig(configPath);
|
|
48496
|
-
const configured = parseTopLevelString(configText, "sqlite_home");
|
|
48497
|
-
const environment = process.env.CODEX_SQLITE_HOME?.trim();
|
|
48498
|
-
const extra = configured ?? environment;
|
|
48499
|
-
if (extra) {
|
|
48500
|
-
const candidate = path6.join(resolveUserPath(extra), "state_5.sqlite");
|
|
48501
|
-
if (!paths.includes(candidate)) paths.push(candidate);
|
|
48502
|
-
}
|
|
48503
|
-
return paths.filter(existsSync5);
|
|
48504
|
-
}
|
|
48505
|
-
async function sqliteModule() {
|
|
48506
|
-
const specifier = "node:sqlite";
|
|
48507
|
-
return await import(specifier);
|
|
48508
|
-
}
|
|
48509
|
-
function hasProviderColumn(database) {
|
|
48510
|
-
return database.prepare("PRAGMA table_info(threads)").all().some((row) => row.name === "model_provider");
|
|
48511
|
-
}
|
|
48512
|
-
function placeholders(count) {
|
|
48513
|
-
return Array.from({ length: count }, () => "?").join(",");
|
|
48514
|
-
}
|
|
48515
|
-
async function countStateRows(configPath) {
|
|
48516
|
-
const dbPaths = stateDbPaths(configPath);
|
|
48517
|
-
if (dbPaths.length === 0) return { count: 0, supported: true };
|
|
48518
|
-
let sqlite;
|
|
48519
|
-
try {
|
|
48520
|
-
sqlite = await sqliteModule();
|
|
48521
|
-
} catch (error) {
|
|
48522
|
-
return { count: 0, supported: false, error: `state DB repair requires a Node runtime with node:sqlite: ${String(error)}` };
|
|
48523
|
-
}
|
|
48524
|
-
let count = 0;
|
|
48525
|
-
try {
|
|
48526
|
-
for (const dbPath of dbPaths) {
|
|
48527
|
-
const database = new sqlite.DatabaseSync(dbPath);
|
|
48528
|
-
try {
|
|
48529
|
-
database.exec("PRAGMA busy_timeout = 5000");
|
|
48530
|
-
if (!hasProviderColumn(database)) continue;
|
|
48531
|
-
const row = database.prepare(
|
|
48532
|
-
`SELECT COUNT(*) AS count FROM threads WHERE model_provider IN (${placeholders(LEGACY_BILI_CODEX_PROVIDER_IDS.length)})`
|
|
48533
|
-
).get(...LEGACY_BILI_CODEX_PROVIDER_IDS);
|
|
48534
|
-
const value = row?.count;
|
|
48535
|
-
if (typeof value === "number") count += value;
|
|
48536
|
-
else if (typeof value === "bigint") count += Number(value);
|
|
48537
|
-
} finally {
|
|
48538
|
-
database.close();
|
|
48539
|
-
}
|
|
48540
|
-
}
|
|
48541
|
-
return { count, supported: true };
|
|
48542
|
-
} catch (error) {
|
|
48543
|
-
return { count, supported: false, error: String(error) };
|
|
48544
|
-
}
|
|
48545
|
-
}
|
|
48546
|
-
async function previewLegacyCodexHistory(configPath = codexConfigFile()) {
|
|
48547
|
-
const provider = resolveActiveCodexProvider(configPath);
|
|
48548
|
-
const files = sessionFiles(configPath);
|
|
48549
|
-
const state = await countStateRows(configPath);
|
|
48550
|
-
return {
|
|
48551
|
-
targetProviderId: provider.id,
|
|
48552
|
-
sourceProviderIds: [...LEGACY_BILI_CODEX_PROVIDER_IDS],
|
|
48553
|
-
jsonlFiles: files.length,
|
|
48554
|
-
sessions: files.reduce((sum, file) => sum + file.matches, 0),
|
|
48555
|
-
stateRows: state.count,
|
|
48556
|
-
stateDbSupported: state.supported,
|
|
48557
|
-
...state.error ? { stateDbError: state.error } : {}
|
|
48558
|
-
};
|
|
48559
|
-
}
|
|
48560
|
-
function assertSessionFileUnchanged(file) {
|
|
48561
|
-
const current = statSync2(file.path);
|
|
48562
|
-
if (current.size !== file.size || current.mtimeMs !== file.mtimeMs) {
|
|
48563
|
-
throw new Error(`Codex session file changed during repair: ${file.path}`);
|
|
48564
|
-
}
|
|
48565
|
-
}
|
|
48566
|
-
function rewriteSessionFile(file, targetProviderId) {
|
|
48567
|
-
assertSessionFileUnchanged(file);
|
|
48568
|
-
const before = readFileSync4(file.path, "utf8");
|
|
48569
|
-
assertSessionFileUnchanged(file);
|
|
48570
|
-
const eol = before.includes("\r\n") ? "\r\n" : "\n";
|
|
48571
|
-
const endsWithEol = /(?:\r\n|\r|\n)$/.test(before);
|
|
48572
|
-
let changed = false;
|
|
48573
|
-
const lines = before.split(/\r\n|\r|\n/);
|
|
48574
|
-
if (endsWithEol) lines.pop();
|
|
48575
|
-
const after = lines.map((line) => {
|
|
48576
|
-
if (!legacySessionMeta(line)) return line;
|
|
48577
|
-
const value = JSON.parse(line);
|
|
48578
|
-
const payload = value.payload;
|
|
48579
|
-
payload.model_provider = targetProviderId;
|
|
48580
|
-
changed = true;
|
|
48581
|
-
return JSON.stringify(value);
|
|
48582
|
-
}).join(eol) + (endsWithEol ? eol : "");
|
|
48583
|
-
if (!changed) return false;
|
|
48584
|
-
const tempPath = `${file.path}.${process.pid}.${randomUUID()}.tmp`;
|
|
48585
|
-
try {
|
|
48586
|
-
writeFileSync4(tempPath, after, { encoding: "utf8", mode: file.mode });
|
|
48587
|
-
assertSessionFileUnchanged(file);
|
|
48588
|
-
renameSync3(tempPath, file.path);
|
|
48589
|
-
} catch (error) {
|
|
48590
|
-
rmSync(tempPath, { force: true });
|
|
48591
|
-
throw error;
|
|
48592
|
-
}
|
|
48593
|
-
return true;
|
|
48594
|
-
}
|
|
48595
|
-
function stateBackupPath(backupPath, configPath, dbPath) {
|
|
48596
|
-
const root = codexHome(configPath);
|
|
48597
|
-
const relative = path6.relative(root, dbPath);
|
|
48598
|
-
if (relative && !relative.startsWith(`..${path6.sep}`) && relative !== ".." && !path6.isAbsolute(relative)) {
|
|
48599
|
-
return path6.join(backupPath, "state", relative);
|
|
48600
|
-
}
|
|
48601
|
-
const digest = createHash3("sha256").update(path6.resolve(dbPath)).digest("hex").slice(0, 16);
|
|
48602
|
-
return path6.join(backupPath, "state", `external-${digest}`, path6.basename(dbPath));
|
|
48603
|
-
}
|
|
48604
|
-
async function repairLegacyCodexHistory(configPath = codexConfigFile()) {
|
|
48605
|
-
const preview = await previewLegacyCodexHistory(configPath);
|
|
48606
|
-
if (!preview.stateDbSupported) throw new Error(preview.stateDbError ?? "Codex state DB cannot be repaired safely");
|
|
48607
|
-
const files = sessionFiles(configPath);
|
|
48608
|
-
const databases = stateDbPaths(configPath);
|
|
48609
|
-
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
48610
|
-
const backupPath = path6.join(dataDir(), "backups", "codex-history-repair", stamp);
|
|
48611
|
-
mkdirSync5(backupPath, { recursive: true });
|
|
48612
|
-
for (const file of files) {
|
|
48613
|
-
assertSessionFileUnchanged(file);
|
|
48614
|
-
const destination = path6.join(backupPath, "jsonl", file.relative);
|
|
48615
|
-
mkdirSync5(path6.dirname(destination), { recursive: true });
|
|
48616
|
-
copyFileSync(file.path, destination);
|
|
48617
|
-
assertSessionFileUnchanged(file);
|
|
48618
|
-
}
|
|
48619
|
-
const sqlite = databases.length > 0 ? await sqliteModule() : void 0;
|
|
48620
|
-
const opened = [];
|
|
48621
|
-
try {
|
|
48622
|
-
if (sqlite) {
|
|
48623
|
-
for (const dbPath of databases) {
|
|
48624
|
-
const database = new sqlite.DatabaseSync(dbPath);
|
|
48625
|
-
opened.push({ database, path: dbPath });
|
|
48626
|
-
database.exec("PRAGMA busy_timeout = 5000");
|
|
48627
|
-
const destination = stateBackupPath(backupPath, configPath, dbPath);
|
|
48628
|
-
mkdirSync5(path6.dirname(destination), { recursive: true });
|
|
48629
|
-
await sqlite.backup(database, destination);
|
|
48630
|
-
}
|
|
48631
|
-
}
|
|
48632
|
-
let migratedJsonlFiles = 0;
|
|
48633
|
-
for (const file of files) if (rewriteSessionFile(file, preview.targetProviderId)) migratedJsonlFiles++;
|
|
48634
|
-
let migratedStateRows = 0;
|
|
48635
|
-
for (const entry of opened) {
|
|
48636
|
-
if (!hasProviderColumn(entry.database)) continue;
|
|
48637
|
-
entry.database.exec("BEGIN IMMEDIATE");
|
|
48638
|
-
try {
|
|
48639
|
-
const result = entry.database.prepare(
|
|
48640
|
-
`UPDATE threads SET model_provider = ? WHERE model_provider IN (${placeholders(LEGACY_BILI_CODEX_PROVIDER_IDS.length)})`
|
|
48641
|
-
).run(preview.targetProviderId, ...LEGACY_BILI_CODEX_PROVIDER_IDS);
|
|
48642
|
-
migratedStateRows += Number(result.changes);
|
|
48643
|
-
entry.database.exec("COMMIT");
|
|
48644
|
-
} catch (error) {
|
|
48645
|
-
entry.database.exec("ROLLBACK");
|
|
48646
|
-
throw error;
|
|
48647
|
-
}
|
|
48648
|
-
}
|
|
48649
|
-
return { ...preview, backupPath, migratedJsonlFiles, migratedStateRows };
|
|
48650
|
-
} catch (error) {
|
|
48651
|
-
throw new Error(`Codex history repair stopped; backups are at ${backupPath}: ${String(error)}`, { cause: error });
|
|
48652
|
-
} finally {
|
|
48653
|
-
for (const entry of opened) entry.database.close();
|
|
48654
|
-
if (files.length === 0 && databases.length === 0) rmSync(backupPath, { recursive: true, force: true });
|
|
48655
|
-
}
|
|
48656
|
-
}
|
|
48657
|
-
|
|
48658
|
-
// src/web/api.ts
|
|
48196
|
+
import { randomUUID } from "crypto";
|
|
48659
48197
|
function readConfig() {
|
|
48660
48198
|
const parsed = safeReadJson(configFile());
|
|
48661
48199
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
@@ -48673,16 +48211,16 @@ function readUpstreamSettings() {
|
|
|
48673
48211
|
}
|
|
48674
48212
|
function atomicWriteConfig(config) {
|
|
48675
48213
|
const filePath = configFile();
|
|
48676
|
-
|
|
48677
|
-
const tempPath = `${filePath}.${process.pid}.${
|
|
48214
|
+
mkdirSync5(dirname4(filePath), { recursive: true });
|
|
48215
|
+
const tempPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
48678
48216
|
let descriptor;
|
|
48679
48217
|
try {
|
|
48680
48218
|
descriptor = openSync(tempPath, "wx", 384);
|
|
48681
|
-
|
|
48219
|
+
writeFileSync4(descriptor, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
48682
48220
|
fsyncSync(descriptor);
|
|
48683
48221
|
closeSync(descriptor);
|
|
48684
48222
|
descriptor = void 0;
|
|
48685
|
-
|
|
48223
|
+
renameSync3(tempPath, filePath);
|
|
48686
48224
|
} catch (error) {
|
|
48687
48225
|
if (descriptor !== void 0) closeSync(descriptor);
|
|
48688
48226
|
try {
|
|
@@ -48765,24 +48303,6 @@ async function handleConfigPut(req, res, onChanged, biliPort = 8787) {
|
|
|
48765
48303
|
res.writeHead(200, { "content-type": "application/json" });
|
|
48766
48304
|
res.end(JSON.stringify({ ok: true, providers: hasProviders ? Object.keys(routes).length : void 0 }));
|
|
48767
48305
|
}
|
|
48768
|
-
async function handleCodexHistoryGet(res) {
|
|
48769
|
-
try {
|
|
48770
|
-
const preview = await previewLegacyCodexHistory();
|
|
48771
|
-
res.writeHead(200, { "content-type": "application/json" });
|
|
48772
|
-
res.end(JSON.stringify(preview));
|
|
48773
|
-
} catch (error) {
|
|
48774
|
-
sendError(res, 409, String(error));
|
|
48775
|
-
}
|
|
48776
|
-
}
|
|
48777
|
-
async function handleCodexHistoryRepair(res) {
|
|
48778
|
-
try {
|
|
48779
|
-
const result = await repairLegacyCodexHistory();
|
|
48780
|
-
res.writeHead(200, { "content-type": "application/json" });
|
|
48781
|
-
res.end(JSON.stringify(result));
|
|
48782
|
-
} catch (error) {
|
|
48783
|
-
sendError(res, 409, String(error));
|
|
48784
|
-
}
|
|
48785
|
-
}
|
|
48786
48306
|
function sendError(res, status, message) {
|
|
48787
48307
|
res.writeHead(status, { "content-type": "application/json" });
|
|
48788
48308
|
res.end(JSON.stringify({ error: message }));
|
|
@@ -48816,7 +48336,7 @@ function version() {
|
|
|
48816
48336
|
try {
|
|
48817
48337
|
const here = fileURLToPath(import.meta.url);
|
|
48818
48338
|
const packagePath = join3(dirname5(here), "..", "..", "package.json");
|
|
48819
|
-
return JSON.parse(
|
|
48339
|
+
return JSON.parse(readFileSync4(packagePath, "utf8")).version ?? "dev";
|
|
48820
48340
|
} catch {
|
|
48821
48341
|
return "dev";
|
|
48822
48342
|
}
|
|
@@ -49111,7 +48631,9 @@ async function* compressLoopStream(initialUpstream, ctx, requestBody, requestOpt
|
|
|
49111
48631
|
}).filter((tc) => tc.name.length > 0);
|
|
49112
48632
|
const proxyCalls = toolCalls.filter((tc) => PROXY_TOOL_NAMES.has(tc.name));
|
|
49113
48633
|
const realCalls = toolCalls.filter((tc) => !PROXY_TOOL_NAMES.has(tc.name));
|
|
49114
|
-
const
|
|
48634
|
+
const mutatingProxy = proxyCalls.filter((tc) => MUTATING_PROXY_TOOLS.has(tc.name));
|
|
48635
|
+
const readonlyProxy = proxyCalls.filter((tc) => READONLY_PROXY_TOOLS.has(tc.name));
|
|
48636
|
+
const hasMutatingOnly = mutatingProxy.length > 0 && realCalls.length === 0;
|
|
49115
48637
|
if (usage) {
|
|
49116
48638
|
const prompt = usage.prompt_tokens ?? usage.input_tokens;
|
|
49117
48639
|
const det = usage.prompt_tokens_details ?? usage.prompt_cache_hit_tokens;
|
|
@@ -49127,7 +48649,27 @@ async function* compressLoopStream(initialUpstream, ctx, requestBody, requestOpt
|
|
|
49127
48649
|
ctx.session.stats.cacheSamples += 1;
|
|
49128
48650
|
}
|
|
49129
48651
|
}
|
|
49130
|
-
if (!
|
|
48652
|
+
if (!hasMutatingOnly) {
|
|
48653
|
+
for (const tc of readonlyProxy) {
|
|
48654
|
+
let args = {};
|
|
48655
|
+
try {
|
|
48656
|
+
args = JSON.parse(tc.arguments);
|
|
48657
|
+
} catch {
|
|
48658
|
+
args = {};
|
|
48659
|
+
}
|
|
48660
|
+
let result;
|
|
48661
|
+
try {
|
|
48662
|
+
result = executeProxyTool(tc.name, args, ctx);
|
|
48663
|
+
} catch (e) {
|
|
48664
|
+
result = `[${tc.name} FAILED: ${e instanceof Error ? e.message : String(e)}]`;
|
|
48665
|
+
}
|
|
48666
|
+
const preview = result.length > 120 ? result.slice(0, 120) + "..." : result;
|
|
48667
|
+
ctx.log(`[acp-proxy: ${tc.name} (${tc.id}) \u2192 ${preview.replace(/\n/g, " ")}]`);
|
|
48668
|
+
yield Buffer.from(
|
|
48669
|
+
buildContentSse(responseId, model, buildVisibilityMarker(tc.name, result)),
|
|
48670
|
+
"utf8"
|
|
48671
|
+
);
|
|
48672
|
+
}
|
|
49131
48673
|
for (const tc of realCalls) {
|
|
49132
48674
|
yield Buffer.from(buildToolCallSse(makeBase(), tc), "utf8");
|
|
49133
48675
|
}
|
|
@@ -49383,8 +48925,23 @@ async function* compressLoopAnthropicStream(initialUpstream, ctx, requestBody, r
|
|
|
49383
48925
|
}
|
|
49384
48926
|
clientIndex = state.clientIndex;
|
|
49385
48927
|
const proxyCalls = [...state.toolBlocks.values()].filter((b2) => PROXY_TOOL_NAMES.has(b2.name));
|
|
49386
|
-
const
|
|
49387
|
-
|
|
48928
|
+
const mutatingProxy = proxyCalls.filter((b2) => MUTATING_PROXY_TOOLS.has(b2.name));
|
|
48929
|
+
const readonlyProxy = proxyCalls.filter((b2) => READONLY_PROXY_TOOLS.has(b2.name));
|
|
48930
|
+
const hasMutatingOnly = mutatingProxy.length > 0 && !hasRealToolUse;
|
|
48931
|
+
if (!hasMutatingOnly) {
|
|
48932
|
+
for (const tc of readonlyProxy) {
|
|
48933
|
+
const args = safeParse2(tc.json);
|
|
48934
|
+
let result;
|
|
48935
|
+
try {
|
|
48936
|
+
result = executeProxyTool2(tc.name, args, ctx);
|
|
48937
|
+
} catch (e) {
|
|
48938
|
+
result = `[${tc.name} FAILED: ${e instanceof Error ? e.message : String(e)}]`;
|
|
48939
|
+
}
|
|
48940
|
+
const preview = result.length > 120 ? result.slice(0, 120) + "..." : result;
|
|
48941
|
+
ctx.log(`[acp-proxy: ${tc.name} (${tc.id}) \u2192 ${preview.replace(/\n/g, " ")}]`);
|
|
48942
|
+
yield Buffer.from(buildTextBlockSse(clientIndex, buildVisibilityMarker(tc.name, result)), "utf8");
|
|
48943
|
+
clientIndex++;
|
|
48944
|
+
}
|
|
49388
48945
|
const stop = hasRealToolUse ? "tool_use" : roundStopReason ?? "end_turn";
|
|
49389
48946
|
yield Buffer.from(buildTerminalSse(stop, totalOutputTokens, totalInputTokens, totalCachedTokens, messageId, model), "utf8");
|
|
49390
48947
|
return;
|
|
@@ -49797,6 +49354,31 @@ function replaceResponsesJsonText(parts, text) {
|
|
|
49797
49354
|
part.text = index === 0 ? text : "";
|
|
49798
49355
|
});
|
|
49799
49356
|
}
|
|
49357
|
+
function surfaceReadonlyJson(current, proxyCalls, ctx) {
|
|
49358
|
+
const markers = [];
|
|
49359
|
+
for (const call of proxyCalls) {
|
|
49360
|
+
if (MUTATING_PROXY_TOOLS.has(call.name)) continue;
|
|
49361
|
+
let args = {};
|
|
49362
|
+
try {
|
|
49363
|
+
args = JSON.parse(call.arguments);
|
|
49364
|
+
} catch {
|
|
49365
|
+
args = {};
|
|
49366
|
+
}
|
|
49367
|
+
let result;
|
|
49368
|
+
try {
|
|
49369
|
+
result = executeProxyTool3(call.name, args, ctx);
|
|
49370
|
+
ctx.log(`[acp-proxy: responses JSON ${call.name} (read-only) \u2192 ${result.slice(0, 120).replace(/\n/g, " ")}]`);
|
|
49371
|
+
} catch (e) {
|
|
49372
|
+
result = `\u274C [ACP] ${call.name} FAILED: ${String(e)}`;
|
|
49373
|
+
ctx.log(`[acp-proxy: responses JSON ${call.name} (read-only) FAILED: ${String(e)}]`);
|
|
49374
|
+
}
|
|
49375
|
+
markers.push(buildVisibilityMarker(call.name, result));
|
|
49376
|
+
}
|
|
49377
|
+
if (markers.length === 0) return current;
|
|
49378
|
+
const out = Array.isArray(current.output) ? [...current.output] : [];
|
|
49379
|
+
out.push({ type: "message", id: `msg_acp_ro_${Date.now()}_${markers.length}`, role: "assistant", content: [{ type: "output_text", text: markers.join("\n") }] });
|
|
49380
|
+
return { ...current, output: out };
|
|
49381
|
+
}
|
|
49800
49382
|
async function compressLoopResponsesJson(initialResponse, ctx, requestBody, requestOptions) {
|
|
49801
49383
|
let current = initialResponse;
|
|
49802
49384
|
for (let loopCount = 1; loopCount <= 5; loopCount++) {
|
|
@@ -49805,8 +49387,12 @@ async function compressLoopResponsesJson(initialResponse, ctx, requestBody, requ
|
|
|
49805
49387
|
const allCalls = [...output.calls, ...extracted.calls].filter((call) => call.name.length > 0);
|
|
49806
49388
|
const proxyCalls = allCalls.filter((call) => PROXY_TOOL_NAMES.has(call.name));
|
|
49807
49389
|
const realCalls = allCalls.filter((call) => !PROXY_TOOL_NAMES.has(call.name));
|
|
49808
|
-
|
|
49809
|
-
|
|
49390
|
+
const mutatingProxy = proxyCalls.filter((call) => MUTATING_PROXY_TOOLS.has(call.name));
|
|
49391
|
+
if (mutatingProxy.length === 0 || realCalls.length > 0) {
|
|
49392
|
+
if (proxyCalls.length > 0) {
|
|
49393
|
+
replaceResponsesJsonText(output.textParts, extracted.clean);
|
|
49394
|
+
current = surfaceReadonlyJson(current, proxyCalls, ctx);
|
|
49395
|
+
}
|
|
49810
49396
|
return current;
|
|
49811
49397
|
}
|
|
49812
49398
|
const inputItems = Array.isArray(requestBody.input) ? [...requestBody.input] : [];
|
|
@@ -49953,9 +49539,30 @@ async function* compressLoopResponsesStream(initialUpstream, ctx, requestBody, r
|
|
|
49953
49539
|
const allCalls = [...fcByItemId.values()].filter((c) => c.name.length > 0);
|
|
49954
49540
|
const proxyCalls = allCalls.filter((c) => PROXY_TOOL_NAMES.has(c.name));
|
|
49955
49541
|
const realCalls = allCalls.filter((c) => !PROXY_TOOL_NAMES.has(c.name));
|
|
49542
|
+
const readonlyProxy = proxyCalls.filter((c) => READONLY_PROXY_TOOLS.has(c.name));
|
|
49956
49543
|
log("debug", `[acp-diag] round ${loopCount} allCalls=[${allCalls.map((c) => c.name).join(",")}] realCalls=[${realCalls.map((c) => c.name).join(",")}] customToolCalls=${customToolCalls} text=${JSON.stringify(contentText.slice(0, 120))}`);
|
|
49957
|
-
const
|
|
49958
|
-
if (!
|
|
49544
|
+
const hasMutatingOnly = proxyCalls.some((c) => MUTATING_PROXY_TOOLS.has(c.name)) && realCalls.length === 0;
|
|
49545
|
+
if (!hasMutatingOnly) {
|
|
49546
|
+
for (const fc of readonlyProxy) {
|
|
49547
|
+
let args = {};
|
|
49548
|
+
try {
|
|
49549
|
+
args = JSON.parse(fc.arguments);
|
|
49550
|
+
} catch (e) {
|
|
49551
|
+
log("warn", `[acp-compress-args] ${fc.name} JSON.parse failed: ${String(e)}`);
|
|
49552
|
+
args = {};
|
|
49553
|
+
}
|
|
49554
|
+
let result;
|
|
49555
|
+
try {
|
|
49556
|
+
result = executeProxyTool3(fc.name, args, ctx);
|
|
49557
|
+
const preview = result.length > 120 ? result.slice(0, 120) + "..." : result;
|
|
49558
|
+
ctx.log(`[acp-proxy: responses ${fc.name} (read-only) (${fc.callId}) \u2192 ${preview.replace(/\n/g, " ")}]`);
|
|
49559
|
+
} catch (e) {
|
|
49560
|
+
result = `\u274C [ACP] ${fc.name} FAILED: ${String(e)}`;
|
|
49561
|
+
ctx.log(`[acp-proxy: responses ${fc.name} (read-only) (${fc.callId}) FAILED: ${String(e)}]`);
|
|
49562
|
+
}
|
|
49563
|
+
const markerItemId = `msg_acp_ro_${Date.now()}_${nextOutputIndex}`;
|
|
49564
|
+
yield Buffer.from(buildMessageItemSequence(markerItemId, nextOutputIndex++, buildVisibilityMarker(fc.name, result)), "utf8");
|
|
49565
|
+
}
|
|
49959
49566
|
let oi2 = nextOutputIndex;
|
|
49960
49567
|
for (const fc of realCalls) {
|
|
49961
49568
|
yield Buffer.from(buildFunctionCallEvents(fc, oi2), "utf8");
|
|
@@ -49967,7 +49574,8 @@ async function* compressLoopResponsesStream(initialUpstream, ctx, requestBody, r
|
|
|
49967
49574
|
return;
|
|
49968
49575
|
}
|
|
49969
49576
|
const hasUsage = !!responseObj?.usage;
|
|
49970
|
-
|
|
49577
|
+
const emittedReadonly = readonlyProxy.length > 0;
|
|
49578
|
+
if (contentText.length === 0 && realCalls.length === 0 && customToolCalls === 0 && !emittedReadonly && !hasUsage) {
|
|
49971
49579
|
ctx.log("[acp-proxy: empty upstream response (no content/usage) \u2014 injecting response.failed for client retry]");
|
|
49972
49580
|
yield Buffer.from(buildFailed(responseObj), "utf8");
|
|
49973
49581
|
return;
|
|
@@ -50048,6 +49656,1070 @@ async function* compressLoopResponsesStream(initialUpstream, ctx, requestBody, r
|
|
|
50048
49656
|
}
|
|
50049
49657
|
}
|
|
50050
49658
|
|
|
49659
|
+
// src/loop/core.ts
|
|
49660
|
+
var MAX_LOOP_ROUNDS = 10;
|
|
49661
|
+
function executeProxyTool4(toolName, args, ctx, callId) {
|
|
49662
|
+
if (toolName === "compress") {
|
|
49663
|
+
return applyRanges(parseCompressInput(args, callId), ctx);
|
|
49664
|
+
}
|
|
49665
|
+
if (toolName === "decompress") {
|
|
49666
|
+
return resolveDecompress(args, ctx);
|
|
49667
|
+
}
|
|
49668
|
+
if (toolName === "search_context") {
|
|
49669
|
+
const query = typeof args.query === "string" ? args.query : "";
|
|
49670
|
+
if (query.length === 0) return "[search_context FAILED: query is required]";
|
|
49671
|
+
const limit = typeof args.limit === "number" && args.limit > 0 ? Math.floor(args.limit) : 5;
|
|
49672
|
+
const blocks = ctx.core.search(query, ctx.session.state).slice(0, limit);
|
|
49673
|
+
if (blocks.length === 0) return `[No blocks matched "${query}"]`;
|
|
49674
|
+
const lines = blocks.map((b2) => {
|
|
49675
|
+
const topic = b2.topic ?? "(no topic)";
|
|
49676
|
+
const preview = b2.summary.length > 200 ? b2.summary.slice(0, 200) + "..." : b2.summary;
|
|
49677
|
+
return `${b2.blockId} (T${b2.tier}) "${topic}"
|
|
49678
|
+
${preview}`;
|
|
49679
|
+
});
|
|
49680
|
+
return `Found ${blocks.length} block(s) for "${query}":
|
|
49681
|
+
|
|
49682
|
+
${lines.join("\n\n")}`;
|
|
49683
|
+
}
|
|
49684
|
+
if (toolName === "acp_status") {
|
|
49685
|
+
return buildStatusReport(ctx.session.state, ctx.messages, estimateTokensFast);
|
|
49686
|
+
}
|
|
49687
|
+
return `[Unknown proxy tool: ${toolName}]`;
|
|
49688
|
+
}
|
|
49689
|
+
function recordUsage(ctx, usage, round) {
|
|
49690
|
+
const prompt = usage.inputTokens;
|
|
49691
|
+
const cached = usage.cachedTokens;
|
|
49692
|
+
const out = usage.outputTokens;
|
|
49693
|
+
if (typeof prompt === "number") ctx.session.stats.inputTokens += prompt;
|
|
49694
|
+
ctx.session.stats.lastInputTokens = (typeof prompt === "number" ? prompt : 0) + (typeof cached === "number" ? cached : 0);
|
|
49695
|
+
if (typeof cached === "number") ctx.session.stats.cachedTokens += cached;
|
|
49696
|
+
if (typeof out === "number") ctx.session.stats.outputTokens += out;
|
|
49697
|
+
ctx.session.stats.cacheSamples += 1;
|
|
49698
|
+
const hitPct = typeof prompt === "number" && typeof cached === "number" && prompt + cached > 0 ? Math.round(cached / (prompt + cached) * 100) : 0;
|
|
49699
|
+
ctx.log(
|
|
49700
|
+
`[acp-usage] round ${round} input=${ctx.session.stats.lastInputTokens} cached=${cached ?? 0} (cache hit ${hitPct}%)`
|
|
49701
|
+
);
|
|
49702
|
+
}
|
|
49703
|
+
async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adapter, systemPrompt) {
|
|
49704
|
+
let activeClearTimer = null;
|
|
49705
|
+
let currentUpstream = upstream;
|
|
49706
|
+
const coreMessages = [...ctx.messages];
|
|
49707
|
+
try {
|
|
49708
|
+
for (let round = 1; round <= MAX_LOOP_ROUNDS; round++) {
|
|
49709
|
+
let assistantText = "";
|
|
49710
|
+
const calls = [];
|
|
49711
|
+
let usage = {};
|
|
49712
|
+
let finishReason;
|
|
49713
|
+
for await (const ev of adapter.parseStream(currentUpstream, round)) {
|
|
49714
|
+
if (ev.kind === "text") {
|
|
49715
|
+
assistantText += ev.delta;
|
|
49716
|
+
if (!ctx.textProtocol && round === 1 && ev.raw) {
|
|
49717
|
+
yield ev.raw;
|
|
49718
|
+
}
|
|
49719
|
+
} else if (ev.kind === "tool_call") {
|
|
49720
|
+
calls.push({ name: ev.name, callId: ev.callId, arguments: ev.arguments });
|
|
49721
|
+
} else if (ev.kind === "usage") {
|
|
49722
|
+
usage = {
|
|
49723
|
+
inputTokens: ev.inputTokens,
|
|
49724
|
+
outputTokens: ev.outputTokens,
|
|
49725
|
+
cachedTokens: ev.cachedTokens
|
|
49726
|
+
};
|
|
49727
|
+
} else if (ev.kind === "done") {
|
|
49728
|
+
finishReason = ev.finishReason;
|
|
49729
|
+
} else if (ev.kind === "meta") {
|
|
49730
|
+
if (round === 1 || !ev.firstRoundOnly) {
|
|
49731
|
+
yield ev.chunk;
|
|
49732
|
+
}
|
|
49733
|
+
}
|
|
49734
|
+
}
|
|
49735
|
+
if (usage.inputTokens !== void 0 || usage.outputTokens !== void 0 || usage.cachedTokens !== void 0) {
|
|
49736
|
+
recordUsage(ctx, usage, round);
|
|
49737
|
+
}
|
|
49738
|
+
let resolvedText = assistantText;
|
|
49739
|
+
let allCalls = calls;
|
|
49740
|
+
if (ctx.textProtocol && assistantText.length > 0 && adapter.extractTextTriggers) {
|
|
49741
|
+
const extracted = adapter.extractTextTriggers(assistantText);
|
|
49742
|
+
resolvedText = extracted.clean;
|
|
49743
|
+
allCalls = [...calls, ...extracted.calls];
|
|
49744
|
+
}
|
|
49745
|
+
if (ctx.textProtocol && resolvedText.length > 0) {
|
|
49746
|
+
yield adapter.emitText(resolvedText);
|
|
49747
|
+
} else if (!ctx.textProtocol && round > 1 && resolvedText.length > 0) {
|
|
49748
|
+
yield adapter.emitText(resolvedText);
|
|
49749
|
+
}
|
|
49750
|
+
let realCalls = 0;
|
|
49751
|
+
const realToolCalls = [];
|
|
49752
|
+
const proxyResults = [];
|
|
49753
|
+
for (const call of allCalls) {
|
|
49754
|
+
if (PROXY_TOOL_NAMES.has(call.name)) {
|
|
49755
|
+
let parsedArgs;
|
|
49756
|
+
try {
|
|
49757
|
+
parsedArgs = call.arguments.length > 0 ? JSON.parse(call.arguments) : {};
|
|
49758
|
+
} catch {
|
|
49759
|
+
parsedArgs = {};
|
|
49760
|
+
}
|
|
49761
|
+
const result = executeProxyTool4(call.name, parsedArgs, ctx, call.callId);
|
|
49762
|
+
proxyResults.push({ name: call.name, callId: call.callId, result, arguments: call.arguments });
|
|
49763
|
+
yield adapter.emitMarker(call.name, result);
|
|
49764
|
+
} else {
|
|
49765
|
+
realToolCalls.push(call);
|
|
49766
|
+
realCalls += 1;
|
|
49767
|
+
}
|
|
49768
|
+
}
|
|
49769
|
+
if (ctx.debug) {
|
|
49770
|
+
const callSummary = allCalls.map((c) => {
|
|
49771
|
+
const argSnippet = c.arguments.length > 200 ? c.arguments.slice(0, 200) + "..." : c.arguments;
|
|
49772
|
+
return `${c.name}(${argSnippet})`;
|
|
49773
|
+
}).join(" | ");
|
|
49774
|
+
ctx.log(`[acp-loop] round ${round}: ${allCalls.length} call(s): ${callSummary || "(none)"}`);
|
|
49775
|
+
for (const pr2 of proxyResults) {
|
|
49776
|
+
const resSnippet = pr2.result.length > 300 ? pr2.result.slice(0, 300) + "..." : pr2.result;
|
|
49777
|
+
ctx.log(`[acp-loop] \u2192 ${pr2.name} result: ${resSnippet}`);
|
|
49778
|
+
}
|
|
49779
|
+
if (realCalls > 0) ctx.log(`[acp-loop] round ${round}: ${realCalls} real tool call(s) forwarded to client`);
|
|
49780
|
+
}
|
|
49781
|
+
if (proxyResults.length > 0) {
|
|
49782
|
+
if (resolvedText.length > 0) {
|
|
49783
|
+
coreMessages.push({
|
|
49784
|
+
id: `acp_loop_r${round}_asst`,
|
|
49785
|
+
role: "assistant",
|
|
49786
|
+
contentType: "text",
|
|
49787
|
+
text: resolvedText
|
|
49788
|
+
});
|
|
49789
|
+
}
|
|
49790
|
+
for (const pr2 of proxyResults) {
|
|
49791
|
+
if (ctx.textProtocol) {
|
|
49792
|
+
coreMessages.push({
|
|
49793
|
+
id: `acp_loop_r${round}_marker_${pr2.callId}`,
|
|
49794
|
+
role: "user",
|
|
49795
|
+
contentType: "text",
|
|
49796
|
+
text: buildVisibilityMarker(pr2.name, pr2.result)
|
|
49797
|
+
});
|
|
49798
|
+
} else {
|
|
49799
|
+
coreMessages.push({
|
|
49800
|
+
id: `acp_loop_r${round}_asst_tc_${pr2.callId}`,
|
|
49801
|
+
role: "assistant",
|
|
49802
|
+
contentType: "tool-call",
|
|
49803
|
+
toolName: pr2.name,
|
|
49804
|
+
toolCallId: pr2.callId,
|
|
49805
|
+
text: pr2.arguments
|
|
49806
|
+
});
|
|
49807
|
+
coreMessages.push({
|
|
49808
|
+
id: `acp_loop_r${round}_tool_${pr2.callId}`,
|
|
49809
|
+
role: "tool",
|
|
49810
|
+
contentType: "tool-result",
|
|
49811
|
+
toolCallId: pr2.callId,
|
|
49812
|
+
text: pr2.result
|
|
49813
|
+
});
|
|
49814
|
+
}
|
|
49815
|
+
}
|
|
49816
|
+
if (!ctx.textProtocol) {
|
|
49817
|
+
const hidden = hideConsumedCompressCalls(ctx.session.state, coreMessages);
|
|
49818
|
+
if (hidden.hidden > 0) {
|
|
49819
|
+
ctx.log(`[acp-loop] round ${round} hideConsumed hid ${hidden.hidden} compress record(s)`);
|
|
49820
|
+
coreMessages.length = 0;
|
|
49821
|
+
coreMessages.push(...hidden.messages);
|
|
49822
|
+
}
|
|
49823
|
+
}
|
|
49824
|
+
}
|
|
49825
|
+
for (const tc of realToolCalls) {
|
|
49826
|
+
yield adapter.emitToolCall(tc);
|
|
49827
|
+
}
|
|
49828
|
+
const reRequest = proxyResults.length > 0 && realCalls === 0;
|
|
49829
|
+
if (!reRequest) {
|
|
49830
|
+
yield adapter.emitCompletion({ finishReason, usage });
|
|
49831
|
+
return;
|
|
49832
|
+
}
|
|
49833
|
+
if (round >= MAX_LOOP_ROUNDS) {
|
|
49834
|
+
ctx.log(`[acp-loop] round ${round} hit MAX_LOOP_ROUNDS; completing gracefully`);
|
|
49835
|
+
log("warn", `[acp-loop] loop limit (${MAX_LOOP_ROUNDS}) reached; completing gracefully`);
|
|
49836
|
+
yield adapter.emitCompletion({ finishReason: "length", usage });
|
|
49837
|
+
return;
|
|
49838
|
+
}
|
|
49839
|
+
ctx.log(`[acp-loop] round ${round} saw mutating proxy tool; re-requesting`);
|
|
49840
|
+
const newBody = adapter.buildRequest(coreMessages, systemPrompt, requestBody);
|
|
49841
|
+
if (process.env.ACP_DUMP_REQ !== "0" && ctx.debug) {
|
|
49842
|
+
try {
|
|
49843
|
+
const fs5 = await import("fs");
|
|
49844
|
+
const dumpDir = process.env.ACP_DUMP_DIR || `${process.env.HOME}/.local/state/billion-context/dumps`;
|
|
49845
|
+
fs5.mkdirSync(dumpDir, { recursive: true });
|
|
49846
|
+
const sid = ctx.session.id ?? "unknown";
|
|
49847
|
+
fs5.writeFileSync(`${dumpDir}/req-${Date.now()}-${sid}-REREQUEST.json`, JSON.stringify(newBody, null, 2));
|
|
49848
|
+
} catch {
|
|
49849
|
+
}
|
|
49850
|
+
}
|
|
49851
|
+
const { response: resp, clearTimer } = await fetchWithTimeout(requestOptions.url, {
|
|
49852
|
+
method: "POST",
|
|
49853
|
+
headers: requestOptions.headers,
|
|
49854
|
+
body: JSON.stringify(newBody),
|
|
49855
|
+
...ctx.proxyUrl ? { dispatcher: proxyDispatcher(ctx.proxyUrl) } : {}
|
|
49856
|
+
});
|
|
49857
|
+
if (!resp.ok || !resp.body) {
|
|
49858
|
+
clearTimer();
|
|
49859
|
+
const errText = await resp.text().catch(() => "upstream error");
|
|
49860
|
+
ctx.log(`[acp-proxy: compress loop upstream error ${resp.status}: ${errText.slice(0, 200)}]`);
|
|
49861
|
+
log("error", `[acp-loop] upstream error ${resp.status}: ${errText.slice(0, 200)}`);
|
|
49862
|
+
yield adapter.emitError(`upstream error ${resp.status}: ${errText.slice(0, 200)}`);
|
|
49863
|
+
return;
|
|
49864
|
+
}
|
|
49865
|
+
currentUpstream = resp.body;
|
|
49866
|
+
if (activeClearTimer) activeClearTimer();
|
|
49867
|
+
activeClearTimer = clearTimer;
|
|
49868
|
+
}
|
|
49869
|
+
} finally {
|
|
49870
|
+
if (activeClearTimer) {
|
|
49871
|
+
activeClearTimer();
|
|
49872
|
+
activeClearTimer = null;
|
|
49873
|
+
}
|
|
49874
|
+
}
|
|
49875
|
+
}
|
|
49876
|
+
|
|
49877
|
+
// src/loop/adapter-responses.ts
|
|
49878
|
+
async function* iterSseEvents(stream2) {
|
|
49879
|
+
const reader = stream2.getReader();
|
|
49880
|
+
let buf = "";
|
|
49881
|
+
try {
|
|
49882
|
+
while (true) {
|
|
49883
|
+
let done;
|
|
49884
|
+
let value;
|
|
49885
|
+
try {
|
|
49886
|
+
({ done, value } = await reader.read());
|
|
49887
|
+
} catch {
|
|
49888
|
+
break;
|
|
49889
|
+
}
|
|
49890
|
+
if (done) break;
|
|
49891
|
+
buf += new TextDecoder().decode(value, { stream: true });
|
|
49892
|
+
buf = buf.replace(/\r\n|\r/g, "\n");
|
|
49893
|
+
let idx;
|
|
49894
|
+
while ((idx = buf.indexOf("\n\n")) >= 0) {
|
|
49895
|
+
const raw = buf.slice(0, idx);
|
|
49896
|
+
buf = buf.slice(idx + 2);
|
|
49897
|
+
if (raw.trim().length > 0) yield raw;
|
|
49898
|
+
}
|
|
49899
|
+
}
|
|
49900
|
+
if (buf.trim().length > 0) yield buf;
|
|
49901
|
+
} finally {
|
|
49902
|
+
reader.releaseLock();
|
|
49903
|
+
}
|
|
49904
|
+
}
|
|
49905
|
+
function extractEventType2(rawEvent) {
|
|
49906
|
+
for (const l of rawEvent.split("\n")) {
|
|
49907
|
+
if (l.startsWith("event:")) return l.slice(6).trim();
|
|
49908
|
+
}
|
|
49909
|
+
return null;
|
|
49910
|
+
}
|
|
49911
|
+
function extractDataLine2(rawEvent) {
|
|
49912
|
+
const parts = [];
|
|
49913
|
+
for (const l of rawEvent.split("\n")) {
|
|
49914
|
+
if (l.startsWith("data:")) {
|
|
49915
|
+
let v2 = l.slice(5);
|
|
49916
|
+
if (v2.startsWith(" ")) v2 = v2.slice(1);
|
|
49917
|
+
parts.push(v2);
|
|
49918
|
+
}
|
|
49919
|
+
}
|
|
49920
|
+
return parts.length ? parts.join("\n") : null;
|
|
49921
|
+
}
|
|
49922
|
+
function buildMessageItemSequence2(itemId, outputIndex, text) {
|
|
49923
|
+
const item = { type: "message", id: itemId, role: "assistant", content: [] };
|
|
49924
|
+
const part = { type: "output_text", text: "" };
|
|
49925
|
+
const doneItem = {
|
|
49926
|
+
type: "message",
|
|
49927
|
+
id: itemId,
|
|
49928
|
+
role: "assistant",
|
|
49929
|
+
content: [{ type: "output_text", text }]
|
|
49930
|
+
};
|
|
49931
|
+
return Buffer.from(
|
|
49932
|
+
[
|
|
49933
|
+
`event: response.output_item.added
|
|
49934
|
+
data: ${JSON.stringify({ type: "response.output_item.added", output_index: outputIndex, item })}
|
|
49935
|
+
|
|
49936
|
+
`,
|
|
49937
|
+
`event: response.content_part.added
|
|
49938
|
+
data: ${JSON.stringify({ type: "response.content_part.added", item_id: itemId, output_index: outputIndex, part })}
|
|
49939
|
+
|
|
49940
|
+
`,
|
|
49941
|
+
`event: response.output_text.delta
|
|
49942
|
+
data: ${JSON.stringify({ type: "response.output_text.delta", item_id: itemId, output_index: outputIndex, delta: text })}
|
|
49943
|
+
|
|
49944
|
+
`,
|
|
49945
|
+
`event: response.output_text.done
|
|
49946
|
+
data: ${JSON.stringify({ type: "response.output_text.done", item_id: itemId, output_index: outputIndex, text })}
|
|
49947
|
+
|
|
49948
|
+
`,
|
|
49949
|
+
`event: response.content_part.done
|
|
49950
|
+
data: ${JSON.stringify({ type: "response.content_part.done", item_id: itemId, output_index: outputIndex, part: { type: "output_text", text } })}
|
|
49951
|
+
|
|
49952
|
+
`,
|
|
49953
|
+
`event: response.output_item.done
|
|
49954
|
+
data: ${JSON.stringify({ type: "response.output_item.done", output_index: outputIndex, item: doneItem })}
|
|
49955
|
+
|
|
49956
|
+
`
|
|
49957
|
+
].join(""),
|
|
49958
|
+
"utf8"
|
|
49959
|
+
);
|
|
49960
|
+
}
|
|
49961
|
+
function buildFunctionCallEvents2(fc, itemId, outputIndex) {
|
|
49962
|
+
return Buffer.from(
|
|
49963
|
+
[
|
|
49964
|
+
`event: response.output_item.added
|
|
49965
|
+
data: ${JSON.stringify({
|
|
49966
|
+
type: "response.output_item.added",
|
|
49967
|
+
output_index: outputIndex,
|
|
49968
|
+
item: { type: "function_call", id: itemId, call_id: fc.callId, name: fc.name, arguments: "" }
|
|
49969
|
+
})}
|
|
49970
|
+
|
|
49971
|
+
`,
|
|
49972
|
+
`event: response.function_call_arguments.delta
|
|
49973
|
+
data: ${JSON.stringify({
|
|
49974
|
+
type: "response.function_call_arguments.delta",
|
|
49975
|
+
item_id: itemId,
|
|
49976
|
+
delta: fc.arguments
|
|
49977
|
+
})}
|
|
49978
|
+
|
|
49979
|
+
`,
|
|
49980
|
+
`event: response.function_call_arguments.done
|
|
49981
|
+
data: ${JSON.stringify({
|
|
49982
|
+
type: "response.function_call_arguments.done",
|
|
49983
|
+
item_id: itemId,
|
|
49984
|
+
arguments: fc.arguments
|
|
49985
|
+
})}
|
|
49986
|
+
|
|
49987
|
+
`,
|
|
49988
|
+
`event: response.output_item.done
|
|
49989
|
+
data: ${JSON.stringify({
|
|
49990
|
+
type: "response.output_item.done",
|
|
49991
|
+
output_index: outputIndex,
|
|
49992
|
+
item: { type: "function_call", id: itemId, call_id: fc.callId, name: fc.name, arguments: fc.arguments }
|
|
49993
|
+
})}
|
|
49994
|
+
|
|
49995
|
+
`
|
|
49996
|
+
].join(""),
|
|
49997
|
+
"utf8"
|
|
49998
|
+
);
|
|
49999
|
+
}
|
|
50000
|
+
function buildCompleted2(responseObj) {
|
|
50001
|
+
const resp = responseObj ?? { id: `resp-proxy-${Date.now()}`, status: "completed", output: [] };
|
|
50002
|
+
return Buffer.from(
|
|
50003
|
+
`event: response.completed
|
|
50004
|
+
data: ${JSON.stringify({ type: "response.completed", response: resp })}
|
|
50005
|
+
|
|
50006
|
+
`,
|
|
50007
|
+
"utf8"
|
|
50008
|
+
);
|
|
50009
|
+
}
|
|
50010
|
+
function createResponsesAdapter(textProtocol, projection) {
|
|
50011
|
+
const suppressTextLifecycle = !!textProtocol;
|
|
50012
|
+
let outputIndex = 0;
|
|
50013
|
+
let responseObj = null;
|
|
50014
|
+
let terminalRaw = null;
|
|
50015
|
+
let terminalKind = null;
|
|
50016
|
+
return {
|
|
50017
|
+
buildRequest(coreMessages, systemPrompt, requestBody) {
|
|
50018
|
+
const customToolCallIds = /* @__PURE__ */ new Set();
|
|
50019
|
+
for (const m2 of coreMessages) {
|
|
50020
|
+
const bm = m2;
|
|
50021
|
+
const raw = bm?.rawResponsesItem;
|
|
50022
|
+
if (raw && (raw.type === "custom_tool_call" || raw.type === "custom_tool_call_output")) {
|
|
50023
|
+
const id = typeof raw.call_id === "string" ? raw.call_id : typeof raw.id === "string" ? raw.id : "";
|
|
50024
|
+
if (id) customToolCallIds.add(id);
|
|
50025
|
+
}
|
|
50026
|
+
}
|
|
50027
|
+
let inputItems;
|
|
50028
|
+
if (projection) {
|
|
50029
|
+
const rebuiltInput = patchResponsesInput(projection, coreMessages);
|
|
50030
|
+
inputItems = typeof rebuiltInput === "string" ? [{ type: "message", role: "user", content: rebuiltInput }] : rebuiltInput;
|
|
50031
|
+
} else {
|
|
50032
|
+
inputItems = coreToResponses(coreMessages, customToolCallIds);
|
|
50033
|
+
}
|
|
50034
|
+
const devParts = projection && projection.systemParts.length > 0 ? [...projection.systemParts, systemPrompt] : [systemPrompt];
|
|
50035
|
+
const withDev = injectResponsesDeveloperMessage(inputItems, devParts.join("\n\n---\n\n"));
|
|
50036
|
+
const rebuilt = { ...requestBody, input: withDev };
|
|
50037
|
+
delete rebuilt.previous_response_id;
|
|
50038
|
+
delete rebuilt.instructions;
|
|
50039
|
+
return rebuilt;
|
|
50040
|
+
},
|
|
50041
|
+
async *parseStream(upstream, round) {
|
|
50042
|
+
const pending = /* @__PURE__ */ new Map();
|
|
50043
|
+
for await (const eventStr of iterSseEvents(upstream)) {
|
|
50044
|
+
const type = extractEventType2(eventStr);
|
|
50045
|
+
const dataLine = extractDataLine2(eventStr);
|
|
50046
|
+
if (!type || !dataLine) continue;
|
|
50047
|
+
let obj;
|
|
50048
|
+
try {
|
|
50049
|
+
obj = JSON.parse(dataLine);
|
|
50050
|
+
} catch {
|
|
50051
|
+
continue;
|
|
50052
|
+
}
|
|
50053
|
+
const rawBuf = Buffer.from(eventStr + "\n\n", "utf8");
|
|
50054
|
+
if (round === 1 && typeof obj.output_index === "number") {
|
|
50055
|
+
outputIndex = Math.max(outputIndex, obj.output_index + 1);
|
|
50056
|
+
}
|
|
50057
|
+
if (type === "response.created" || type === "response.in_progress") {
|
|
50058
|
+
yield { kind: "meta", chunk: rawBuf, firstRoundOnly: true };
|
|
50059
|
+
} else if (type === "response.output_item.added") {
|
|
50060
|
+
const item = obj.item;
|
|
50061
|
+
if (item?.type === "function_call") {
|
|
50062
|
+
const itemId = typeof item.id === "string" ? item.id : "";
|
|
50063
|
+
pending.set(itemId, {
|
|
50064
|
+
itemId,
|
|
50065
|
+
callId: typeof item.call_id === "string" ? item.call_id : "",
|
|
50066
|
+
name: typeof item.name === "string" ? item.name : "",
|
|
50067
|
+
arguments: ""
|
|
50068
|
+
});
|
|
50069
|
+
} else if (item?.type === "custom_tool_call") {
|
|
50070
|
+
yield { kind: "meta", chunk: rawBuf, firstRoundOnly: false };
|
|
50071
|
+
} else if (!suppressTextLifecycle) {
|
|
50072
|
+
yield { kind: "meta", chunk: rawBuf, firstRoundOnly: true };
|
|
50073
|
+
}
|
|
50074
|
+
} else if (type === "response.content_part.added" || type === "response.content_part.done" || type === "response.output_text.done") {
|
|
50075
|
+
if (!suppressTextLifecycle) {
|
|
50076
|
+
yield { kind: "meta", chunk: rawBuf, firstRoundOnly: true };
|
|
50077
|
+
}
|
|
50078
|
+
} else if (type === "response.output_text.delta") {
|
|
50079
|
+
const delta = typeof obj.delta === "string" ? obj.delta : "";
|
|
50080
|
+
if (delta.length > 0) {
|
|
50081
|
+
yield { kind: "text", delta, raw: rawBuf };
|
|
50082
|
+
}
|
|
50083
|
+
} else if (type === "response.function_call_arguments.delta") {
|
|
50084
|
+
const itemId = typeof obj.item_id === "string" ? obj.item_id : "";
|
|
50085
|
+
const delta = typeof obj.delta === "string" ? obj.delta : "";
|
|
50086
|
+
const fc = pending.get(itemId);
|
|
50087
|
+
if (fc) fc.arguments += delta;
|
|
50088
|
+
} else if (type === "response.function_call_arguments.done") {
|
|
50089
|
+
const itemId = typeof obj.item_id === "string" ? obj.item_id : "";
|
|
50090
|
+
const args = typeof obj.arguments === "string" ? obj.arguments : "";
|
|
50091
|
+
const fc = pending.get(itemId);
|
|
50092
|
+
if (fc && args) fc.arguments = args;
|
|
50093
|
+
} else if (type === "response.output_item.done") {
|
|
50094
|
+
const item = obj.item;
|
|
50095
|
+
if (item?.type === "function_call") {
|
|
50096
|
+
const itemId = typeof item.id === "string" ? item.id : "";
|
|
50097
|
+
const fc = pending.get(itemId);
|
|
50098
|
+
if (fc) {
|
|
50099
|
+
if (typeof item.arguments === "string" && item.arguments) fc.arguments = item.arguments;
|
|
50100
|
+
pending.delete(itemId);
|
|
50101
|
+
yield {
|
|
50102
|
+
kind: "tool_call",
|
|
50103
|
+
name: fc.name,
|
|
50104
|
+
callId: fc.callId,
|
|
50105
|
+
arguments: fc.arguments
|
|
50106
|
+
};
|
|
50107
|
+
}
|
|
50108
|
+
} else if (item?.type === "custom_tool_call") {
|
|
50109
|
+
yield { kind: "meta", chunk: rawBuf, firstRoundOnly: false };
|
|
50110
|
+
} else if (!suppressTextLifecycle) {
|
|
50111
|
+
yield { kind: "meta", chunk: rawBuf, firstRoundOnly: true };
|
|
50112
|
+
}
|
|
50113
|
+
} else if (type === "response.completed") {
|
|
50114
|
+
responseObj = obj.response ?? null;
|
|
50115
|
+
terminalKind = "completed";
|
|
50116
|
+
terminalRaw = null;
|
|
50117
|
+
const respUsage = responseObj?.usage;
|
|
50118
|
+
const pd = respUsage?.input_tokens_details;
|
|
50119
|
+
yield {
|
|
50120
|
+
kind: "usage",
|
|
50121
|
+
inputTokens: typeof respUsage?.input_tokens === "number" ? respUsage.input_tokens : void 0,
|
|
50122
|
+
outputTokens: typeof respUsage?.output_tokens === "number" ? respUsage.output_tokens : void 0,
|
|
50123
|
+
cachedTokens: typeof pd?.cached_tokens === "number" ? pd.cached_tokens : void 0
|
|
50124
|
+
};
|
|
50125
|
+
yield { kind: "done", finishReason: "completed" };
|
|
50126
|
+
} else if (type === "response.incomplete") {
|
|
50127
|
+
terminalKind = "incomplete";
|
|
50128
|
+
terminalRaw = rawBuf;
|
|
50129
|
+
yield { kind: "done", finishReason: "incomplete" };
|
|
50130
|
+
} else if (type === "response.failed" || type === "response.error") {
|
|
50131
|
+
terminalKind = "failed";
|
|
50132
|
+
terminalRaw = rawBuf;
|
|
50133
|
+
yield { kind: "done", finishReason: "failed" };
|
|
50134
|
+
} else {
|
|
50135
|
+
yield { kind: "meta", chunk: rawBuf, firstRoundOnly: true };
|
|
50136
|
+
}
|
|
50137
|
+
}
|
|
50138
|
+
if (!terminalKind) {
|
|
50139
|
+
yield { kind: "done", finishReason: "failed" };
|
|
50140
|
+
}
|
|
50141
|
+
},
|
|
50142
|
+
emitText(delta) {
|
|
50143
|
+
return buildMessageItemSequence2(`msg-proxy-${Date.now()}-${outputIndex}`, outputIndex++, delta);
|
|
50144
|
+
},
|
|
50145
|
+
emitToolCall(call) {
|
|
50146
|
+
const buf = buildFunctionCallEvents2(call, `fc-proxy-${Date.now()}-${outputIndex}`, outputIndex);
|
|
50147
|
+
outputIndex += 1;
|
|
50148
|
+
return buf;
|
|
50149
|
+
},
|
|
50150
|
+
emitMarker(toolName, result) {
|
|
50151
|
+
return buildMessageItemSequence2(
|
|
50152
|
+
`marker-${Date.now()}-${outputIndex}`,
|
|
50153
|
+
outputIndex++,
|
|
50154
|
+
buildVisibilityMarker(toolName, result)
|
|
50155
|
+
);
|
|
50156
|
+
},
|
|
50157
|
+
emitCompletion(opts) {
|
|
50158
|
+
if (terminalRaw && (terminalKind === "failed" || terminalKind === "incomplete")) {
|
|
50159
|
+
return terminalRaw;
|
|
50160
|
+
}
|
|
50161
|
+
if (!responseObj && opts?.finishReason === "failed") {
|
|
50162
|
+
const failed = {
|
|
50163
|
+
id: `resp-error-${Date.now()}`,
|
|
50164
|
+
status: "failed",
|
|
50165
|
+
error: { code: "server_error", message: "upstream returned no response" }
|
|
50166
|
+
};
|
|
50167
|
+
return Buffer.from(
|
|
50168
|
+
`event: response.failed
|
|
50169
|
+
data: ${JSON.stringify({ type: "response.failed", response: failed })}
|
|
50170
|
+
|
|
50171
|
+
`,
|
|
50172
|
+
"utf8"
|
|
50173
|
+
);
|
|
50174
|
+
}
|
|
50175
|
+
let resp = responseObj ? { ...responseObj } : { id: `resp-proxy-${Date.now()}`, status: "completed", output: [] };
|
|
50176
|
+
if (opts?.usage) {
|
|
50177
|
+
const usage = {
|
|
50178
|
+
...typeof resp.usage === "object" ? resp.usage : {}
|
|
50179
|
+
};
|
|
50180
|
+
if (typeof opts.usage.inputTokens === "number") usage.input_tokens = opts.usage.inputTokens;
|
|
50181
|
+
if (typeof opts.usage.outputTokens === "number") usage.output_tokens = opts.usage.outputTokens;
|
|
50182
|
+
if (typeof opts.usage.cachedTokens === "number") {
|
|
50183
|
+
usage.input_tokens_details = {
|
|
50184
|
+
...usage.input_tokens_details ?? {},
|
|
50185
|
+
cached_tokens: opts.usage.cachedTokens
|
|
50186
|
+
};
|
|
50187
|
+
}
|
|
50188
|
+
resp = { ...resp, usage };
|
|
50189
|
+
}
|
|
50190
|
+
return buildCompleted2(resp);
|
|
50191
|
+
},
|
|
50192
|
+
emitError(message) {
|
|
50193
|
+
const resp = {
|
|
50194
|
+
id: `resp-error-${Date.now()}`,
|
|
50195
|
+
status: "failed",
|
|
50196
|
+
error: { code: "server_error", message }
|
|
50197
|
+
};
|
|
50198
|
+
return Buffer.from(
|
|
50199
|
+
`event: response.failed
|
|
50200
|
+
data: ${JSON.stringify({ type: "response.failed", response: resp })}
|
|
50201
|
+
|
|
50202
|
+
`,
|
|
50203
|
+
"utf8"
|
|
50204
|
+
);
|
|
50205
|
+
},
|
|
50206
|
+
extractTextTriggers(text) {
|
|
50207
|
+
const calls = [];
|
|
50208
|
+
let clean = text;
|
|
50209
|
+
let hadTrigger = false;
|
|
50210
|
+
const triggers = [
|
|
50211
|
+
{ name: "compress", open: ACP_TEXT_OPEN, close: ACP_TEXT_CLOSE, requirePayload: true },
|
|
50212
|
+
{ name: "acp_status", open: ACP_STATUS_OPEN, close: ACP_STATUS_CLOSE, requirePayload: false },
|
|
50213
|
+
{ name: "search_context", open: ACP_SEARCH_OPEN, close: ACP_SEARCH_CLOSE, requirePayload: true },
|
|
50214
|
+
{ name: "decompress", open: ACP_DECOMPRESS_OPEN, close: ACP_DECOMPRESS_CLOSE, requirePayload: true }
|
|
50215
|
+
];
|
|
50216
|
+
for (const t of triggers) {
|
|
50217
|
+
let start = clean.indexOf(t.open);
|
|
50218
|
+
while (start >= 0) {
|
|
50219
|
+
const end = clean.indexOf(t.close, start + t.open.length);
|
|
50220
|
+
if (end < 0) break;
|
|
50221
|
+
hadTrigger = true;
|
|
50222
|
+
const payload = clean.slice(start + t.open.length, end).trim();
|
|
50223
|
+
if (payload.length > 0 || !t.requirePayload) {
|
|
50224
|
+
const stamp = `${Date.now()}-${calls.length}`;
|
|
50225
|
+
calls.push({
|
|
50226
|
+
name: t.name,
|
|
50227
|
+
callId: `call_text_${stamp}`,
|
|
50228
|
+
arguments: payload.length > 0 ? payload : "{}"
|
|
50229
|
+
});
|
|
50230
|
+
}
|
|
50231
|
+
clean = clean.slice(0, start) + clean.slice(end + t.close.length);
|
|
50232
|
+
start = clean.indexOf(t.open);
|
|
50233
|
+
}
|
|
50234
|
+
}
|
|
50235
|
+
return { clean: hadTrigger ? clean : text, calls };
|
|
50236
|
+
}
|
|
50237
|
+
};
|
|
50238
|
+
}
|
|
50239
|
+
|
|
50240
|
+
// src/loop/adapter-openai.ts
|
|
50241
|
+
async function* iterSseChunks(stream2) {
|
|
50242
|
+
const reader = stream2.getReader();
|
|
50243
|
+
let buf = "";
|
|
50244
|
+
try {
|
|
50245
|
+
while (true) {
|
|
50246
|
+
let done;
|
|
50247
|
+
let value;
|
|
50248
|
+
try {
|
|
50249
|
+
({ done, value } = await reader.read());
|
|
50250
|
+
} catch {
|
|
50251
|
+
break;
|
|
50252
|
+
}
|
|
50253
|
+
if (done) break;
|
|
50254
|
+
buf += new TextDecoder().decode(value, { stream: true });
|
|
50255
|
+
buf = buf.replace(/\r\n|\r/g, "\n");
|
|
50256
|
+
let idx;
|
|
50257
|
+
while ((idx = buf.indexOf("\n\n")) >= 0) {
|
|
50258
|
+
const raw = buf.slice(0, idx);
|
|
50259
|
+
buf = buf.slice(idx + 2);
|
|
50260
|
+
if (raw.trim().length > 0) yield raw;
|
|
50261
|
+
}
|
|
50262
|
+
}
|
|
50263
|
+
if (buf.trim().length > 0) yield buf;
|
|
50264
|
+
} finally {
|
|
50265
|
+
reader.releaseLock();
|
|
50266
|
+
}
|
|
50267
|
+
}
|
|
50268
|
+
function createOpenaiAdapter(requestBody) {
|
|
50269
|
+
const model = requestBody.model ?? "unknown";
|
|
50270
|
+
let responseId = `chatcmpl-proxy-${Date.now()}`;
|
|
50271
|
+
let toolIndex = 0;
|
|
50272
|
+
const makeBase = () => ({
|
|
50273
|
+
id: responseId,
|
|
50274
|
+
object: "chat.completion.chunk",
|
|
50275
|
+
created: Date.now(),
|
|
50276
|
+
model
|
|
50277
|
+
});
|
|
50278
|
+
const buildContent = (content) => Buffer.from(
|
|
50279
|
+
`data: ${JSON.stringify({
|
|
50280
|
+
id: responseId,
|
|
50281
|
+
object: "chat.completion.chunk",
|
|
50282
|
+
created: Date.now(),
|
|
50283
|
+
model,
|
|
50284
|
+
choices: [{ index: 0, delta: { content }, finish_reason: null }]
|
|
50285
|
+
})}
|
|
50286
|
+
|
|
50287
|
+
`,
|
|
50288
|
+
"utf8"
|
|
50289
|
+
);
|
|
50290
|
+
const buildToolCall = (call) => {
|
|
50291
|
+
const idx = toolIndex++;
|
|
50292
|
+
return Buffer.from(
|
|
50293
|
+
`data: ${JSON.stringify({
|
|
50294
|
+
...makeBase(),
|
|
50295
|
+
choices: [{
|
|
50296
|
+
index: 0,
|
|
50297
|
+
delta: {
|
|
50298
|
+
tool_calls: [{
|
|
50299
|
+
index: idx,
|
|
50300
|
+
id: call.callId,
|
|
50301
|
+
type: "function",
|
|
50302
|
+
function: { name: call.name, arguments: call.arguments }
|
|
50303
|
+
}]
|
|
50304
|
+
},
|
|
50305
|
+
finish_reason: null
|
|
50306
|
+
}]
|
|
50307
|
+
})}
|
|
50308
|
+
|
|
50309
|
+
`,
|
|
50310
|
+
"utf8"
|
|
50311
|
+
);
|
|
50312
|
+
};
|
|
50313
|
+
const buildFinish = (finishReason, usage) => Buffer.from(
|
|
50314
|
+
`data: ${JSON.stringify({
|
|
50315
|
+
...makeBase(),
|
|
50316
|
+
choices: [{ index: 0, delta: {}, finish_reason: finishReason }],
|
|
50317
|
+
...usage ? { usage } : {}
|
|
50318
|
+
})}
|
|
50319
|
+
|
|
50320
|
+
`,
|
|
50321
|
+
"utf8"
|
|
50322
|
+
);
|
|
50323
|
+
return {
|
|
50324
|
+
buildRequest(coreMessages, systemPrompt, body) {
|
|
50325
|
+
const messages = coreToOpenai(coreMessages);
|
|
50326
|
+
const withSys = injectOpenaiSystem(messages, [systemPrompt]);
|
|
50327
|
+
return { ...body, messages: withSys };
|
|
50328
|
+
},
|
|
50329
|
+
async *parseStream(upstream, _round) {
|
|
50330
|
+
const pending = /* @__PURE__ */ new Map();
|
|
50331
|
+
for await (const eventStr of iterSseChunks(upstream)) {
|
|
50332
|
+
const dataLine = eventStr.split("\n").find((l) => l.startsWith("data:"));
|
|
50333
|
+
if (!dataLine) continue;
|
|
50334
|
+
const jsonStr = dataLine.slice(5).trim();
|
|
50335
|
+
if (jsonStr === "[DONE]") {
|
|
50336
|
+
for (const [, tc] of pending) {
|
|
50337
|
+
if (tc.name.length > 0 || tc.id.length > 0) {
|
|
50338
|
+
yield {
|
|
50339
|
+
kind: "tool_call",
|
|
50340
|
+
name: tc.name,
|
|
50341
|
+
callId: tc.id,
|
|
50342
|
+
arguments: tc.arguments
|
|
50343
|
+
};
|
|
50344
|
+
}
|
|
50345
|
+
}
|
|
50346
|
+
pending.clear();
|
|
50347
|
+
yield { kind: "done", finishReason: "stop" };
|
|
50348
|
+
continue;
|
|
50349
|
+
}
|
|
50350
|
+
let parsed;
|
|
50351
|
+
try {
|
|
50352
|
+
parsed = JSON.parse(jsonStr);
|
|
50353
|
+
} catch {
|
|
50354
|
+
continue;
|
|
50355
|
+
}
|
|
50356
|
+
const rawBuf = Buffer.from(eventStr + "\n\n", "utf8");
|
|
50357
|
+
const choices = parsed.choices;
|
|
50358
|
+
const choice = choices?.[0];
|
|
50359
|
+
if (!choice) {
|
|
50360
|
+
if (parsed.usage) {
|
|
50361
|
+
const u2 = parsed.usage;
|
|
50362
|
+
const pd = u2.prompt_tokens_details;
|
|
50363
|
+
yield {
|
|
50364
|
+
kind: "usage",
|
|
50365
|
+
inputTokens: typeof u2.prompt_tokens === "number" ? u2.prompt_tokens : void 0,
|
|
50366
|
+
outputTokens: typeof u2.completion_tokens === "number" ? u2.completion_tokens : void 0,
|
|
50367
|
+
cachedTokens: typeof pd?.cached_tokens === "number" ? pd.cached_tokens : void 0
|
|
50368
|
+
};
|
|
50369
|
+
}
|
|
50370
|
+
continue;
|
|
50371
|
+
}
|
|
50372
|
+
const delta = choice.delta;
|
|
50373
|
+
const finishReason = typeof choice.finish_reason === "string" ? choice.finish_reason : void 0;
|
|
50374
|
+
if (finishReason) {
|
|
50375
|
+
for (const [, tc] of pending) {
|
|
50376
|
+
if (tc.name.length > 0 || tc.id.length > 0) {
|
|
50377
|
+
yield {
|
|
50378
|
+
kind: "tool_call",
|
|
50379
|
+
name: tc.name,
|
|
50380
|
+
callId: tc.id,
|
|
50381
|
+
arguments: tc.arguments
|
|
50382
|
+
};
|
|
50383
|
+
}
|
|
50384
|
+
}
|
|
50385
|
+
pending.clear();
|
|
50386
|
+
const u2 = parsed.usage;
|
|
50387
|
+
const pd = u2?.prompt_tokens_details;
|
|
50388
|
+
yield {
|
|
50389
|
+
kind: "usage",
|
|
50390
|
+
inputTokens: typeof u2?.prompt_tokens === "number" ? u2.prompt_tokens : void 0,
|
|
50391
|
+
outputTokens: typeof u2?.completion_tokens === "number" ? u2.completion_tokens : void 0,
|
|
50392
|
+
cachedTokens: typeof pd?.cached_tokens === "number" ? pd.cached_tokens : void 0
|
|
50393
|
+
};
|
|
50394
|
+
yield { kind: "done", finishReason };
|
|
50395
|
+
}
|
|
50396
|
+
if (!delta) continue;
|
|
50397
|
+
if (delta.tool_calls) {
|
|
50398
|
+
const tcs = delta.tool_calls;
|
|
50399
|
+
for (const tc of tcs) {
|
|
50400
|
+
const idx = typeof tc.index === "number" ? tc.index : 0;
|
|
50401
|
+
const fn = tc.function;
|
|
50402
|
+
const name = typeof fn?.name === "string" ? fn.name : "";
|
|
50403
|
+
const id = typeof tc.id === "string" ? tc.id : "";
|
|
50404
|
+
const args = typeof fn?.arguments === "string" ? fn.arguments : "";
|
|
50405
|
+
let buf = pending.get(idx);
|
|
50406
|
+
if (!buf) {
|
|
50407
|
+
buf = { index: idx, id, name, arguments: args };
|
|
50408
|
+
pending.set(idx, buf);
|
|
50409
|
+
} else {
|
|
50410
|
+
if (id) buf.id = id;
|
|
50411
|
+
if (name) buf.name = name;
|
|
50412
|
+
buf.arguments += args;
|
|
50413
|
+
}
|
|
50414
|
+
}
|
|
50415
|
+
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
50416
|
+
yield { kind: "text", delta: delta.content };
|
|
50417
|
+
}
|
|
50418
|
+
continue;
|
|
50419
|
+
}
|
|
50420
|
+
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
50421
|
+
yield { kind: "text", delta: delta.content, raw: rawBuf };
|
|
50422
|
+
} else if (delta.role || Object.keys(delta).length === 0 && !finishReason) {
|
|
50423
|
+
yield { kind: "meta", chunk: rawBuf, firstRoundOnly: true };
|
|
50424
|
+
}
|
|
50425
|
+
}
|
|
50426
|
+
},
|
|
50427
|
+
emitText(delta) {
|
|
50428
|
+
return buildContent(delta);
|
|
50429
|
+
},
|
|
50430
|
+
emitToolCall(call) {
|
|
50431
|
+
return buildToolCall(call);
|
|
50432
|
+
},
|
|
50433
|
+
emitMarker(toolName, result) {
|
|
50434
|
+
return buildContent(buildVisibilityMarker(toolName, result));
|
|
50435
|
+
},
|
|
50436
|
+
emitCompletion(opts) {
|
|
50437
|
+
const finishReason = opts?.finishReason ?? "stop";
|
|
50438
|
+
const usage = opts?.usage ? {
|
|
50439
|
+
prompt_tokens: opts.usage.inputTokens,
|
|
50440
|
+
completion_tokens: opts.usage.outputTokens,
|
|
50441
|
+
...typeof opts.usage.cachedTokens === "number" ? { prompt_tokens_details: { cached_tokens: opts.usage.cachedTokens } } : {}
|
|
50442
|
+
} : null;
|
|
50443
|
+
return Buffer.concat([buildFinish(finishReason, usage), Buffer.from("data: [DONE]\n\n", "utf8")]);
|
|
50444
|
+
},
|
|
50445
|
+
emitError(message) {
|
|
50446
|
+
return Buffer.concat([
|
|
50447
|
+
buildContent(`
|
|
50448
|
+
[acp-proxy: ${message}]
|
|
50449
|
+
`),
|
|
50450
|
+
buildFinish("stop", null),
|
|
50451
|
+
Buffer.from("data: [DONE]\n\n", "utf8")
|
|
50452
|
+
]);
|
|
50453
|
+
}
|
|
50454
|
+
};
|
|
50455
|
+
}
|
|
50456
|
+
|
|
50457
|
+
// src/loop/adapter-anthropic.ts
|
|
50458
|
+
async function* iterSseEvents2(stream2) {
|
|
50459
|
+
const reader = stream2.getReader();
|
|
50460
|
+
let buf = "";
|
|
50461
|
+
try {
|
|
50462
|
+
while (true) {
|
|
50463
|
+
let done;
|
|
50464
|
+
let value;
|
|
50465
|
+
try {
|
|
50466
|
+
({ done, value } = await reader.read());
|
|
50467
|
+
} catch {
|
|
50468
|
+
break;
|
|
50469
|
+
}
|
|
50470
|
+
if (done) break;
|
|
50471
|
+
buf += new TextDecoder().decode(value, { stream: true });
|
|
50472
|
+
buf = buf.replace(/\r\n|\r/g, "\n");
|
|
50473
|
+
let idx;
|
|
50474
|
+
while ((idx = buf.indexOf("\n\n")) >= 0) {
|
|
50475
|
+
const raw = buf.slice(0, idx);
|
|
50476
|
+
buf = buf.slice(idx + 2);
|
|
50477
|
+
if (raw.trim().length > 0) yield raw;
|
|
50478
|
+
}
|
|
50479
|
+
}
|
|
50480
|
+
if (buf.trim().length > 0) yield buf;
|
|
50481
|
+
} finally {
|
|
50482
|
+
reader.releaseLock();
|
|
50483
|
+
}
|
|
50484
|
+
}
|
|
50485
|
+
function parseAnthropicSse2(eventStr) {
|
|
50486
|
+
const lines = eventStr.split("\n");
|
|
50487
|
+
let type = "";
|
|
50488
|
+
const dataLines = [];
|
|
50489
|
+
for (const l of lines) {
|
|
50490
|
+
if (l.startsWith("event:")) type = l.slice(6).trim();
|
|
50491
|
+
else if (l.startsWith("data:")) dataLines.push(l.slice(5).replace(/^ /, ""));
|
|
50492
|
+
}
|
|
50493
|
+
if (!type) return null;
|
|
50494
|
+
const jsonStr = dataLines.join("\n").trim();
|
|
50495
|
+
if (!jsonStr) return { type, data: {} };
|
|
50496
|
+
try {
|
|
50497
|
+
return { type, data: JSON.parse(jsonStr) };
|
|
50498
|
+
} catch {
|
|
50499
|
+
return { type, data: {} };
|
|
50500
|
+
}
|
|
50501
|
+
}
|
|
50502
|
+
function remapIndexInEvent(eventStr, newIndex) {
|
|
50503
|
+
const lines = eventStr.split("\n");
|
|
50504
|
+
const rebuilt = [];
|
|
50505
|
+
let touched = false;
|
|
50506
|
+
for (const l of lines) {
|
|
50507
|
+
if (!touched && l.startsWith("data:")) {
|
|
50508
|
+
const jsonStr = l.slice(5).replace(/^ /, "");
|
|
50509
|
+
try {
|
|
50510
|
+
const obj = JSON.parse(jsonStr);
|
|
50511
|
+
if (typeof obj === "object" && obj !== null && typeof obj.index === "number") {
|
|
50512
|
+
obj.index = newIndex;
|
|
50513
|
+
rebuilt.push(`data: ${JSON.stringify(obj)}`);
|
|
50514
|
+
touched = true;
|
|
50515
|
+
continue;
|
|
50516
|
+
}
|
|
50517
|
+
} catch {
|
|
50518
|
+
}
|
|
50519
|
+
}
|
|
50520
|
+
rebuilt.push(l);
|
|
50521
|
+
}
|
|
50522
|
+
return Buffer.from(rebuilt.join("\n") + "\n\n", "utf8");
|
|
50523
|
+
}
|
|
50524
|
+
function createAnthropicAdapter(requestBody, originalSystem) {
|
|
50525
|
+
const model = requestBody.model ?? void 0;
|
|
50526
|
+
let messageId;
|
|
50527
|
+
let clientIndex = 0;
|
|
50528
|
+
const buildTextBlock = (index, text) => Buffer.from(
|
|
50529
|
+
`event: content_block_start
|
|
50530
|
+
data: ${JSON.stringify({ type: "content_block_start", index, content_block: { type: "text", text: "" } })}
|
|
50531
|
+
|
|
50532
|
+
event: content_block_delta
|
|
50533
|
+
data: ${JSON.stringify({ type: "content_block_delta", index, delta: { type: "text_delta", text } })}
|
|
50534
|
+
|
|
50535
|
+
event: content_block_stop
|
|
50536
|
+
data: ${JSON.stringify({ type: "content_block_stop", index })}
|
|
50537
|
+
|
|
50538
|
+
`,
|
|
50539
|
+
"utf8"
|
|
50540
|
+
);
|
|
50541
|
+
const buildToolUseBlock = (index, call) => Buffer.from(
|
|
50542
|
+
`event: content_block_start
|
|
50543
|
+
data: ${JSON.stringify({ type: "content_block_start", index, content_block: { type: "tool_use", id: call.callId, name: call.name, input: {} } })}
|
|
50544
|
+
|
|
50545
|
+
event: content_block_delta
|
|
50546
|
+
data: ${JSON.stringify({ type: "content_block_delta", index, delta: { type: "input_json_delta", partial_json: call.arguments } })}
|
|
50547
|
+
|
|
50548
|
+
event: content_block_stop
|
|
50549
|
+
data: ${JSON.stringify({ type: "content_block_stop", index })}
|
|
50550
|
+
|
|
50551
|
+
`,
|
|
50552
|
+
"utf8"
|
|
50553
|
+
);
|
|
50554
|
+
const buildTerminal = (stopReason, outputTokens, inputTokens, cachedTokens) => {
|
|
50555
|
+
const usage = {
|
|
50556
|
+
input_tokens: inputTokens,
|
|
50557
|
+
output_tokens: outputTokens,
|
|
50558
|
+
cache_read_input_tokens: cachedTokens
|
|
50559
|
+
};
|
|
50560
|
+
const extra = {};
|
|
50561
|
+
if (messageId) extra.id = messageId;
|
|
50562
|
+
if (model) extra.model = model;
|
|
50563
|
+
return Buffer.from(
|
|
50564
|
+
`event: message_delta
|
|
50565
|
+
data: ${JSON.stringify({ type: "message_delta", delta: { stop_reason: stopReason, stop_sequence: null }, usage, ...extra })}
|
|
50566
|
+
|
|
50567
|
+
event: message_stop
|
|
50568
|
+
data: ${JSON.stringify({ type: "message_stop" })}
|
|
50569
|
+
|
|
50570
|
+
`,
|
|
50571
|
+
"utf8"
|
|
50572
|
+
);
|
|
50573
|
+
};
|
|
50574
|
+
return {
|
|
50575
|
+
buildRequest(coreMessages, systemPrompt, body) {
|
|
50576
|
+
const messages = coreToAnthropic(coreMessages);
|
|
50577
|
+
const baseText = originalSystem !== void 0 ? extractSystem(originalSystem) : "";
|
|
50578
|
+
const full = baseText ? `${baseText}
|
|
50579
|
+
|
|
50580
|
+
---
|
|
50581
|
+
|
|
50582
|
+
${systemPrompt}` : systemPrompt;
|
|
50583
|
+
const system = originalSystem !== void 0 ? buildSystem(full, originalSystem) : full;
|
|
50584
|
+
return { ...body, system, messages };
|
|
50585
|
+
},
|
|
50586
|
+
async *parseStream(upstream, round) {
|
|
50587
|
+
const pending = /* @__PURE__ */ new Map();
|
|
50588
|
+
let roundInput;
|
|
50589
|
+
let roundCached;
|
|
50590
|
+
let roundOutput;
|
|
50591
|
+
let stopReason;
|
|
50592
|
+
let usageYielded = false;
|
|
50593
|
+
const indexMap = /* @__PURE__ */ new Map();
|
|
50594
|
+
for await (const eventStr of iterSseEvents2(upstream)) {
|
|
50595
|
+
const parsed = parseAnthropicSse2(eventStr);
|
|
50596
|
+
if (!parsed) continue;
|
|
50597
|
+
const { type, data } = parsed;
|
|
50598
|
+
const rawBuf = Buffer.from(eventStr + "\n\n", "utf8");
|
|
50599
|
+
if (type === "message_start") {
|
|
50600
|
+
const msg2 = data.message ?? {};
|
|
50601
|
+
if (typeof msg2.id === "string" && !messageId) messageId = msg2.id;
|
|
50602
|
+
const u2 = msg2.usage ?? {};
|
|
50603
|
+
if (typeof u2.input_tokens === "number") roundInput = u2.input_tokens;
|
|
50604
|
+
if (typeof u2.cache_read_input_tokens === "number") roundCached = u2.cache_read_input_tokens;
|
|
50605
|
+
if (round === 1) {
|
|
50606
|
+
yield { kind: "meta", chunk: rawBuf, firstRoundOnly: true };
|
|
50607
|
+
}
|
|
50608
|
+
} else if (type === "ping") {
|
|
50609
|
+
yield { kind: "meta", chunk: rawBuf };
|
|
50610
|
+
} else if (type === "content_block_start") {
|
|
50611
|
+
const upstreamIndex = data.index ?? 0;
|
|
50612
|
+
const block = data.content_block ?? {};
|
|
50613
|
+
if (block.type === "tool_use") {
|
|
50614
|
+
const name = typeof block.name === "string" ? block.name : "";
|
|
50615
|
+
const id = typeof block.id === "string" ? block.id : `toolu_${upstreamIndex}`;
|
|
50616
|
+
pending.set(upstreamIndex, { id, name, json: "" });
|
|
50617
|
+
} else if (round === 1) {
|
|
50618
|
+
const ci2 = clientIndex++;
|
|
50619
|
+
indexMap.set(upstreamIndex, ci2);
|
|
50620
|
+
yield { kind: "meta", chunk: remapIndexInEvent(eventStr, ci2), firstRoundOnly: true };
|
|
50621
|
+
}
|
|
50622
|
+
} else if (type === "content_block_delta") {
|
|
50623
|
+
const upstreamIndex = data.index ?? 0;
|
|
50624
|
+
const delta = data.delta ?? {};
|
|
50625
|
+
if (pending.has(upstreamIndex)) {
|
|
50626
|
+
if (delta.type === "input_json_delta" && typeof delta.partial_json === "string") {
|
|
50627
|
+
pending.get(upstreamIndex).json += delta.partial_json;
|
|
50628
|
+
}
|
|
50629
|
+
} else if (delta.type === "text_delta" && typeof delta.text === "string") {
|
|
50630
|
+
if (round === 1) {
|
|
50631
|
+
const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
|
|
50632
|
+
yield { kind: "text", delta: delta.text, raw: remapIndexInEvent(eventStr, ci2) };
|
|
50633
|
+
} else {
|
|
50634
|
+
yield { kind: "text", delta: delta.text };
|
|
50635
|
+
}
|
|
50636
|
+
} else if (round === 1) {
|
|
50637
|
+
const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
|
|
50638
|
+
yield { kind: "meta", chunk: remapIndexInEvent(eventStr, ci2), firstRoundOnly: true };
|
|
50639
|
+
}
|
|
50640
|
+
} else if (type === "content_block_stop") {
|
|
50641
|
+
const upstreamIndex = data.index ?? 0;
|
|
50642
|
+
const tb = pending.get(upstreamIndex);
|
|
50643
|
+
if (tb) {
|
|
50644
|
+
pending.delete(upstreamIndex);
|
|
50645
|
+
yield {
|
|
50646
|
+
kind: "tool_call",
|
|
50647
|
+
name: tb.name,
|
|
50648
|
+
callId: tb.id,
|
|
50649
|
+
arguments: tb.json
|
|
50650
|
+
};
|
|
50651
|
+
} else if (round === 1) {
|
|
50652
|
+
const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
|
|
50653
|
+
yield { kind: "meta", chunk: remapIndexInEvent(eventStr, ci2), firstRoundOnly: true };
|
|
50654
|
+
}
|
|
50655
|
+
} else if (type === "message_delta") {
|
|
50656
|
+
const u2 = data.usage ?? {};
|
|
50657
|
+
if (typeof u2.output_tokens === "number") roundOutput = u2.output_tokens;
|
|
50658
|
+
if (typeof u2.input_tokens === "number") roundInput = u2.input_tokens;
|
|
50659
|
+
if (typeof u2.cache_read_input_tokens === "number") roundCached = u2.cache_read_input_tokens;
|
|
50660
|
+
const d = data.delta ?? {};
|
|
50661
|
+
if (typeof d.stop_reason === "string") stopReason = d.stop_reason;
|
|
50662
|
+
if (!usageYielded) {
|
|
50663
|
+
usageYielded = true;
|
|
50664
|
+
yield {
|
|
50665
|
+
kind: "usage",
|
|
50666
|
+
inputTokens: roundInput,
|
|
50667
|
+
outputTokens: roundOutput,
|
|
50668
|
+
cachedTokens: roundCached
|
|
50669
|
+
};
|
|
50670
|
+
}
|
|
50671
|
+
yield { kind: "done", finishReason: stopReason };
|
|
50672
|
+
} else if (type === "message_stop") {
|
|
50673
|
+
if (!usageYielded) {
|
|
50674
|
+
usageYielded = true;
|
|
50675
|
+
yield {
|
|
50676
|
+
kind: "usage",
|
|
50677
|
+
inputTokens: roundInput,
|
|
50678
|
+
outputTokens: roundOutput,
|
|
50679
|
+
cachedTokens: roundCached
|
|
50680
|
+
};
|
|
50681
|
+
}
|
|
50682
|
+
yield { kind: "done", finishReason: stopReason ?? "end_turn" };
|
|
50683
|
+
} else if (round === 1) {
|
|
50684
|
+
yield { kind: "meta", chunk: rawBuf, firstRoundOnly: true };
|
|
50685
|
+
}
|
|
50686
|
+
}
|
|
50687
|
+
},
|
|
50688
|
+
emitText(delta) {
|
|
50689
|
+
return buildTextBlock(clientIndex++, delta);
|
|
50690
|
+
},
|
|
50691
|
+
emitToolCall(call) {
|
|
50692
|
+
return buildToolUseBlock(clientIndex++, call);
|
|
50693
|
+
},
|
|
50694
|
+
emitMarker(toolName, result) {
|
|
50695
|
+
return buildTextBlock(clientIndex++, buildVisibilityMarker(toolName, result));
|
|
50696
|
+
},
|
|
50697
|
+
emitCompletion(opts) {
|
|
50698
|
+
const stopReason = opts?.finishReason ?? "end_turn";
|
|
50699
|
+
return buildTerminal(
|
|
50700
|
+
stopReason,
|
|
50701
|
+
opts?.usage?.outputTokens ?? 0,
|
|
50702
|
+
opts?.usage?.inputTokens ?? 0,
|
|
50703
|
+
opts?.usage?.cachedTokens ?? 0
|
|
50704
|
+
);
|
|
50705
|
+
},
|
|
50706
|
+
emitError(message) {
|
|
50707
|
+
const errBlock = buildTextBlock(clientIndex++, `
|
|
50708
|
+
[acp-proxy: ${message}]
|
|
50709
|
+
`);
|
|
50710
|
+
return Buffer.concat([errBlock, buildTerminal("end_turn", 0, 0, 0)]);
|
|
50711
|
+
}
|
|
50712
|
+
};
|
|
50713
|
+
}
|
|
50714
|
+
|
|
50715
|
+
// src/loop/index.ts
|
|
50716
|
+
function pickAdapter(protocol, requestBody, textProtocol, responsesProjection, anthropicSystem) {
|
|
50717
|
+
if (protocol === "responses") return createResponsesAdapter(textProtocol, responsesProjection);
|
|
50718
|
+
if (protocol === "openai") return createOpenaiAdapter(requestBody);
|
|
50719
|
+
if (protocol === "anthropic") return createAnthropicAdapter(requestBody, anthropicSystem);
|
|
50720
|
+
throw new Error(`[acp-loop] unknown protocol: ${protocol}`);
|
|
50721
|
+
}
|
|
50722
|
+
|
|
50051
50723
|
// src/stream-openai.ts
|
|
50052
50724
|
function rewriteOpenaiJsonResponse(body, ctx) {
|
|
50053
50725
|
if (!body || typeof body !== "object") return body;
|
|
@@ -50184,7 +50856,7 @@ function extractKey(headers) {
|
|
|
50184
50856
|
return "(no-key)";
|
|
50185
50857
|
}
|
|
50186
50858
|
function clientConversationHeader(headers) {
|
|
50187
|
-
const names = ["x-session-affinity", "x-acp-session", "x-session-id", "x-opencode-session", "session-id", "session_id"];
|
|
50859
|
+
const names = ["x-claude-code-session-id", "x-session-affinity", "x-acp-session", "x-session-id", "x-opencode-session", "session-id", "session_id"];
|
|
50188
50860
|
for (const name of names) {
|
|
50189
50861
|
const v2 = headers[name];
|
|
50190
50862
|
if (typeof v2 === "string" && v2.trim().length > 0) return v2.trim();
|
|
@@ -50206,7 +50878,7 @@ import tls3 from "tls";
|
|
|
50206
50878
|
// src/ca.ts
|
|
50207
50879
|
var import_node_forge = __toESM(require_lib(), 1);
|
|
50208
50880
|
import fs2 from "fs";
|
|
50209
|
-
import
|
|
50881
|
+
import path5 from "path";
|
|
50210
50882
|
import tls2 from "tls";
|
|
50211
50883
|
var ROOT_CERT_FILE = "root-ca.pem";
|
|
50212
50884
|
var ROOT_KEY_FILE = "root-ca-key.pem";
|
|
@@ -50245,8 +50917,8 @@ function ensureRootCA() {
|
|
|
50245
50917
|
if (rootCertPem && rootKeyPem) return;
|
|
50246
50918
|
const dir = caDir();
|
|
50247
50919
|
fs2.mkdirSync(dir, { recursive: true });
|
|
50248
|
-
const certPath =
|
|
50249
|
-
const keyPath =
|
|
50920
|
+
const certPath = path5.join(dir, ROOT_CERT_FILE);
|
|
50921
|
+
const keyPath = path5.join(dir, ROOT_KEY_FILE);
|
|
50250
50922
|
if (fs2.existsSync(certPath) && fs2.existsSync(keyPath)) {
|
|
50251
50923
|
rootCertPem = fs2.readFileSync(certPath, "utf8");
|
|
50252
50924
|
rootKeyPem = fs2.readFileSync(keyPath, "utf8");
|
|
@@ -50394,304 +51066,6 @@ function readMitmUpstream(socket) {
|
|
|
50394
51066
|
return socket[MITM_UPSTREAM_KEY];
|
|
50395
51067
|
}
|
|
50396
51068
|
|
|
50397
|
-
// src/codex-takeover.ts
|
|
50398
|
-
import {
|
|
50399
|
-
closeSync as closeSync2,
|
|
50400
|
-
existsSync as existsSync7,
|
|
50401
|
-
fsyncSync as fsyncSync2,
|
|
50402
|
-
mkdirSync as mkdirSync7,
|
|
50403
|
-
openSync as openSync2,
|
|
50404
|
-
readFileSync as readFileSync7,
|
|
50405
|
-
renameSync as renameSync5,
|
|
50406
|
-
statSync as statSync3,
|
|
50407
|
-
unlinkSync as unlinkSync3,
|
|
50408
|
-
writeFileSync as writeFileSync6
|
|
50409
|
-
} from "fs";
|
|
50410
|
-
import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
|
|
50411
|
-
import path8 from "path";
|
|
50412
|
-
var CODEX_ROUTE_STATE_VERSION = 1;
|
|
50413
|
-
var INSTALLED_BASE_FIELDS = {
|
|
50414
|
-
name: "OpenAI",
|
|
50415
|
-
wire_api: "responses",
|
|
50416
|
-
requires_openai_auth: true
|
|
50417
|
-
};
|
|
50418
|
-
function codexRouteStateFile() {
|
|
50419
|
-
const override = process.env.BILI_CODEX_ROUTE_STATE?.trim();
|
|
50420
|
-
return override ? path8.resolve(override) : path8.join(dataDir(), "codex-route-state.json");
|
|
50421
|
-
}
|
|
50422
|
-
function resolvePaths(options = {}) {
|
|
50423
|
-
return {
|
|
50424
|
-
configPath: options.configPath ?? codexConfigFile(),
|
|
50425
|
-
statePath: options.statePath ?? codexRouteStateFile(),
|
|
50426
|
-
ownerPid: options.ownerPid
|
|
50427
|
-
};
|
|
50428
|
-
}
|
|
50429
|
-
function sha256(text) {
|
|
50430
|
-
return createHash4("sha256").update(text).digest("hex");
|
|
50431
|
-
}
|
|
50432
|
-
function atomicWrite(filePath, text) {
|
|
50433
|
-
mkdirSync7(path8.dirname(filePath), { recursive: true });
|
|
50434
|
-
const mode = existsSync7(filePath) ? statSync3(filePath).mode : 384;
|
|
50435
|
-
const tempPath = path8.join(path8.dirname(filePath), `.${path8.basename(filePath)}.${process.pid}.${randomUUID3()}.tmp`);
|
|
50436
|
-
let fd;
|
|
50437
|
-
try {
|
|
50438
|
-
fd = openSync2(tempPath, "wx", mode);
|
|
50439
|
-
writeFileSync6(fd, text, "utf8");
|
|
50440
|
-
fsyncSync2(fd);
|
|
50441
|
-
closeSync2(fd);
|
|
50442
|
-
fd = void 0;
|
|
50443
|
-
renameSync5(tempPath, filePath);
|
|
50444
|
-
} catch (error) {
|
|
50445
|
-
if (fd !== void 0) closeSync2(fd);
|
|
50446
|
-
try {
|
|
50447
|
-
unlinkSync3(tempPath);
|
|
50448
|
-
} catch {
|
|
50449
|
-
}
|
|
50450
|
-
throw error;
|
|
50451
|
-
}
|
|
50452
|
-
}
|
|
50453
|
-
function parseState(statePath) {
|
|
50454
|
-
if (!existsSync7(statePath)) return void 0;
|
|
50455
|
-
try {
|
|
50456
|
-
const value = JSON.parse(readFileSync7(statePath, "utf8"));
|
|
50457
|
-
if (value.version === CODEX_ROUTE_STATE_VERSION && value.active === true && typeof value.providerId === "string" && typeof value.configPath === "string" && typeof value.original?.baseUrl === "string" && typeof value.installed?.baseUrl === "string" && value.installed.supportsWebsockets === false && value.originalFields && typeof value.originalFields === "object" && Array.isArray(value.insertedFields) && typeof value.sectionPrefixLength === "number" && value.sectionPrefixLength >= 0) return value;
|
|
50458
|
-
} catch {
|
|
50459
|
-
}
|
|
50460
|
-
return void 0;
|
|
50461
|
-
}
|
|
50462
|
-
function writeState(statePath, state) {
|
|
50463
|
-
atomicWrite(statePath, JSON.stringify(state, null, 2) + "\n");
|
|
50464
|
-
}
|
|
50465
|
-
function removeState(statePath) {
|
|
50466
|
-
try {
|
|
50467
|
-
unlinkSync3(statePath);
|
|
50468
|
-
} catch (error) {
|
|
50469
|
-
if (error.code !== "ENOENT") throw error;
|
|
50470
|
-
}
|
|
50471
|
-
}
|
|
50472
|
-
function installedBaseUrl(port) {
|
|
50473
|
-
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(`invalid bili port: ${port}`);
|
|
50474
|
-
return `http://127.0.0.1:${port}/codex`;
|
|
50475
|
-
}
|
|
50476
|
-
function isInstalledBaseUrl(value) {
|
|
50477
|
-
try {
|
|
50478
|
-
const url = new URL(value);
|
|
50479
|
-
const host = url.hostname.toLowerCase();
|
|
50480
|
-
return url.protocol === "http:" && (host === "127.0.0.1" || host === "localhost" || host === "::1") && url.pathname.replace(/\/+$/, "") === "/codex";
|
|
50481
|
-
} catch {
|
|
50482
|
-
return false;
|
|
50483
|
-
}
|
|
50484
|
-
}
|
|
50485
|
-
function storedField(text, providerId, key) {
|
|
50486
|
-
const snapshot = getCodexProviderFieldSnapshot(text, providerId, key);
|
|
50487
|
-
return snapshot === void 0 ? { present: false } : { present: true, ...snapshot };
|
|
50488
|
-
}
|
|
50489
|
-
function pidAlive(pid) {
|
|
50490
|
-
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
50491
|
-
try {
|
|
50492
|
-
process.kill(pid, 0);
|
|
50493
|
-
return true;
|
|
50494
|
-
} catch (error) {
|
|
50495
|
-
return error.code === "EPERM";
|
|
50496
|
-
}
|
|
50497
|
-
}
|
|
50498
|
-
function getCodexRouteState(options = {}) {
|
|
50499
|
-
return parseState(resolvePaths(options).statePath);
|
|
50500
|
-
}
|
|
50501
|
-
function getCodexTakeoverStatus(options = {}) {
|
|
50502
|
-
const paths = resolvePaths(options);
|
|
50503
|
-
const state = parseState(paths.statePath);
|
|
50504
|
-
if (!state) {
|
|
50505
|
-
try {
|
|
50506
|
-
return {
|
|
50507
|
-
state: "disabled",
|
|
50508
|
-
configPath: paths.configPath,
|
|
50509
|
-
statePath: paths.statePath,
|
|
50510
|
-
provider: resolveActiveCodexProvider(paths.configPath)
|
|
50511
|
-
};
|
|
50512
|
-
} catch (error) {
|
|
50513
|
-
return {
|
|
50514
|
-
state: "disabled",
|
|
50515
|
-
configPath: paths.configPath,
|
|
50516
|
-
statePath: paths.statePath,
|
|
50517
|
-
detail: String(error)
|
|
50518
|
-
};
|
|
50519
|
-
}
|
|
50520
|
-
}
|
|
50521
|
-
if (path8.resolve(state.configPath) !== path8.resolve(paths.configPath)) {
|
|
50522
|
-
return {
|
|
50523
|
-
state: "conflict",
|
|
50524
|
-
configPath: paths.configPath,
|
|
50525
|
-
statePath: paths.statePath,
|
|
50526
|
-
providerId: state.providerId,
|
|
50527
|
-
detail: `route state belongs to a different Codex config: ${state.configPath}`
|
|
50528
|
-
};
|
|
50529
|
-
}
|
|
50530
|
-
const text = readCodexConfig(paths.configPath);
|
|
50531
|
-
const currentBase = getCodexProviderField(text, state.providerId, "base_url");
|
|
50532
|
-
const currentWebsockets = getCodexProviderField(text, state.providerId, "supports_websockets");
|
|
50533
|
-
const matches = currentBase === state.installed.baseUrl && currentWebsockets === false;
|
|
50534
|
-
const port = Number.parseInt(new URL(state.installed.baseUrl).port, 10);
|
|
50535
|
-
return {
|
|
50536
|
-
state: matches ? "enabled" : "conflict",
|
|
50537
|
-
configPath: paths.configPath,
|
|
50538
|
-
statePath: paths.statePath,
|
|
50539
|
-
providerId: state.providerId,
|
|
50540
|
-
port,
|
|
50541
|
-
baseUrl: state.installed.baseUrl,
|
|
50542
|
-
originalBaseUrl: state.original.baseUrl,
|
|
50543
|
-
lastRequestAt: state.lastRequestAt,
|
|
50544
|
-
lastRequestPath: state.lastRequestPath,
|
|
50545
|
-
...matches ? {} : { detail: "managed Codex route fields changed; restore will preserve user edits" }
|
|
50546
|
-
};
|
|
50547
|
-
}
|
|
50548
|
-
function enableCodexTakeover(port, options = {}) {
|
|
50549
|
-
const paths = resolvePaths(options);
|
|
50550
|
-
const existing = parseState(paths.statePath);
|
|
50551
|
-
if (existing) {
|
|
50552
|
-
const status = getCodexTakeoverStatus(paths);
|
|
50553
|
-
const otherLiveOwner = existing.pid && existing.pid !== paths.ownerPid && pidAlive(existing.pid);
|
|
50554
|
-
if (status.state === "enabled" && status.port === port) {
|
|
50555
|
-
if (otherLiveOwner && paths.ownerPid) {
|
|
50556
|
-
throw new Error(`Codex route is owned by running bili process ${existing.pid}`);
|
|
50557
|
-
}
|
|
50558
|
-
if (paths.ownerPid && existing.pid !== paths.ownerPid) {
|
|
50559
|
-
existing.pid = paths.ownerPid;
|
|
50560
|
-
writeState(paths.statePath, existing);
|
|
50561
|
-
return getCodexTakeoverStatus(paths);
|
|
50562
|
-
}
|
|
50563
|
-
return status;
|
|
50564
|
-
}
|
|
50565
|
-
if (otherLiveOwner) {
|
|
50566
|
-
throw new Error(`Codex route is owned by running bili process ${existing.pid}`);
|
|
50567
|
-
}
|
|
50568
|
-
disableCodexTakeover(paths);
|
|
50569
|
-
}
|
|
50570
|
-
const provider = resolveActiveCodexProvider(paths.configPath);
|
|
50571
|
-
if (provider.wireApi !== "responses") {
|
|
50572
|
-
throw new Error(`Codex provider "${provider.id}" uses wire_api=${provider.wireApi}; local route requires responses`);
|
|
50573
|
-
}
|
|
50574
|
-
if (isInstalledBaseUrl(provider.baseUrl)) {
|
|
50575
|
-
throw new Error("Codex provider already points at a local /codex route without billion-context ownership state");
|
|
50576
|
-
}
|
|
50577
|
-
const before = readCodexConfig(paths.configPath);
|
|
50578
|
-
const installedUrl = installedBaseUrl(port);
|
|
50579
|
-
const managedKeys = ["base_url", "supports_websockets"];
|
|
50580
|
-
const originalFields = {};
|
|
50581
|
-
for (const key of managedKeys) originalFields[key] = storedField(before, provider.id, key);
|
|
50582
|
-
let after = before;
|
|
50583
|
-
const insertedFields = [];
|
|
50584
|
-
let sectionAdded = false;
|
|
50585
|
-
if (!provider.sectionExists) {
|
|
50586
|
-
sectionAdded = true;
|
|
50587
|
-
const fields = {
|
|
50588
|
-
...INSTALLED_BASE_FIELDS,
|
|
50589
|
-
base_url: installedUrl,
|
|
50590
|
-
supports_websockets: false
|
|
50591
|
-
};
|
|
50592
|
-
after = appendCodexProviderSection(after, provider.id, fields);
|
|
50593
|
-
insertedFields.push(...Object.keys(fields));
|
|
50594
|
-
} else {
|
|
50595
|
-
const baseResult = setCodexProviderField(after, provider.id, "base_url", installedUrl);
|
|
50596
|
-
after = baseResult.text;
|
|
50597
|
-
if (baseResult.added) insertedFields.push("base_url");
|
|
50598
|
-
const websocketResult = setCodexProviderField(after, provider.id, "supports_websockets", false);
|
|
50599
|
-
after = websocketResult.text;
|
|
50600
|
-
if (websocketResult.added) insertedFields.push("supports_websockets");
|
|
50601
|
-
}
|
|
50602
|
-
const state = {
|
|
50603
|
-
version: CODEX_ROUTE_STATE_VERSION,
|
|
50604
|
-
active: true,
|
|
50605
|
-
...paths.ownerPid ? { pid: paths.ownerPid } : {},
|
|
50606
|
-
providerId: provider.id,
|
|
50607
|
-
configPath: paths.configPath,
|
|
50608
|
-
sectionAdded,
|
|
50609
|
-
sectionPrefixLength: sectionAdded ? before.length === 0 ? 0 : /(?:\r\n|\r|\n)$/.test(before) ? before.match(/(\r\n|\r|\n)$/)?.[0].length ?? 1 : 2 * (before.match(/\r\n|\r|\n/)?.[0].length ?? (process.platform === "win32" ? 2 : 1)) : 0,
|
|
50610
|
-
insertedFields,
|
|
50611
|
-
originalFields,
|
|
50612
|
-
original: { baseUrl: provider.baseUrl, supportsWebsockets: provider.supportsWebsockets },
|
|
50613
|
-
installed: { baseUrl: installedUrl, supportsWebsockets: false },
|
|
50614
|
-
configHashBefore: sha256(before),
|
|
50615
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
50616
|
-
};
|
|
50617
|
-
writeState(paths.statePath, state);
|
|
50618
|
-
try {
|
|
50619
|
-
atomicWrite(paths.configPath, after);
|
|
50620
|
-
} catch (error) {
|
|
50621
|
-
removeState(paths.statePath);
|
|
50622
|
-
throw error;
|
|
50623
|
-
}
|
|
50624
|
-
return getCodexTakeoverStatus(paths);
|
|
50625
|
-
}
|
|
50626
|
-
function disableCodexTakeover(options = {}) {
|
|
50627
|
-
const paths = resolvePaths(options);
|
|
50628
|
-
const state = parseState(paths.statePath);
|
|
50629
|
-
if (!state) return getCodexTakeoverStatus(paths);
|
|
50630
|
-
let text = readCodexConfig(state.configPath);
|
|
50631
|
-
const preserved = [];
|
|
50632
|
-
const installedValues = {
|
|
50633
|
-
...INSTALLED_BASE_FIELDS,
|
|
50634
|
-
base_url: state.installed.baseUrl,
|
|
50635
|
-
supports_websockets: false
|
|
50636
|
-
};
|
|
50637
|
-
for (const [key, installedValue] of Object.entries(installedValues)) {
|
|
50638
|
-
if (!state.sectionAdded && key !== "base_url" && key !== "supports_websockets") continue;
|
|
50639
|
-
const current = getCodexProviderField(text, state.providerId, key);
|
|
50640
|
-
if (current !== installedValue) {
|
|
50641
|
-
if (current !== void 0) preserved.push(key);
|
|
50642
|
-
continue;
|
|
50643
|
-
}
|
|
50644
|
-
const original = state.originalFields[key];
|
|
50645
|
-
if (original?.present && original.value !== void 0) {
|
|
50646
|
-
text = typeof original.encoded === "string" ? restoreCodexProviderField(text, state.providerId, key, {
|
|
50647
|
-
value: original.value,
|
|
50648
|
-
encoded: original.encoded
|
|
50649
|
-
}) : setCodexProviderField(text, state.providerId, key, original.value).text;
|
|
50650
|
-
} else {
|
|
50651
|
-
text = removeCodexProviderField(text, state.providerId, key);
|
|
50652
|
-
}
|
|
50653
|
-
}
|
|
50654
|
-
if (state.sectionAdded) {
|
|
50655
|
-
text = removeCodexProviderSectionWithPrefixIfEmpty(text, state.providerId, state.sectionPrefixLength ?? 0);
|
|
50656
|
-
}
|
|
50657
|
-
atomicWrite(state.configPath, text);
|
|
50658
|
-
removeState(paths.statePath);
|
|
50659
|
-
const status = getCodexTakeoverStatus({ ...paths, configPath: state.configPath });
|
|
50660
|
-
if (preserved.length > 0) status.detail = `\u68C0\u6D4B\u5230\u7528\u6237\u4FEE\u6539\uFF0C\u5DF2\u4FDD\u7559\uFF1A${preserved.join(", ")}`;
|
|
50661
|
-
return status;
|
|
50662
|
-
}
|
|
50663
|
-
function recoverStaleCodexTakeover(options = {}) {
|
|
50664
|
-
const paths = resolvePaths(options);
|
|
50665
|
-
const state = parseState(paths.statePath);
|
|
50666
|
-
if (state?.pid && !pidAlive(state.pid)) return disableCodexTakeover(paths);
|
|
50667
|
-
return getCodexTakeoverStatus(paths);
|
|
50668
|
-
}
|
|
50669
|
-
function claimCodexTakeover(ownerPid = process.pid, options = {}) {
|
|
50670
|
-
const paths = resolvePaths(options);
|
|
50671
|
-
const state = parseState(paths.statePath);
|
|
50672
|
-
if (!state) return getCodexTakeoverStatus(paths);
|
|
50673
|
-
if (state.pid && state.pid !== ownerPid && pidAlive(state.pid)) {
|
|
50674
|
-
throw new Error(`Codex route is owned by running bili process ${state.pid}`);
|
|
50675
|
-
}
|
|
50676
|
-
state.pid = ownerPid;
|
|
50677
|
-
writeState(paths.statePath, state);
|
|
50678
|
-
return getCodexTakeoverStatus(paths);
|
|
50679
|
-
}
|
|
50680
|
-
function restoreCodexTakeoverOwnedBy(ownerPid = process.pid, options = {}) {
|
|
50681
|
-
const paths = resolvePaths(options);
|
|
50682
|
-
const state = parseState(paths.statePath);
|
|
50683
|
-
if (!state || state.pid !== ownerPid) return getCodexTakeoverStatus(paths);
|
|
50684
|
-
return disableCodexTakeover(paths);
|
|
50685
|
-
}
|
|
50686
|
-
function recordCodexRouteRequest(requestPath, options = {}) {
|
|
50687
|
-
const paths = resolvePaths(options);
|
|
50688
|
-
const state = parseState(paths.statePath);
|
|
50689
|
-
if (!state) return;
|
|
50690
|
-
state.lastRequestAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
50691
|
-
state.lastRequestPath = requestPath;
|
|
50692
|
-
writeState(paths.statePath, state);
|
|
50693
|
-
}
|
|
50694
|
-
|
|
50695
51069
|
// src/content-encoding.ts
|
|
50696
51070
|
import { promisify } from "util";
|
|
50697
51071
|
import { brotliDecompress, gunzip, inflate, inflateRaw } from "zlib";
|
|
@@ -51427,19 +51801,24 @@ function resolveUpstream(_opts, reqUrl, req) {
|
|
|
51427
51801
|
const mitmKey = mitmUpstream.replace(/^https:\/\//, "mitm://");
|
|
51428
51802
|
return { upstream: mitmUpstream, rewrittenUrl: mitmKey + (reqUrl ?? "") };
|
|
51429
51803
|
}
|
|
51430
|
-
|
|
51431
|
-
|
|
51432
|
-
|
|
51433
|
-
|
|
51434
|
-
const
|
|
51435
|
-
|
|
51436
|
-
|
|
51437
|
-
|
|
51438
|
-
|
|
51439
|
-
|
|
51440
|
-
|
|
51441
|
-
|
|
51442
|
-
|
|
51804
|
+
const KNOWN_PROTOCOLS = ["responses", "anthropic", "openai"];
|
|
51805
|
+
if (reqUrl.startsWith("/bili/")) {
|
|
51806
|
+
let rest = reqUrl.slice(6);
|
|
51807
|
+
let explicitProtocol;
|
|
51808
|
+
for (const p2 of KNOWN_PROTOCOLS) {
|
|
51809
|
+
const prefix = `${p2}/`;
|
|
51810
|
+
if (rest.startsWith(prefix + "http://") || rest.startsWith(prefix + "https://")) {
|
|
51811
|
+
explicitProtocol = p2;
|
|
51812
|
+
rest = rest.slice(prefix.length);
|
|
51813
|
+
break;
|
|
51814
|
+
}
|
|
51815
|
+
}
|
|
51816
|
+
if (rest.startsWith("http://") || rest.startsWith("https://")) {
|
|
51817
|
+
try {
|
|
51818
|
+
const u2 = new URL(rest);
|
|
51819
|
+
return { upstream: `${u2.protocol}//${u2.host}`, rewrittenUrl: rest, explicitProtocol };
|
|
51820
|
+
} catch {
|
|
51821
|
+
}
|
|
51443
51822
|
}
|
|
51444
51823
|
}
|
|
51445
51824
|
return void 0;
|
|
@@ -51521,13 +51900,6 @@ async function startServer(opts) {
|
|
|
51521
51900
|
if (process.platform === "win32") {
|
|
51522
51901
|
process.on("SIGBREAK", () => shutdown("SIGBREAK"));
|
|
51523
51902
|
}
|
|
51524
|
-
server.once("close", () => {
|
|
51525
|
-
try {
|
|
51526
|
-
restoreCodexTakeoverOwnedBy(process.pid);
|
|
51527
|
-
} catch (error) {
|
|
51528
|
-
log2("warn", `[codex-route] restore on server close failed: ${String(error)}`);
|
|
51529
|
-
}
|
|
51530
|
-
});
|
|
51531
51903
|
return server;
|
|
51532
51904
|
}
|
|
51533
51905
|
function isLoopback(addr) {
|
|
@@ -51593,25 +51965,8 @@ async function handle(req, res, opts, core, config, log2) {
|
|
|
51593
51965
|
}, opts.port);
|
|
51594
51966
|
}
|
|
51595
51967
|
if (req.method === "POST" && req.url === "/__bili/config/reload") return handleConfigReload(opts, res, log2);
|
|
51596
|
-
if (req.method === "GET" && req.url === "/__bili/codex") {
|
|
51597
|
-
res.writeHead(200, { "content-type": "application/json" });
|
|
51598
|
-
res.end(JSON.stringify(getCodexTakeoverStatus()));
|
|
51599
|
-
return;
|
|
51600
|
-
}
|
|
51601
|
-
if (req.method === "POST" && (req.url === "/__bili/codex/enable" || req.url === "/__bili/codex/disable")) {
|
|
51602
|
-
try {
|
|
51603
|
-
const status = req.url.endsWith("/enable") ? enableCodexTakeover(opts.port, { ownerPid: process.pid }) : disableCodexTakeover();
|
|
51604
|
-
res.writeHead(200, { "content-type": "application/json" });
|
|
51605
|
-
res.end(JSON.stringify(status));
|
|
51606
|
-
} catch (error) {
|
|
51607
|
-
res.writeHead(409, { "content-type": "application/json" });
|
|
51608
|
-
res.end(JSON.stringify({ error: String(error) }));
|
|
51609
|
-
}
|
|
51610
|
-
return;
|
|
51611
|
-
}
|
|
51612
51968
|
if (req.method === "GET" && req.url === "/__bili/upstream") {
|
|
51613
|
-
const
|
|
51614
|
-
const target = routeState?.original.baseUrl ?? opts.upstream;
|
|
51969
|
+
const target = opts.upstream;
|
|
51615
51970
|
const decision = resolveProxyDecision(opts.routes, opts.proxy, target, opts.proxyFallback);
|
|
51616
51971
|
const connection = getUpstreamConnectionStatus();
|
|
51617
51972
|
const connectionMatchesTarget = (() => {
|
|
@@ -51638,8 +51993,7 @@ async function handle(req, res, opts, core, config, log2) {
|
|
|
51638
51993
|
return;
|
|
51639
51994
|
}
|
|
51640
51995
|
if (req.method === "POST" && req.url === "/__bili/upstream/test") {
|
|
51641
|
-
const
|
|
51642
|
-
const target = routeState?.original.baseUrl ?? opts.upstream;
|
|
51996
|
+
const target = opts.upstream;
|
|
51643
51997
|
const targetUrl2 = new URL(target).origin;
|
|
51644
51998
|
const proxyUrl = resolveProxyDecision(opts.routes, opts.proxy, target, opts.proxyFallback).proxy;
|
|
51645
51999
|
try {
|
|
@@ -51658,11 +52012,9 @@ async function handle(req, res, opts, core, config, log2) {
|
|
|
51658
52012
|
}
|
|
51659
52013
|
return;
|
|
51660
52014
|
}
|
|
51661
|
-
if (req.
|
|
51662
|
-
|
|
51663
|
-
|
|
51664
|
-
res.writeHead(503, { "content-type": "application/json" });
|
|
51665
|
-
res.end(JSON.stringify({ error: "Codex local route is not enabled; run bili codex enable" }));
|
|
52015
|
+
if (req.headers.upgrade === "websocket") {
|
|
52016
|
+
res.writeHead(426, { "content-type": "application/json" });
|
|
52017
|
+
res.end(JSON.stringify({ error: "WebSocket upgrades are not supported; use HTTP POST" }));
|
|
51666
52018
|
return;
|
|
51667
52019
|
}
|
|
51668
52020
|
let bodyBuffer;
|
|
@@ -51687,12 +52039,9 @@ async function handle(req, res, opts, core, config, log2) {
|
|
|
51687
52039
|
const urlPath = url.split("?", 2)[0];
|
|
51688
52040
|
const responsesCompact = urlPath.endsWith("/responses/compact");
|
|
51689
52041
|
const countTokens = isCountTokensRequest(req.method ?? "GET", urlPath, bodyBuffer.length > 0);
|
|
51690
|
-
const protocol = req.method === "POST" && bodyBuffer.length > 0 ? urlPath.endsWith("/chat/completions") ? "openai" : urlPath.endsWith("/v1/messages") || urlPath.endsWith("/messages") ? "anthropic" : urlPath.endsWith("/responses") || responsesCompact ? "responses" : null : null;
|
|
51691
52042
|
const route = resolveUpstream(opts, req.url ?? "", req);
|
|
51692
|
-
if (route && (req.url === "/codex" || req.url?.startsWith("/codex/") || req.url?.startsWith("/codex?"))) {
|
|
51693
|
-
recordCodexRouteRequest(req.url ?? "/codex");
|
|
51694
|
-
}
|
|
51695
52043
|
const upstreamOrigin = route ? route.upstream : opts.upstream;
|
|
52044
|
+
const protocol = route?.explicitProtocol ?? (req.method === "POST" && bodyBuffer.length > 0 ? urlPath.endsWith("/chat/completions") ? "openai" : urlPath.endsWith("/v1/messages") || urlPath.endsWith("/messages") ? "anthropic" : urlPath.endsWith("/responses") || responsesCompact ? "responses" : null : null);
|
|
51696
52045
|
let parsed = null;
|
|
51697
52046
|
if (protocol && bodyBuffer.length > 0) {
|
|
51698
52047
|
try {
|
|
@@ -51824,7 +52173,7 @@ function prepareAnthropic(parsed, req, opts, core, config, log2, session) {
|
|
|
51824
52173
|
}
|
|
51825
52174
|
const rebuilt = { ...parsed, messages: rebuiltMessages, system: systemOut, tools: toolsOut };
|
|
51826
52175
|
markDirty(session);
|
|
51827
|
-
return { body: JSON.stringify(rebuilt), session, processedMessages, originalMessages, protocol: "anthropic", stream: stream2, compressInjected: opts.compress.injectTool };
|
|
52176
|
+
return { body: JSON.stringify(rebuilt), session, processedMessages, originalMessages, anthropicSystem: parsed.system, protocol: "anthropic", stream: stream2, compressInjected: opts.compress.injectTool };
|
|
51828
52177
|
}
|
|
51829
52178
|
function prepareOpenai(parsed, req, opts, core, config, log2, session) {
|
|
51830
52179
|
const sessionId = session.id;
|
|
@@ -51888,12 +52237,14 @@ function prepareResponses(parsed, req, opts, core, config, log2, session, identi
|
|
|
51888
52237
|
}
|
|
51889
52238
|
let processedMessages = [];
|
|
51890
52239
|
let originalMessages = [];
|
|
52240
|
+
let responsesProjection;
|
|
51891
52241
|
let rebuiltInput = parsed.input;
|
|
51892
52242
|
let toolsOut = parsed.tools;
|
|
51893
52243
|
const shouldInject = opts.compress.injectTool;
|
|
51894
52244
|
const responsesTextProtocol = FORCE_TEXT_PROTOCOL || isChatGptCodexUpstream(session.meta.upstreamOrigin) || isCodexResponsesLite(req.headers, parsed);
|
|
51895
52245
|
try {
|
|
51896
52246
|
const projection = responsesToCore(parsed);
|
|
52247
|
+
responsesProjection = projection;
|
|
51897
52248
|
const { msgs } = projection;
|
|
51898
52249
|
originalMessages = msgs;
|
|
51899
52250
|
if (process.env.ACP_DEBUG) {
|
|
@@ -51959,6 +52310,7 @@ function prepareResponses(parsed, req, opts, core, config, log2, session, identi
|
|
|
51959
52310
|
session,
|
|
51960
52311
|
processedMessages,
|
|
51961
52312
|
originalMessages,
|
|
52313
|
+
responsesProjection,
|
|
51962
52314
|
protocol: "responses",
|
|
51963
52315
|
stream: stream2,
|
|
51964
52316
|
compressInjected: shouldInject,
|
|
@@ -52097,9 +52449,20 @@ async function forward(req, res, opts, body, prepared, core, config, log2, route
|
|
|
52097
52449
|
return fn?.name ?? t.name ?? "?";
|
|
52098
52450
|
});
|
|
52099
52451
|
log2("info", `[debug] tools=[${toolNames.join(",")}] msgs=${parsed.messages?.length ?? 0} stream=${parsed.stream ?? false} system_len=${JSON.stringify(parsed.messages?.find((m2) => m2.role === "system")?.content ?? "").length}`);
|
|
52100
|
-
if (process.env.ACP_DUMP_REQ
|
|
52101
|
-
const
|
|
52102
|
-
|
|
52452
|
+
if (process.env.ACP_DUMP_REQ !== "0") {
|
|
52453
|
+
const dumpDir = process.env.ACP_DUMP_DIR || `${stateDir()}/dumps`;
|
|
52454
|
+
try {
|
|
52455
|
+
fs3.mkdirSync(dumpDir, { recursive: true });
|
|
52456
|
+
} catch {
|
|
52457
|
+
}
|
|
52458
|
+
const sid = prepared?.session.id ?? "unknown";
|
|
52459
|
+
const out = `${dumpDir}/req-${Date.now()}-${sid}.json`;
|
|
52460
|
+
try {
|
|
52461
|
+
const pretty = JSON.stringify(JSON.parse(body), null, 2);
|
|
52462
|
+
fs3.writeFileSync(out, pretty);
|
|
52463
|
+
} catch {
|
|
52464
|
+
fs3.writeFileSync(out, body);
|
|
52465
|
+
}
|
|
52103
52466
|
log2("info", `[debug] forwarded body written to ${out}`);
|
|
52104
52467
|
}
|
|
52105
52468
|
} catch {
|
|
@@ -52115,6 +52478,13 @@ async function forward(req, res, opts, body, prepared, core, config, log2, route
|
|
|
52115
52478
|
headers["x-session-id"] = affinity;
|
|
52116
52479
|
}
|
|
52117
52480
|
const proxyUrl = resolveProxy(opts.routes, opts.proxy, route?.rewrittenUrl ?? upstreamUrl, opts.proxyFallback);
|
|
52481
|
+
if (opts.debug) {
|
|
52482
|
+
const hdrLog = {};
|
|
52483
|
+
for (const [hk, hv] of Object.entries(headers)) {
|
|
52484
|
+
if (typeof hv === "string") hdrLog[hk] = hv.length > 200 ? hv.slice(0, 200) + "..." : hv;
|
|
52485
|
+
}
|
|
52486
|
+
log2("info", `[${prepared?.session.id ?? "unknown"}] \u2192 upstream headers: ${JSON.stringify(hdrLog)}`);
|
|
52487
|
+
}
|
|
52118
52488
|
const dispatcher = proxyDispatcher(proxyUrl);
|
|
52119
52489
|
const init = {
|
|
52120
52490
|
method: req.method ?? "GET",
|
|
@@ -52136,6 +52506,14 @@ async function forward(req, res, opts, body, prepared, core, config, log2, route
|
|
|
52136
52506
|
if (UPSTREAM_HOP_HEADERS.has(k2.toLowerCase())) return;
|
|
52137
52507
|
respHeaders[k2] = v2;
|
|
52138
52508
|
});
|
|
52509
|
+
if (opts.debug) {
|
|
52510
|
+
const respLog = {};
|
|
52511
|
+
upstream.headers.forEach((v2, k2) => {
|
|
52512
|
+
if (UPSTREAM_HOP_HEADERS.has(k2.toLowerCase())) return;
|
|
52513
|
+
respLog[k2] = v2.length > 300 ? v2.slice(0, 300) + "..." : v2;
|
|
52514
|
+
});
|
|
52515
|
+
log2("info", `[${prepared?.session.id ?? "unknown"}] \u2190 upstream response headers: ${JSON.stringify(respLog)}`);
|
|
52516
|
+
}
|
|
52139
52517
|
if (!upstream.ok) {
|
|
52140
52518
|
res.writeHead(upstream.status, respHeaders);
|
|
52141
52519
|
if (upstream.body) await pipeThrough(upstream.body, res);
|
|
@@ -52179,7 +52557,37 @@ async function forward(req, res, opts, body, prepared, core, config, log2, route
|
|
|
52179
52557
|
dumpRaw = dumpStreamToFile(b2, opts.dumpSse, `${Date.now()}-${prepared.session.id}-raw.sse`);
|
|
52180
52558
|
}
|
|
52181
52559
|
try {
|
|
52182
|
-
if (
|
|
52560
|
+
if (process.env.ACP_LOOP_V2 !== "0") {
|
|
52561
|
+
const parsedReq = JSON.parse(typeof body === "string" ? body : body.toString("utf8"));
|
|
52562
|
+
const reqHeaders = {};
|
|
52563
|
+
for (const [k2, v2] of Object.entries(headers)) {
|
|
52564
|
+
if (k2.toLowerCase() === "content-length" || k2.toLowerCase() === "host") continue;
|
|
52565
|
+
reqHeaders[k2] = v2;
|
|
52566
|
+
}
|
|
52567
|
+
reqHeaders["content-type"] = "application/json";
|
|
52568
|
+
const textProtocol = prepared.protocol === "responses" && !!prepared.responsesTextProtocol;
|
|
52569
|
+
const systemPrompt = textProtocol ? buildCompressTextSystemPrompt() : buildCompressSystemPrompt();
|
|
52570
|
+
const adapter = pickAdapter(prepared.protocol, parsedReq, textProtocol, prepared.responsesProjection, prepared.anthropicSystem);
|
|
52571
|
+
const loop = runCompressLoop(
|
|
52572
|
+
streamToRead,
|
|
52573
|
+
{ core, config, messages: prepared.processedMessages.length > 0 ? prepared.processedMessages : prepared.originalMessages, session: prepared.session, log: ctx.log, proxyUrl, textProtocol, debug: opts.debug },
|
|
52574
|
+
parsedReq,
|
|
52575
|
+
{ url: upstreamUrl, headers: reqHeaders },
|
|
52576
|
+
adapter,
|
|
52577
|
+
systemPrompt
|
|
52578
|
+
);
|
|
52579
|
+
for await (const chunk of loop) {
|
|
52580
|
+
{
|
|
52581
|
+
const s3 = chunk.toString("utf8");
|
|
52582
|
+
if (s3.includes("<acp ") || s3.includes("</acp")) {
|
|
52583
|
+
log2("warn", `[${prepared.session.id}] tag echo: ${prepared.protocol} response stream contains <acp tag`);
|
|
52584
|
+
}
|
|
52585
|
+
}
|
|
52586
|
+
res.write(chunk);
|
|
52587
|
+
if (res.writableNeedDrain) await new Promise((r) => res.once("drain", () => r()));
|
|
52588
|
+
}
|
|
52589
|
+
res.end();
|
|
52590
|
+
} else if (prepared.protocol === "openai") {
|
|
52183
52591
|
const parsedReq = JSON.parse(typeof body === "string" ? body : body.toString("utf8"));
|
|
52184
52592
|
const reqHeaders = {};
|
|
52185
52593
|
for (const [k2, v2] of Object.entries(headers)) {
|
|
@@ -52325,10 +52733,10 @@ async function pipeThrough(stream2, res) {
|
|
|
52325
52733
|
}
|
|
52326
52734
|
}
|
|
52327
52735
|
async function dumpStreamToFile(stream2, dir, name) {
|
|
52328
|
-
const { mkdirSync:
|
|
52736
|
+
const { mkdirSync: mkdirSync6, createWriteStream: createWriteStream2 } = await import("fs");
|
|
52329
52737
|
const { join: join4 } = await import("path");
|
|
52330
52738
|
try {
|
|
52331
|
-
|
|
52739
|
+
mkdirSync6(dir, { recursive: true });
|
|
52332
52740
|
const ws2 = createWriteStream2(join4(dir, name));
|
|
52333
52741
|
const reader = stream2.getReader();
|
|
52334
52742
|
try {
|
|
@@ -55397,12 +55805,12 @@ var To = (s3) => {
|
|
|
55397
55805
|
};
|
|
55398
55806
|
|
|
55399
55807
|
// src/update.ts
|
|
55400
|
-
import
|
|
55808
|
+
import path6 from "path";
|
|
55401
55809
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
55402
55810
|
var REGISTRY_BASE = "https://registry.npmjs.org";
|
|
55403
55811
|
var CHECK_INTERVAL_MS = 3 * 60 * 1e3;
|
|
55404
|
-
var THROTTLE_FILE =
|
|
55405
|
-
var LOCK_FILE =
|
|
55812
|
+
var THROTTLE_FILE = path6.join(cacheDir(), ".update-check");
|
|
55813
|
+
var LOCK_FILE = path6.join(cacheDir(), ".update-lock");
|
|
55406
55814
|
var LOCK_STALE_MS = 2 * 60 * 1e3;
|
|
55407
55815
|
var timer;
|
|
55408
55816
|
var inFlight = false;
|
|
@@ -55431,27 +55839,27 @@ async function readLastCheck() {
|
|
|
55431
55839
|
}
|
|
55432
55840
|
async function writeLastCheck(ts2) {
|
|
55433
55841
|
try {
|
|
55434
|
-
await mkdir2(
|
|
55842
|
+
await mkdir2(path6.dirname(THROTTLE_FILE), { recursive: true });
|
|
55435
55843
|
await writeFile2(THROTTLE_FILE, String(ts2), "utf-8");
|
|
55436
55844
|
} catch {
|
|
55437
55845
|
}
|
|
55438
55846
|
}
|
|
55439
55847
|
async function findInstallDir(packageName) {
|
|
55440
|
-
let dir =
|
|
55848
|
+
let dir = path6.dirname(fileURLToPath2(import.meta.url));
|
|
55441
55849
|
for (; ; ) {
|
|
55442
55850
|
try {
|
|
55443
|
-
const pkg = JSON.parse(await readFile2(
|
|
55851
|
+
const pkg = JSON.parse(await readFile2(path6.join(dir, "package.json"), "utf-8"));
|
|
55444
55852
|
if (pkg.name === packageName) return dir;
|
|
55445
55853
|
} catch {
|
|
55446
55854
|
}
|
|
55447
|
-
const parent =
|
|
55855
|
+
const parent = path6.dirname(dir);
|
|
55448
55856
|
if (parent === dir) return void 0;
|
|
55449
55857
|
dir = parent;
|
|
55450
55858
|
}
|
|
55451
55859
|
}
|
|
55452
55860
|
async function readDiskVersion(installDir) {
|
|
55453
55861
|
try {
|
|
55454
|
-
const pkg = JSON.parse(await readFile2(
|
|
55862
|
+
const pkg = JSON.parse(await readFile2(path6.join(installDir, "package.json"), "utf-8"));
|
|
55455
55863
|
return pkg.version;
|
|
55456
55864
|
} catch {
|
|
55457
55865
|
return void 0;
|
|
@@ -55605,14 +56013,14 @@ async function installViaTarball(version2, tarballUrl, installDir) {
|
|
|
55605
56013
|
} catch (e) {
|
|
55606
56014
|
return { ok: false, error: `tarball download failed: ${String(e)}` };
|
|
55607
56015
|
}
|
|
55608
|
-
const tmpFile =
|
|
56016
|
+
const tmpFile = path6.join(cacheDir(), `.update-${version2}.tgz`);
|
|
55609
56017
|
try {
|
|
55610
56018
|
await mkdir2(cacheDir(), { recursive: true });
|
|
55611
56019
|
await writeFile2(tmpFile, tgzBuffer);
|
|
55612
56020
|
} catch (e) {
|
|
55613
56021
|
return { ok: false, error: `failed to write temp file ${tmpFile}: ${String(e)}` };
|
|
55614
56022
|
}
|
|
55615
|
-
const stagingDir =
|
|
56023
|
+
const stagingDir = path6.join(cacheDir(), `.update-staging-${version2}`);
|
|
55616
56024
|
try {
|
|
55617
56025
|
await rm(stagingDir, { recursive: true, force: true });
|
|
55618
56026
|
await mkdir2(stagingDir, { recursive: true });
|
|
@@ -55655,14 +56063,14 @@ function startAutoUpdate(opts) {
|
|
|
55655
56063
|
}
|
|
55656
56064
|
|
|
55657
56065
|
// src/cli.ts
|
|
55658
|
-
import { readFileSync as
|
|
56066
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
55659
56067
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
55660
|
-
import
|
|
56068
|
+
import path7 from "path";
|
|
55661
56069
|
var VERSION = (() => {
|
|
55662
56070
|
try {
|
|
55663
56071
|
const here = fileURLToPath3(import.meta.url);
|
|
55664
|
-
const pkg =
|
|
55665
|
-
return JSON.parse(
|
|
56072
|
+
const pkg = path7.join(path7.dirname(here), "..", "package.json");
|
|
56073
|
+
return JSON.parse(readFileSync5(pkg, "utf8")).version ?? "dev";
|
|
55666
56074
|
} catch {
|
|
55667
56075
|
return "dev";
|
|
55668
56076
|
}
|
|
@@ -55670,8 +56078,8 @@ var VERSION = (() => {
|
|
|
55670
56078
|
var PACKAGE_NAME = (() => {
|
|
55671
56079
|
try {
|
|
55672
56080
|
const here = fileURLToPath3(import.meta.url);
|
|
55673
|
-
const pkg =
|
|
55674
|
-
return JSON.parse(
|
|
56081
|
+
const pkg = path7.join(path7.dirname(here), "..", "package.json");
|
|
56082
|
+
return JSON.parse(readFileSync5(pkg, "utf8")).name ?? "billion-context";
|
|
55675
56083
|
} catch {
|
|
55676
56084
|
return "billion-context";
|
|
55677
56085
|
}
|
|
@@ -55680,9 +56088,6 @@ var HELP = `bili ${VERSION} \u2014 billion-context proxy
|
|
|
55680
56088
|
|
|
55681
56089
|
Usage:
|
|
55682
56090
|
bili [start] [options] start the proxy (default: reads ${configFile()})
|
|
55683
|
-
bili codex enable [--port N] route Codex Desktop through the local proxy
|
|
55684
|
-
bili codex disable restore the active Codex provider endpoint
|
|
55685
|
-
bili codex status show Codex Desktop local-route status
|
|
55686
56091
|
bili update check for & install a newer version now
|
|
55687
56092
|
bili --version print version
|
|
55688
56093
|
bili --help show this help
|
|
@@ -55762,31 +56167,15 @@ function parseArgs(argv) {
|
|
|
55762
56167
|
command = command === "help" || command === "version" ? command : "start";
|
|
55763
56168
|
} else if (cmd === "update") {
|
|
55764
56169
|
command = "update";
|
|
55765
|
-
} else if (cmd === "codex") {
|
|
55766
|
-
command = "codex";
|
|
55767
56170
|
} else {
|
|
55768
56171
|
console.error(`bili: unknown command "${cmd}" (try "bili --help")`);
|
|
55769
56172
|
process.exit(2);
|
|
55770
56173
|
}
|
|
55771
56174
|
}
|
|
55772
|
-
|
|
55773
|
-
if (positional[0] === "codex") {
|
|
55774
|
-
command = "codex";
|
|
55775
|
-
const action = positional[1];
|
|
55776
|
-
if (action !== "enable" && action !== "disable" && action !== "status") {
|
|
55777
|
-
console.error('bili: "codex" requires enable, disable, or status');
|
|
55778
|
-
process.exit(2);
|
|
55779
|
-
}
|
|
55780
|
-
if (positional.length > 2) {
|
|
55781
|
-
console.error(`bili: unexpected argument "${positional[2]}"`);
|
|
55782
|
-
process.exit(2);
|
|
55783
|
-
}
|
|
55784
|
-
codexAction = action;
|
|
55785
|
-
}
|
|
55786
|
-
return { command, codexAction, overrides };
|
|
56175
|
+
return { command, overrides };
|
|
55787
56176
|
}
|
|
55788
56177
|
async function main() {
|
|
55789
|
-
const { command,
|
|
56178
|
+
const { command, overrides } = parseArgs(process.argv.slice(2));
|
|
55790
56179
|
if (command === "help") {
|
|
55791
56180
|
process.stdout.write(HELP);
|
|
55792
56181
|
return;
|
|
@@ -55802,28 +56191,8 @@ async function main() {
|
|
|
55802
56191
|
for (const [k2, v2] of Object.entries(overrides)) {
|
|
55803
56192
|
if (v2 !== void 0) process.env[k2] = v2;
|
|
55804
56193
|
}
|
|
55805
|
-
if (command === "codex") {
|
|
55806
|
-
if (codexAction === "enable") recoverStaleCodexTakeover();
|
|
55807
|
-
const status = codexAction === "enable" ? enableCodexTakeover(loadOptions().port) : codexAction === "disable" ? disableCodexTakeover() : getCodexTakeoverStatus();
|
|
55808
|
-
if (status.state === "enabled") {
|
|
55809
|
-
process.stdout.write(`Codex route enabled for ${status.providerId}: ${status.baseUrl} \u2192 ${status.originalBaseUrl}
|
|
55810
|
-
`);
|
|
55811
|
-
} else if (status.state === "disabled") {
|
|
55812
|
-
const provider = status.provider ? ` (provider ${status.provider.id})` : "";
|
|
55813
|
-
process.stdout.write(`Codex route disabled${provider}: ${status.configPath}${status.detail ? `
|
|
55814
|
-
${status.detail}` : ""}
|
|
55815
|
-
`);
|
|
55816
|
-
} else {
|
|
55817
|
-
process.stdout.write(`Codex Desktop local route conflict: ${status.detail ?? status.configPath}
|
|
55818
|
-
`);
|
|
55819
|
-
process.exitCode = 1;
|
|
55820
|
-
}
|
|
55821
|
-
return;
|
|
55822
|
-
}
|
|
55823
|
-
recoverStaleCodexTakeover();
|
|
55824
56194
|
ensureConfigTemplate();
|
|
55825
56195
|
const opts = loadOptions();
|
|
55826
|
-
claimCodexTakeover(process.pid);
|
|
55827
56196
|
await startServer(opts);
|
|
55828
56197
|
if (opts.autoUpdate) {
|
|
55829
56198
|
startAutoUpdate({ packageName: PACKAGE_NAME, currentVersion: VERSION, autoUpdate: true });
|