@eddyskywalker/dsh-chatgpt-subscription 0.11.5 → 0.12.1
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/CHANGELOG.md +19 -0
- package/README.md +36 -2
- package/lib/client.js +182 -59
- package/lib/client.js.map +1 -1
- package/lib/index.js +465 -25
- package/lib/types/client/CodexSubscriptionSection.d.ts +18 -0
- package/lib/types/client/CodexSubscriptionSection.d.ts.map +1 -1
- package/lib/types/client/antigravity/styles.d.ts.map +1 -1
- package/lib/types/client/claude/styles.d.ts.map +1 -1
- package/lib/types/client/common/plugin-style.d.ts +23 -0
- package/lib/types/client/common/plugin-style.d.ts.map +1 -0
- package/lib/types/client/common/styles.d.ts.map +1 -1
- package/lib/types/client/kimi-code/styles.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +45 -5
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/mermaid/styles.d.ts.map +1 -1
- package/lib/types/client/minimax-code/styles.d.ts.map +1 -1
- package/lib/types/client/styles.d.ts +5 -0
- package/lib/types/client/styles.d.ts.map +1 -1
- package/lib/types/client/workbuddy/styles.d.ts.map +1 -1
- package/lib/types/host/antigravity/mapper.d.ts +3 -2
- package/lib/types/host/antigravity/mapper.d.ts.map +1 -1
- package/lib/types/host/antigravity/tool-schema.d.ts +13 -1
- package/lib/types/host/antigravity/tool-schema.d.ts.map +1 -1
- package/lib/types/host/claude/adapter.d.ts.map +1 -1
- package/lib/types/host/claude/mapper.d.ts +57 -3
- package/lib/types/host/claude/mapper.d.ts.map +1 -1
- package/lib/types/host/codex-fetch.d.ts.map +1 -1
- package/lib/types/host/command-code/adapter.d.ts.map +1 -1
- package/lib/types/host/common/context-budget-fetch.d.ts +7 -0
- package/lib/types/host/common/context-budget-fetch.d.ts.map +1 -0
- package/lib/types/host/fetch-address-policy.d.ts +2 -1
- package/lib/types/host/fetch-address-policy.d.ts.map +1 -1
- package/lib/types/host/fetch-configuration.d.ts +6 -0
- package/lib/types/host/fetch-configuration.d.ts.map +1 -0
- package/lib/types/host/kimi-code/adapter.d.ts.map +1 -1
- package/lib/types/host/kimi-code/mapper.d.ts.map +1 -1
- package/lib/types/host/minimax-code/adapter.d.ts.map +1 -1
- package/lib/types/host/routes.d.ts +2 -2
- package/lib/types/host/routes.d.ts.map +1 -1
- package/lib/types/host/search-provider-switcher.d.ts +2 -0
- package/lib/types/host/search-provider-switcher.d.ts.map +1 -1
- package/lib/types/host/workbuddy/adapter.d.ts.map +1 -1
- package/lib/types/index.d.ts +7 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/shared/contracts.d.ts +11 -0
- package/lib/types/shared/contracts.d.ts.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -2984,6 +2984,24 @@ var CodexAccountPool = class extends AccountPoolCore {
|
|
|
2984
2984
|
}
|
|
2985
2985
|
};
|
|
2986
2986
|
//#endregion
|
|
2987
|
+
//#region src/host/fetch-configuration.ts
|
|
2988
|
+
const DEFAULT_FETCH_MAX_BODY_CHARS = 1e5;
|
|
2989
|
+
const DEFAULT_FETCH_MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
|
|
2990
|
+
function positiveFetchLimit(value, fallback, name) {
|
|
2991
|
+
const resolved = value === void 0 ? fallback : value;
|
|
2992
|
+
if (!Number.isSafeInteger(resolved) || resolved < 1) throw new Error(`${name} must be a positive safe integer.`);
|
|
2993
|
+
return resolved;
|
|
2994
|
+
}
|
|
2995
|
+
function resolveFetchConfiguration(config) {
|
|
2996
|
+
const fetchProvider = config.fetchProvider ?? "auto";
|
|
2997
|
+
if (fetchProvider !== "auto" && fetchProvider !== "plugin" && fetchProvider !== "dsh") throw new Error("fetchProvider must be auto, plugin, or dsh.");
|
|
2998
|
+
return {
|
|
2999
|
+
fetchProvider,
|
|
3000
|
+
fetchMaxBodyChars: positiveFetchLimit(config.fetchMaxBodyChars, DEFAULT_FETCH_MAX_BODY_CHARS, "fetchMaxBodyChars"),
|
|
3001
|
+
fetchMaxResponseBytes: positiveFetchLimit(config.fetchMaxResponseBytes, DEFAULT_FETCH_MAX_RESPONSE_BYTES, "fetchMaxResponseBytes")
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
3004
|
+
//#endregion
|
|
2987
3005
|
//#region src/host/fetch-address-policy.ts
|
|
2988
3006
|
/**
|
|
2989
3007
|
* Destination policy for this plugin's fetch provider.
|
|
@@ -3001,7 +3019,8 @@ var CodexAccountPool = class extends AccountPoolCore {
|
|
|
3001
3019
|
* proxy on this machine must not become a path into loopback or a LAN — and it
|
|
3002
3020
|
* also refuses a name this machine resolves into private space. Only the proxy's
|
|
3003
3021
|
* own fake-ip answers are accepted, since that is what the proxy's name
|
|
3004
|
-
* resolution looks like from here
|
|
3022
|
+
* resolution looks like from here: the IPv4 one, and the IPv6 one only beside it
|
|
3023
|
+
* (see assertPublicFetchTarget). The URL hostname still reaches the proxy
|
|
3005
3024
|
* intact, so the proxy decides where the name really goes.
|
|
3006
3025
|
*
|
|
3007
3026
|
* @module dsh-chatgpt-subscription/fetch-address-policy
|
|
@@ -3114,11 +3133,22 @@ function isNonPublicIpLiteral(hostname) {
|
|
|
3114
3133
|
*/
|
|
3115
3134
|
function assertPublicFetchTarget(hostname, addresses) {
|
|
3116
3135
|
if (isNonPublicIpLiteral(hostname)) throw new WebError(`URL hostname "${hostname}" is not a public IP address`, "WEB_BLOCKED_URL");
|
|
3136
|
+
const claimedByProxy = addresses.some(isProxyFakeIpAddress);
|
|
3117
3137
|
for (const address of addresses) {
|
|
3118
3138
|
if (isPublicIpAddress(address) || isProxyFakeIpAddress(address)) continue;
|
|
3139
|
+
if (claimedByProxy && isUnassignedUniqueLocal(address)) continue;
|
|
3119
3140
|
throw new WebError(`URL hostname "${hostname}" resolves to a non-public IP address`, "WEB_BLOCKED_URL");
|
|
3120
3141
|
}
|
|
3121
3142
|
}
|
|
3143
|
+
/**
|
|
3144
|
+
* Whether an IPv6 address is inside fc00::/8: the unique-local half RFC 4193
|
|
3145
|
+
* leaves unassigned (real unique-local networks are numbered from fd00::/8), and
|
|
3146
|
+
* where proxies place their IPv6 fake-ip range.
|
|
3147
|
+
*/
|
|
3148
|
+
function isUnassignedUniqueLocal(address) {
|
|
3149
|
+
const words = ipv6Words(unbracket(address));
|
|
3150
|
+
return words !== void 0 && (words[0] ?? 0) >> 8 === 252;
|
|
3151
|
+
}
|
|
3122
3152
|
/** Whether a dotted quad is public, using {@link NON_PUBLIC_IPV4}. */
|
|
3123
3153
|
function isPublicIpv4(address) {
|
|
3124
3154
|
const value = ipv4Value(address);
|
|
@@ -3204,12 +3234,10 @@ function unbracket(hostname) {
|
|
|
3204
3234
|
}
|
|
3205
3235
|
//#endregion
|
|
3206
3236
|
//#region src/host/codex-fetch.ts
|
|
3207
|
-
const DEFAULT_MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
|
|
3208
|
-
const DEFAULT_MAX_BODY_CHARS = 1e5;
|
|
3209
3237
|
function createCodexFetchProvider(options = {}) {
|
|
3210
3238
|
const fetchFn = options.fetchFn ?? fetch;
|
|
3211
|
-
const maxResponseBytes = options.maxResponseBytes
|
|
3212
|
-
const maxBodyChars = options.maxBodyChars
|
|
3239
|
+
const maxResponseBytes = positiveFetchLimit(options.maxResponseBytes, DEFAULT_FETCH_MAX_RESPONSE_BYTES, "maxResponseBytes");
|
|
3240
|
+
const maxBodyChars = positiveFetchLimit(options.maxBodyChars, DEFAULT_FETCH_MAX_BODY_CHARS, "maxBodyChars");
|
|
3213
3241
|
const resolveHostAddresses = options.resolveHostAddresses ?? lookupHostAddresses;
|
|
3214
3242
|
return {
|
|
3215
3243
|
id: CODEX_FETCH_PROVIDER_ID,
|
|
@@ -7363,7 +7391,7 @@ function identityKey(credentials) {
|
|
|
7363
7391
|
//#endregion
|
|
7364
7392
|
//#region src/host/routes.ts
|
|
7365
7393
|
const MAX_BODY_BYTES$6 = 64 * 1024;
|
|
7366
|
-
function registerRoutes(ctx, oauth, usage, preferences, proxyManager, searchSwitcher, routeAudit, accountPool) {
|
|
7394
|
+
function registerRoutes(ctx, oauth, usage, preferences, proxyManager, searchSwitcher, routeAudit, accountPool, fetchConfiguration) {
|
|
7367
7395
|
const handler = async (request, response) => {
|
|
7368
7396
|
const url = new URL(request.url ?? "/", "http://dsh.local");
|
|
7369
7397
|
if (request.method === "GET" && url.pathname === `/api/dsh-chatgpt-subscription/status`) {
|
|
@@ -7378,7 +7406,8 @@ function registerRoutes(ctx, oauth, usage, preferences, proxyManager, searchSwit
|
|
|
7378
7406
|
preferences: preferences.status(),
|
|
7379
7407
|
detectedProxy: proxyManager?.getSystemProxy() ?? null,
|
|
7380
7408
|
activeProxy: proxyManager?.resolveActiveProxyUrl() ?? null,
|
|
7381
|
-
switcher: searchSwitcher?.status() ?? null
|
|
7409
|
+
switcher: searchSwitcher?.status() ?? null,
|
|
7410
|
+
fetchConfiguration
|
|
7382
7411
|
}
|
|
7383
7412
|
});
|
|
7384
7413
|
return;
|
|
@@ -7790,10 +7819,13 @@ var SearchProviderSwitcher = class {
|
|
|
7790
7819
|
}
|
|
7791
7820
|
const codexSelected = preference === SEARCH_PROVIDER_CODEX;
|
|
7792
7821
|
const nextSearch = codexSelected ? CODEX_SEARCH_PROVIDER_ID : this.originalSearchProvider;
|
|
7793
|
-
const nextFetch = codexSelected || options.pluginFetch === true ? CODEX_FETCH_PROVIDER_ID : this.originalFetchProvider;
|
|
7822
|
+
const nextFetch = options.fetchProvider === "plugin" || options.fetchProvider !== "dsh" && (codexSelected || options.pluginFetch === true) ? CODEX_FETCH_PROVIDER_ID : this.originalFetchProvider;
|
|
7794
7823
|
const configured = config.searchProvider === nextSearch && config.fetchProvider === nextFetch;
|
|
7795
7824
|
const applied = running === void 0 || running.searchProvider === nextSearch && running.fetchProvider === nextFetch;
|
|
7796
|
-
if (configured && applied)
|
|
7825
|
+
if (configured && applied) {
|
|
7826
|
+
this.state = "applied";
|
|
7827
|
+
return;
|
|
7828
|
+
}
|
|
7797
7829
|
const nextConfig = { ...config };
|
|
7798
7830
|
if (nextSearch === void 0) delete nextConfig.searchProvider;
|
|
7799
7831
|
else nextConfig.searchProvider = nextSearch;
|
|
@@ -8562,7 +8594,170 @@ function mergeSchemas(left, right) {
|
|
|
8562
8594
|
if (Array.isArray(left.required) && Array.isArray(right.required)) merged.required = [.../* @__PURE__ */ new Set([...left.required, ...right.required])];
|
|
8563
8595
|
return merged;
|
|
8564
8596
|
}
|
|
8565
|
-
|
|
8597
|
+
/** Value constraints a branch summary reports, in a stable order. */
|
|
8598
|
+
const constraintFields = [
|
|
8599
|
+
"format",
|
|
8600
|
+
"pattern",
|
|
8601
|
+
"minimum",
|
|
8602
|
+
"maximum",
|
|
8603
|
+
"minLength",
|
|
8604
|
+
"maxLength",
|
|
8605
|
+
"minItems",
|
|
8606
|
+
"maxItems",
|
|
8607
|
+
"minProperties",
|
|
8608
|
+
"maxProperties"
|
|
8609
|
+
];
|
|
8610
|
+
/** Keys the same-type union merge below represents rather than drops. */
|
|
8611
|
+
const mergedTypeKeys = /* @__PURE__ */ new Set([
|
|
8612
|
+
"type",
|
|
8613
|
+
"enum",
|
|
8614
|
+
"nullable",
|
|
8615
|
+
"description",
|
|
8616
|
+
...constraintFields
|
|
8617
|
+
]);
|
|
8618
|
+
/**
|
|
8619
|
+
* One alternative in the words a model can act on.
|
|
8620
|
+
*
|
|
8621
|
+
* Folding removes the branch from the schema itself, so this text carries the
|
|
8622
|
+
* branch description and its constraints too: the type alone would leave the
|
|
8623
|
+
* model with strictly less than the schema it started from.
|
|
8624
|
+
*/
|
|
8625
|
+
function describeBranch(branch) {
|
|
8626
|
+
const type = typeof branch.type === "string" ? branch.type : isRecord$22(branch.properties) ? "object" : isRecord$22(branch.items) ? "array" : "value";
|
|
8627
|
+
const details = [];
|
|
8628
|
+
if (isRecord$22(branch.properties)) {
|
|
8629
|
+
const names = Object.keys(branch.properties);
|
|
8630
|
+
if (names.length > 0) details.push(`{${names.join(",")}}`);
|
|
8631
|
+
}
|
|
8632
|
+
if (Array.isArray(branch.required) && branch.required.length > 0) details.push(`requires ${branch.required.map(String).join(",")}`);
|
|
8633
|
+
if (Array.isArray(branch.enum)) details.push(`enum[${branch.enum.map((value) => JSON.stringify(value)).join(",")}]`);
|
|
8634
|
+
for (const field of constraintFields) if (branch[field] !== void 0) details.push(`${field}=${JSON.stringify(branch[field])}`);
|
|
8635
|
+
if (branch.nullable === true) details.push("or null");
|
|
8636
|
+
const summary = details.length > 0 ? `${type} ${details.join(" ")}` : type;
|
|
8637
|
+
const description = typeof branch.description === "string" ? branch.description.trim() : "";
|
|
8638
|
+
return description === "" ? summary : `${summary} — "${description}"`;
|
|
8639
|
+
}
|
|
8640
|
+
/** How well one branch can stand in for a whole union; a named object shape wins. */
|
|
8641
|
+
function branchRank(branch) {
|
|
8642
|
+
if (isRecord$22(branch.properties)) return 3;
|
|
8643
|
+
if (branch.type === "object") return 2;
|
|
8644
|
+
if (branch.type === "array") return 1;
|
|
8645
|
+
return 0;
|
|
8646
|
+
}
|
|
8647
|
+
/**
|
|
8648
|
+
* The one JSON Schema type a literal set declares, when it declares exactly one.
|
|
8649
|
+
*
|
|
8650
|
+
* Read from the caller's literals, never from the wire's stringified `enum`: a
|
|
8651
|
+
* `{ const: 5 }` branch is a number, and `String(5)` alone cannot say so.
|
|
8652
|
+
*/
|
|
8653
|
+
function literalType(values) {
|
|
8654
|
+
const kinds = new Set(values.map((value) => typeof value));
|
|
8655
|
+
if (kinds.size !== 1) return void 0;
|
|
8656
|
+
const [kind] = kinds;
|
|
8657
|
+
return kind === "string" || kind === "number" || kind === "boolean" ? kind : void 0;
|
|
8658
|
+
}
|
|
8659
|
+
/**
|
|
8660
|
+
* The single form the wire can declare for a set of union branches.
|
|
8661
|
+
*
|
|
8662
|
+
* `folded` is false only when the declared form carries every constraint the
|
|
8663
|
+
* branches between them declared; once a constraint is only in the description,
|
|
8664
|
+
* the fold says so instead of passing a partial restatement off as the union.
|
|
8665
|
+
*/
|
|
8666
|
+
function unionShape(branches) {
|
|
8667
|
+
if (branches.length === 1) return {
|
|
8668
|
+
shape: branches[0],
|
|
8669
|
+
folded: false
|
|
8670
|
+
};
|
|
8671
|
+
if (new Set(branches.map((branch) => branch.type)).size === 1 && branches.every((branch) => !isRecord$22(branch.properties) && !isRecord$22(branch.items))) {
|
|
8672
|
+
const shape = { type: branches[0].type };
|
|
8673
|
+
if (branches.every((branch) => Array.isArray(branch.enum))) {
|
|
8674
|
+
const values = [...new Set(branches.flatMap((branch) => branch.enum))];
|
|
8675
|
+
if (values.length > 0) shape.enum = values;
|
|
8676
|
+
}
|
|
8677
|
+
if (branches.some((branch) => branch.nullable === true)) shape.nullable = true;
|
|
8678
|
+
return {
|
|
8679
|
+
shape,
|
|
8680
|
+
folded: branches.some((branch) => Object.entries(branch).some(([key, value]) => !mergedTypeKeys.has(key) && !Object.is(shape[key], value)))
|
|
8681
|
+
};
|
|
8682
|
+
}
|
|
8683
|
+
return {
|
|
8684
|
+
shape: branches.reduce((best, branch) => branchRank(branch) > branchRank(best) ? branch : best),
|
|
8685
|
+
folded: true
|
|
8686
|
+
};
|
|
8687
|
+
}
|
|
8688
|
+
/** Append the folded union to whatever description the node already carried. */
|
|
8689
|
+
function withUnionHint(description, hint) {
|
|
8690
|
+
const base = typeof description === "string" ? description.trim() : "";
|
|
8691
|
+
return base === "" ? hint : `${base}\n\n${hint}`;
|
|
8692
|
+
}
|
|
8693
|
+
function unionHint(alternatives, declared) {
|
|
8694
|
+
const text = `Union of accepted forms: ${alternatives.map(describeBranch).join(" | ")}.`;
|
|
8695
|
+
return declared === void 0 ? text : `${text} Send "${describeBranch(declared)}": this gateway validates the declared form only, and the alternatives describe the original tool contract.`;
|
|
8696
|
+
}
|
|
8697
|
+
/**
|
|
8698
|
+
* One node's own fields, children folded, composition keys set aside.
|
|
8699
|
+
*
|
|
8700
|
+
* `anyOf`, `oneOf` and `allOf` are keywords only in these positions: a parameter
|
|
8701
|
+
* NAMED `anyOf` is data, so recursion goes through `properties` by name and never
|
|
8702
|
+
* reads a property key as a keyword.
|
|
8703
|
+
*/
|
|
8704
|
+
function foldOwnFields(node) {
|
|
8705
|
+
const out = {};
|
|
8706
|
+
for (const [key, value] of Object.entries(node)) {
|
|
8707
|
+
if (key === "anyOf" || key === "oneOf" || key === "allOf") continue;
|
|
8708
|
+
if (key === "properties" && isRecord$22(value)) {
|
|
8709
|
+
out.properties = Object.fromEntries(Object.entries(value).map(([name, child]) => [name, foldSchemaUnions(child)]));
|
|
8710
|
+
continue;
|
|
8711
|
+
}
|
|
8712
|
+
if (key === "items") {
|
|
8713
|
+
out.items = foldSchemaUnions(value);
|
|
8714
|
+
continue;
|
|
8715
|
+
}
|
|
8716
|
+
out[key] = value;
|
|
8717
|
+
}
|
|
8718
|
+
return out;
|
|
8719
|
+
}
|
|
8720
|
+
/**
|
|
8721
|
+
* Replace every composition keyword in one already-converted schema with a
|
|
8722
|
+
* single declared form, moving what folding drops into `description`.
|
|
8723
|
+
*
|
|
8724
|
+
* This runs after {@link toAntigravityToolSchema} resolved references and merged
|
|
8725
|
+
* `allOf`, so it is one pass over the wire shape and no second sanitizer can
|
|
8726
|
+
* strip the hint it just wrote.
|
|
8727
|
+
*
|
|
8728
|
+
* Folding NARROWS the wire contract: preferring the object branch leaves the
|
|
8729
|
+
* schema declaring an object, so a caller that would have sent the string form
|
|
8730
|
+
* now sends against a schema that does not mention it. That is the only way
|
|
8731
|
+
* past a gateway that refuses a composed schema outright, and the description
|
|
8732
|
+
* keeps the dropped forms visible as the original tool contract rather than
|
|
8733
|
+
* silently erasing them. DSH's own tool validator is the real gate: an
|
|
8734
|
+
* argument the model sends anyway fails there, naming the actual problem.
|
|
8735
|
+
*/
|
|
8736
|
+
function foldSchemaUnions(node) {
|
|
8737
|
+
if (!isRecord$22(node)) return node;
|
|
8738
|
+
let own = foldOwnFields(node);
|
|
8739
|
+
if (Array.isArray(node.allOf)) for (const branch of node.allOf) {
|
|
8740
|
+
const folded = foldSchemaUnions(branch);
|
|
8741
|
+
if (isRecord$22(folded)) own = mergeSchemas(own, folded);
|
|
8742
|
+
}
|
|
8743
|
+
const alternatives = Array.isArray(node.anyOf) ? node.anyOf : Array.isArray(node.oneOf) ? node.oneOf : [];
|
|
8744
|
+
if (alternatives.length === 0) return own;
|
|
8745
|
+
const branches = alternatives.map((branch) => foldSchemaUnions(branch)).filter(isRecord$22);
|
|
8746
|
+
if (branches.length === 0) return own;
|
|
8747
|
+
const shapes = branches.filter((branch) => branch.type !== "null");
|
|
8748
|
+
const acceptsNull = branches.some((branch) => branch.type === "null");
|
|
8749
|
+
if (shapes.length === 0) {
|
|
8750
|
+
const onlyNull = mergeSchemas({ nullable: true }, own);
|
|
8751
|
+
onlyNull.description = withUnionHint(own.description, unionHint(branches, { nullable: true }));
|
|
8752
|
+
return onlyNull;
|
|
8753
|
+
}
|
|
8754
|
+
const { shape, folded } = unionShape(shapes);
|
|
8755
|
+
const merged = mergeSchemas(shape, own);
|
|
8756
|
+
if (acceptsNull) merged.nullable = true;
|
|
8757
|
+
if (branches.length > 1) merged.description = withUnionHint(merged.description, unionHint(branches, folded ? shape : void 0));
|
|
8758
|
+
return merged;
|
|
8759
|
+
}
|
|
8760
|
+
function toAntigravityToolSchema(schema, options = {}) {
|
|
8566
8761
|
if (!isRecord$22(schema)) return schema;
|
|
8567
8762
|
const root = schema;
|
|
8568
8763
|
function resolveReference(reference) {
|
|
@@ -8605,9 +8800,17 @@ function toAntigravityToolSchema(schema) {
|
|
|
8605
8800
|
"boolean"
|
|
8606
8801
|
].includes(typeof node.const)) out.enum = [String(node.const)];
|
|
8607
8802
|
} else if (Array.isArray(node.enum)) out.enum = node.enum.map((value) => String(value));
|
|
8803
|
+
if (options.foldUnions && node.type === void 0 && !Array.isArray(node.type)) {
|
|
8804
|
+
const values = Object.hasOwn(node, "const") ? [node.const] : node.enum;
|
|
8805
|
+
if (Array.isArray(values) && values.length > 0) {
|
|
8806
|
+
const literal = literalType(values);
|
|
8807
|
+
if (literal !== void 0) out.type = literal;
|
|
8808
|
+
}
|
|
8809
|
+
}
|
|
8608
8810
|
return mergeSchemas(inherited, out);
|
|
8609
8811
|
}
|
|
8610
|
-
|
|
8812
|
+
const converted = convert(root, /* @__PURE__ */ new Set());
|
|
8813
|
+
return options.foldUnions === true ? foldSchemaUnions(converted) : converted;
|
|
8611
8814
|
}
|
|
8612
8815
|
//#endregion
|
|
8613
8816
|
//#region src/host/antigravity/mapper.ts
|
|
@@ -9018,15 +9221,27 @@ function convertMessages(options, model, runtimeModel, images = NO_RESOLVED_IMAG
|
|
|
9018
9221
|
}
|
|
9019
9222
|
return contents;
|
|
9020
9223
|
}
|
|
9021
|
-
function stripMetaSchema$3(schema) {
|
|
9022
|
-
return toAntigravityToolSchema(schema);
|
|
9224
|
+
function stripMetaSchema$3(schema, options) {
|
|
9225
|
+
return toAntigravityToolSchema(schema, options);
|
|
9226
|
+
}
|
|
9227
|
+
/**
|
|
9228
|
+
* Whether this request's tool declarations must declare one form per parameter.
|
|
9229
|
+
*
|
|
9230
|
+
* Antigravity routes a Claude-family runtime model to Vertex Anthropic, whose
|
|
9231
|
+
* `input_schema` validation rejects a request whose tool schema still composes
|
|
9232
|
+
* itself with `anyOf`/`oneOf`/`allOf` (#39). Gemini ignores `anyOf`, so its
|
|
9233
|
+
* declarations keep the composed form they always sent.
|
|
9234
|
+
*/
|
|
9235
|
+
function foldsToolSchemaUnions(modelId, runtimeModel) {
|
|
9236
|
+
return modelId.startsWith("claude-") || runtimeModel.startsWith("claude-");
|
|
9023
9237
|
}
|
|
9024
|
-
function convertTools(tools) {
|
|
9238
|
+
function convertTools(tools, modelId = "", runtimeModel = "") {
|
|
9025
9239
|
if (!tools || tools.length === 0) return void 0;
|
|
9240
|
+
const foldUnions = foldsToolSchemaUnions(modelId, runtimeModel);
|
|
9026
9241
|
return [{ functionDeclarations: tools.map((tool) => ({
|
|
9027
9242
|
name: tool.name,
|
|
9028
9243
|
description: tool.description || "",
|
|
9029
|
-
parameters: stripMetaSchema$3(tool.parameters) || {
|
|
9244
|
+
parameters: stripMetaSchema$3(tool.parameters, { foldUnions }) || {
|
|
9030
9245
|
type: "object",
|
|
9031
9246
|
properties: {}
|
|
9032
9247
|
}
|
|
@@ -9087,7 +9302,7 @@ function buildRequest$2(options, model, projectId, runtimeModel, effort, images
|
|
|
9087
9302
|
generationConfig.maxOutputTokens = options.maxTokens !== void 0 ? Math.min(options.maxTokens, maxAllowed) : maxAllowed;
|
|
9088
9303
|
request.generationConfig = generationConfig;
|
|
9089
9304
|
const toolChoice = options.toolChoice;
|
|
9090
|
-
const tools = convertTools(options.tools);
|
|
9305
|
+
const tools = convertTools(options.tools, model.id, runtimeModel);
|
|
9091
9306
|
if (tools) {
|
|
9092
9307
|
request.tools = tools;
|
|
9093
9308
|
if (toolChoice) request.toolConfig = { functionCallingConfig: { mode: mapToolChoiceMode(toolChoice) } };
|
|
@@ -9979,6 +10194,103 @@ function registerAntigravityRoutes(ctx, store, modelSettings, preferences, fetch
|
|
|
9979
10194
|
});
|
|
9980
10195
|
}
|
|
9981
10196
|
//#endregion
|
|
10197
|
+
//#region src/host/common/context-budget-fetch.ts
|
|
10198
|
+
/**
|
|
10199
|
+
* Recover a rejected input+output budget without dropping model-visible history.
|
|
10200
|
+
* Only explicit wire output controls and provider-confirmed counts qualify.
|
|
10201
|
+
* HTTP success/SSE, auth, quota, and byte-limit failures retain their old paths.
|
|
10202
|
+
*/
|
|
10203
|
+
function fetchWithContextBudgetRecovery(fetchFn) {
|
|
10204
|
+
return async (input, init) => {
|
|
10205
|
+
const response = await fetchFn(input, init);
|
|
10206
|
+
if (response.status !== 400 && response.status !== 422) return response;
|
|
10207
|
+
if (typeof init?.body !== "string") return response;
|
|
10208
|
+
let body;
|
|
10209
|
+
try {
|
|
10210
|
+
const parsed = JSON.parse(init.body);
|
|
10211
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return response;
|
|
10212
|
+
body = parsed;
|
|
10213
|
+
} catch {
|
|
10214
|
+
return response;
|
|
10215
|
+
}
|
|
10216
|
+
const keys = [
|
|
10217
|
+
"max_tokens",
|
|
10218
|
+
"max_completion_tokens",
|
|
10219
|
+
"max_output_tokens"
|
|
10220
|
+
].filter((key) => key in body);
|
|
10221
|
+
if (keys.length !== 1) return response;
|
|
10222
|
+
const key = keys[0];
|
|
10223
|
+
const requested = body[key];
|
|
10224
|
+
if (typeof requested !== "number" || !Number.isSafeInteger(requested) || requested <= 0) return response;
|
|
10225
|
+
const reduced = reducedOutputBudget(await boundedDiagnostic(response), requested);
|
|
10226
|
+
if (reduced === void 0) return response;
|
|
10227
|
+
const thinking = body.thinking;
|
|
10228
|
+
if (typeof thinking === "object" && thinking !== null) {
|
|
10229
|
+
const budget = thinking.budget_tokens;
|
|
10230
|
+
if (typeof budget === "number" && reduced <= budget) return response;
|
|
10231
|
+
}
|
|
10232
|
+
init.signal?.throwIfAborted();
|
|
10233
|
+
await response.body?.cancel().catch(() => void 0);
|
|
10234
|
+
return fetchFn(input, {
|
|
10235
|
+
...init,
|
|
10236
|
+
body: JSON.stringify({
|
|
10237
|
+
...body,
|
|
10238
|
+
[key]: reduced
|
|
10239
|
+
})
|
|
10240
|
+
});
|
|
10241
|
+
};
|
|
10242
|
+
}
|
|
10243
|
+
/** Read a bounded clone so a non-recoverable refusal remains readable by its owner. */
|
|
10244
|
+
async function boundedDiagnostic(response) {
|
|
10245
|
+
const reader = response.clone().body?.getReader();
|
|
10246
|
+
if (reader === void 0) return "";
|
|
10247
|
+
const decoder = new TextDecoder();
|
|
10248
|
+
let size = 0;
|
|
10249
|
+
let text = "";
|
|
10250
|
+
try {
|
|
10251
|
+
for (;;) {
|
|
10252
|
+
const { done, value } = await reader.read();
|
|
10253
|
+
if (done) return text + decoder.decode();
|
|
10254
|
+
size += value.byteLength;
|
|
10255
|
+
if (size > 16384) return "";
|
|
10256
|
+
text += decoder.decode(value, { stream: true });
|
|
10257
|
+
}
|
|
10258
|
+
} catch {
|
|
10259
|
+
return "";
|
|
10260
|
+
} finally {
|
|
10261
|
+
reader.cancel().catch(() => void 0);
|
|
10262
|
+
}
|
|
10263
|
+
}
|
|
10264
|
+
function diagnosticMessage(value, depth = 0) {
|
|
10265
|
+
if (depth > 4) return void 0;
|
|
10266
|
+
if (typeof value === "string") return value;
|
|
10267
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
10268
|
+
const fields = value;
|
|
10269
|
+
return diagnosticMessage(fields.error, depth + 1) ?? diagnosticMessage(fields.message, depth + 1);
|
|
10270
|
+
}
|
|
10271
|
+
/** Never infer capacity from a model name or from an echoed request. */
|
|
10272
|
+
function reducedOutputBudget(detail, requested) {
|
|
10273
|
+
let message;
|
|
10274
|
+
try {
|
|
10275
|
+
message = diagnosticMessage(JSON.parse(detail));
|
|
10276
|
+
} catch {
|
|
10277
|
+
message = detail;
|
|
10278
|
+
}
|
|
10279
|
+
if (message === void 0) return void 0;
|
|
10280
|
+
const match = /maximum context length is ([\d,]+) tokens[\s\S]*?requested ([\d,]+) tokens[\s\S]*?\(([\d,]+) in the messages, ([\d,]+) in the completion\)/i.exec(message);
|
|
10281
|
+
if (match === null) return void 0;
|
|
10282
|
+
const [window, total, prompt, completion] = match.slice(1).map((value) => Number(value.replaceAll(",", "")));
|
|
10283
|
+
if (![
|
|
10284
|
+
window,
|
|
10285
|
+
total,
|
|
10286
|
+
prompt,
|
|
10287
|
+
completion
|
|
10288
|
+
].every((value) => Number.isSafeInteger(value) && value > 0)) return void 0;
|
|
10289
|
+
if (completion !== requested || prompt + completion !== total || total <= window) return void 0;
|
|
10290
|
+
const available = window - prompt - 1024;
|
|
10291
|
+
return available >= 1024 && available < requested ? available : void 0;
|
|
10292
|
+
}
|
|
10293
|
+
//#endregion
|
|
9982
10294
|
//#region src/host/command-code/model-catalog.ts
|
|
9983
10295
|
/** Every model the Command Code registry describes, in registry order. */
|
|
9984
10296
|
const COMMAND_CODE_MODELS = [
|
|
@@ -14092,7 +14404,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
14092
14404
|
"anthropic-version": "2023-06-01"
|
|
14093
14405
|
} : baseHeaders;
|
|
14094
14406
|
try {
|
|
14095
|
-
response = await fetchFn(endpoint, {
|
|
14407
|
+
response = await fetchWithContextBudgetRecovery(fetchFn)(endpoint, {
|
|
14096
14408
|
method: "POST",
|
|
14097
14409
|
headers,
|
|
14098
14410
|
body,
|
|
@@ -19923,7 +20235,20 @@ function estimatedInputTokens(options) {
|
|
|
19923
20235
|
characters += JSON.stringify(tool.parameters).length;
|
|
19924
20236
|
} catch {}
|
|
19925
20237
|
}
|
|
19926
|
-
|
|
20238
|
+
const contentCharacters = (content) => {
|
|
20239
|
+
if (!Array.isArray(content)) return typeof content === "string" ? content.length : 0;
|
|
20240
|
+
return content.reduce((total, block) => {
|
|
20241
|
+
if (!isRecord$16(block)) return total;
|
|
20242
|
+
if ((block.type === "text" || block.type === "reasoning") && typeof block.text === "string") return total + block.text.length;
|
|
20243
|
+
if (block.type === "tool-result") return total + contentCharacters(block.content);
|
|
20244
|
+
if (block.type === "tool-call") {
|
|
20245
|
+
const args = toolCallArguments$3(block.arguments);
|
|
20246
|
+
return total + String(block.name ?? "").length + args.length;
|
|
20247
|
+
}
|
|
20248
|
+
return total;
|
|
20249
|
+
}, 0);
|
|
20250
|
+
};
|
|
20251
|
+
for (const message of options.messages) characters += contentCharacters(message.content);
|
|
19927
20252
|
if (characters === 0) return void 0;
|
|
19928
20253
|
return Math.ceil(characters / 4);
|
|
19929
20254
|
}
|
|
@@ -21082,7 +21407,7 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
21082
21407
|
const endpoint = wire === "anthropic" ? `${base}/v1/messages?beta=true` : `${base}/v1/chat/completions`;
|
|
21083
21408
|
const headers = await modelRequestHeaders$1(apiCredentials.accessToken, wire);
|
|
21084
21409
|
try {
|
|
21085
|
-
response = await fetchFn(endpoint, {
|
|
21410
|
+
response = await fetchWithContextBudgetRecovery(fetchFn)(endpoint, {
|
|
21086
21411
|
method: "POST",
|
|
21087
21412
|
headers,
|
|
21088
21413
|
body,
|
|
@@ -25799,7 +26124,7 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
|
|
|
25799
26124
|
let response;
|
|
25800
26125
|
for (let attempt = 0;; attempt += 1) {
|
|
25801
26126
|
try {
|
|
25802
|
-
response = await fetchFn(messagesUrl(credentials.region), {
|
|
26127
|
+
response = await fetchWithContextBudgetRecovery(fetchFn)(messagesUrl(credentials.region), {
|
|
25803
26128
|
method: "POST",
|
|
25804
26129
|
headers: modelRequestHeaders(credentials.accessToken),
|
|
25805
26130
|
body,
|
|
@@ -30539,7 +30864,7 @@ var WorkBuddyAdapter = class extends LlmAdapter {
|
|
|
30539
30864
|
credentials = effective.credentials;
|
|
30540
30865
|
}
|
|
30541
30866
|
try {
|
|
30542
|
-
response = await fetchFn(`${credentials.backend}${CHAT_PATH}`, {
|
|
30867
|
+
response = await fetchWithContextBudgetRecovery(fetchFn)(`${credentials.backend}${CHAT_PATH}`, {
|
|
30543
30868
|
method: "POST",
|
|
30544
30869
|
headers: workBuddyHeaders(credentials, { accept: "text/event-stream" }),
|
|
30545
30870
|
body,
|
|
@@ -34202,14 +34527,94 @@ function offloadOldestRequestImages(options) {
|
|
|
34202
34527
|
};
|
|
34203
34528
|
}
|
|
34204
34529
|
/**
|
|
34530
|
+
* Longest side, in pixels, the wire accepts for one image in any request.
|
|
34531
|
+
* TRANSCRIBED from the API's vision limits. The harness normalizes stored
|
|
34532
|
+
* images to an 8192 px long edge, so a stored image can still exceed this.
|
|
34533
|
+
*/
|
|
34534
|
+
const MAX_IMAGE_EDGE = 8e3;
|
|
34535
|
+
/**
|
|
34536
|
+
* Longest side, in pixels, the wire accepts for every image once one request
|
|
34537
|
+
* carries more than {@link MANY_IMAGE_THRESHOLD} images. TRANSCRIBED: past that
|
|
34538
|
+
* count the request is rejected with "At least one of the image dimensions
|
|
34539
|
+
* exceed max allowed size for many-image requests: 2000 pixels".
|
|
34540
|
+
*
|
|
34541
|
+
* Rejection is a 400 for the whole request. History keeps every image, so a
|
|
34542
|
+
* long session can only add images. Once it holds more than twenty and one is
|
|
34543
|
+
* wider than 2000 px, which is common for side-by-side screenshots, every later
|
|
34544
|
+
* turn fails. The session cannot recover by itself.
|
|
34545
|
+
*/
|
|
34546
|
+
const MANY_IMAGE_EDGE = 2e3;
|
|
34547
|
+
/**
|
|
34548
|
+
* Encoded-byte target for one downscaled request version.
|
|
34549
|
+
*
|
|
34550
|
+
* CHOICE: 3.5 MiB of raw bytes is about 4.9 MB of base64. That stays under the
|
|
34551
|
+
* 5 MB per-image ceiling whichever form the server measures. When no quality
|
|
34552
|
+
* level meets the target, the harness keeps its smallest output.
|
|
34553
|
+
*/
|
|
34554
|
+
const REQUEST_IMAGE_VERSION_MAX_BYTES = 3.5 * 1024 * 1024;
|
|
34555
|
+
function countRequestImages(content) {
|
|
34556
|
+
if (!Array.isArray(content)) return 0;
|
|
34557
|
+
let count = 0;
|
|
34558
|
+
for (const block of content) {
|
|
34559
|
+
if (!isRecord$5(block)) continue;
|
|
34560
|
+
if (block.type === "image") count += 1;
|
|
34561
|
+
else if (block.type === "tool-result") count += countRequestImages(block.content);
|
|
34562
|
+
}
|
|
34563
|
+
return count;
|
|
34564
|
+
}
|
|
34565
|
+
/**
|
|
34566
|
+
* Long-edge ceiling every image of this request must meet.
|
|
34567
|
+
*
|
|
34568
|
+
* The API counts image blocks, so a repeated attachment counts every time it
|
|
34569
|
+
* appears. The count is taken before images are resolved: a block that later
|
|
34570
|
+
* degrades to placeholder text still counts. Overcounting can only lower the
|
|
34571
|
+
* ceiling, and a lower ceiling never causes a rejection.
|
|
34572
|
+
*/
|
|
34573
|
+
function requestImageEdgeLimit(options) {
|
|
34574
|
+
let count = 0;
|
|
34575
|
+
for (const message of options.messages) count += countRequestImages(message.content);
|
|
34576
|
+
return count > 20 ? MANY_IMAGE_EDGE : MAX_IMAGE_EDGE;
|
|
34577
|
+
}
|
|
34578
|
+
/**
|
|
34579
|
+
* Request-version target for one stored image whose long edge is over the
|
|
34580
|
+
* ceiling, or undefined when it fits. The short edge rounds to the nearest
|
|
34581
|
+
* pixel, matching the harness's long-edge resize. A reference without usable
|
|
34582
|
+
* dimensions is sent as stored.
|
|
34583
|
+
*/
|
|
34584
|
+
function requestImageEdgeTarget(ref, edge) {
|
|
34585
|
+
const { width, height } = ref;
|
|
34586
|
+
if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width <= 0 || height <= 0) return void 0;
|
|
34587
|
+
if (Math.max(width, height) <= edge) return void 0;
|
|
34588
|
+
const maxPixels = Math.max(1, Math.floor(edge * edge * (Math.min(width, height) / Math.max(width, height))));
|
|
34589
|
+
return width >= height ? {
|
|
34590
|
+
width: edge,
|
|
34591
|
+
height: Math.max(1, Math.round(edge * height / width)),
|
|
34592
|
+
maxPixels,
|
|
34593
|
+
maxBytes: REQUEST_IMAGE_VERSION_MAX_BYTES
|
|
34594
|
+
} : {
|
|
34595
|
+
width: Math.max(1, Math.round(edge * width / height)),
|
|
34596
|
+
height: edge,
|
|
34597
|
+
maxPixels,
|
|
34598
|
+
maxBytes: REQUEST_IMAGE_VERSION_MAX_BYTES
|
|
34599
|
+
};
|
|
34600
|
+
}
|
|
34601
|
+
/**
|
|
34205
34602
|
* Read every durable { type: 'image', attachment } block one request carries.
|
|
34206
34603
|
* An unreadable image resolves to 'unavailable' rather than disappearing, so
|
|
34207
34604
|
* the model is told the picture is missing instead of answering about a blank.
|
|
34605
|
+
*
|
|
34606
|
+
* An image whose long edge is over this request's ceiling is sent as the
|
|
34607
|
+
* harness's downscaled request version (see {@link requestImageEdgeLimit}). The
|
|
34608
|
+
* stored image and durable history do not change, and an image that already
|
|
34609
|
+
* fits is sent byte-for-byte as stored. If a downscaled version cannot be
|
|
34610
|
+
* produced, the image resolves to 'unavailable'. A visible placeholder is better
|
|
34611
|
+
* than sending the original, which would make the whole request fail.
|
|
34208
34612
|
*/
|
|
34209
34613
|
async function resolveRequestImages(options, attachments, signal) {
|
|
34210
34614
|
const refs = /* @__PURE__ */ new Map();
|
|
34211
34615
|
for (const message of options.messages) collectImageRefs(message.content, refs);
|
|
34212
34616
|
if (refs.size === 0) return NO_RESOLVED_IMAGES;
|
|
34617
|
+
const edge = requestImageEdgeLimit(options);
|
|
34213
34618
|
const resolved = /* @__PURE__ */ new Map();
|
|
34214
34619
|
await Promise.all([...refs].map(async ([attachmentId, ref]) => {
|
|
34215
34620
|
if (!attachments) {
|
|
@@ -34217,6 +34622,16 @@ async function resolveRequestImages(options, attachments, signal) {
|
|
|
34217
34622
|
return;
|
|
34218
34623
|
}
|
|
34219
34624
|
try {
|
|
34625
|
+
const target = requestImageEdgeTarget(ref, edge);
|
|
34626
|
+
if (target !== void 0) {
|
|
34627
|
+
const version = await attachments.readImageRequest(ref, target, signal);
|
|
34628
|
+
resolved.set(attachmentId, Math.max(version.width, version.height) > edge ? { kind: "unavailable" } : {
|
|
34629
|
+
kind: "inline",
|
|
34630
|
+
mediaType: version.mediaType,
|
|
34631
|
+
data: Buffer.from(version.data).toString("base64")
|
|
34632
|
+
});
|
|
34633
|
+
return;
|
|
34634
|
+
}
|
|
34220
34635
|
const stored = await attachments.readImage(ref, signal);
|
|
34221
34636
|
resolved.set(attachmentId, {
|
|
34222
34637
|
kind: "inline",
|
|
@@ -36687,7 +37102,7 @@ var ClaudeAdapter = class ClaudeAdapter extends LlmAdapter {
|
|
|
36687
37102
|
cacheTtl
|
|
36688
37103
|
});
|
|
36689
37104
|
try {
|
|
36690
|
-
return await fetchFn("https://api.anthropic.com/v1/messages?beta=true", {
|
|
37105
|
+
return await fetchWithContextBudgetRecovery(fetchFn)("https://api.anthropic.com/v1/messages?beta=true", {
|
|
36691
37106
|
method: "POST",
|
|
36692
37107
|
headers: buildClaudeHeaders(credentials.accessToken, {
|
|
36693
37108
|
model: requestOptions.model,
|
|
@@ -42055,6 +42470,13 @@ function releaseListener(handle) {
|
|
|
42055
42470
|
//#endregion
|
|
42056
42471
|
//#region src/index.ts
|
|
42057
42472
|
const Config = z.object({
|
|
42473
|
+
fetchProvider: z.union([
|
|
42474
|
+
z.const("auto"),
|
|
42475
|
+
z.const("plugin"),
|
|
42476
|
+
z.const("dsh")
|
|
42477
|
+
]).default("auto"),
|
|
42478
|
+
fetchMaxBodyChars: z.number().min(1).max(Number.MAX_SAFE_INTEGER).step(1).default(DEFAULT_FETCH_MAX_BODY_CHARS),
|
|
42479
|
+
fetchMaxResponseBytes: z.number().min(1).max(Number.MAX_SAFE_INTEGER).step(1).default(DEFAULT_FETCH_MAX_RESPONSE_BYTES),
|
|
42058
42480
|
syncAgentPresets: z.boolean().default(true),
|
|
42059
42481
|
subagentModelAuthorization: z.boolean().default(true),
|
|
42060
42482
|
subagentModelTools: z.array(z.string()).default([]),
|
|
@@ -42071,6 +42493,7 @@ const inject = [
|
|
|
42071
42493
|
"loader"
|
|
42072
42494
|
];
|
|
42073
42495
|
function apply(ctx, pluginConfig = {}) {
|
|
42496
|
+
const fetchConfiguration = resolveFetchConfiguration(pluginConfig);
|
|
42074
42497
|
if (pluginConfig.syncAgentPresets !== false) ctx.effect(() => installBundledPresets(ctx, installDispatchPreset(ctx)), "dsh-chatgpt-subscription: agent preset sync");
|
|
42075
42498
|
const store = createPlatformTokenStore();
|
|
42076
42499
|
const preferences = registerPreferenceStore(ctx.settings);
|
|
@@ -42407,9 +42830,22 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
42407
42830
|
signal: options?.signal
|
|
42408
42831
|
})).catch(() => []));
|
|
42409
42832
|
const searchSwitcher = new SearchProviderSwitcher(ctx.loader);
|
|
42833
|
+
let lastFetchDiagnostic;
|
|
42410
42834
|
const applyWebProviders = (current = preferences.status()) => {
|
|
42411
42835
|
const pluginFetch = proxyManager.resolveActiveProxyUrl() !== null;
|
|
42412
|
-
searchSwitcher.select(current.searchProvider, {
|
|
42836
|
+
searchSwitcher.select(current.searchProvider, {
|
|
42837
|
+
pluginFetch,
|
|
42838
|
+
fetchProvider: fetchConfiguration.fetchProvider
|
|
42839
|
+
}).then(() => {
|
|
42840
|
+
const status = searchSwitcher.status();
|
|
42841
|
+
if (status.state === "missing" || status.state === "failed") return;
|
|
42842
|
+
const reason = fetchConfiguration.fetchProvider !== "auto" ? "explicit configuration" : current.searchProvider === "codex" ? "Codex search selected" : pluginFetch ? "active proxy detected" : "no proxy or Codex search";
|
|
42843
|
+
const diagnostic = `${status.configuredFetchProvider ?? "DSH default"} (mode=${fetchConfiguration.fetchProvider}; ${reason}; plugin limits=${fetchConfiguration.fetchMaxBodyChars} chars/${fetchConfiguration.fetchMaxResponseBytes} bytes)`;
|
|
42844
|
+
if (diagnostic !== lastFetchDiagnostic) {
|
|
42845
|
+
lastFetchDiagnostic = diagnostic;
|
|
42846
|
+
ctx.logger.info(`[dsh-chatgpt-subscription] Web fetch provider configured: ${diagnostic}`);
|
|
42847
|
+
}
|
|
42848
|
+
}).catch((error) => {
|
|
42413
42849
|
ctx.logger.warn(`[dsh-chatgpt-subscription] Web provider selection could not be applied: ${error instanceof Error ? error.message : String(error)}`);
|
|
42414
42850
|
});
|
|
42415
42851
|
};
|
|
@@ -42435,13 +42871,17 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
42435
42871
|
}))
|
|
42436
42872
|
});
|
|
42437
42873
|
};
|
|
42438
|
-
const disposeRoutes = registerRoutes(ctx, oauth, usage, preferences, proxyManager, searchSwitcher, readRouteAudit, codexAccountPool);
|
|
42874
|
+
const disposeRoutes = registerRoutes(ctx, oauth, usage, preferences, proxyManager, searchSwitcher, readRouteAudit, codexAccountPool, fetchConfiguration);
|
|
42439
42875
|
const disposeAdapter = ctx.llm.registerAdapter([PROVIDER_ID$7], adapter);
|
|
42440
42876
|
const disposeImageTool = ctx.tools.register(createCodexImageTool(oauth, ctx.attachments, { fetchFn: proxyFetch }));
|
|
42441
42877
|
const disposeVideoTool = ctx.tools.register(createKimiVideoTool(ctx, { fetchFn: proxyFetch }));
|
|
42442
42878
|
ctx.inject(["web"], (ctx) => {
|
|
42443
42879
|
ctx.web.registerSearchProvider(createCodexSearchProvider(oauth, { fetchFn: proxyFetch }));
|
|
42444
|
-
ctx.web.registerFetchProvider(createCodexFetchProvider({
|
|
42880
|
+
ctx.web.registerFetchProvider(createCodexFetchProvider({
|
|
42881
|
+
fetchFn: proxyFetch,
|
|
42882
|
+
maxBodyChars: fetchConfiguration.fetchMaxBodyChars,
|
|
42883
|
+
maxResponseBytes: fetchConfiguration.fetchMaxResponseBytes
|
|
42884
|
+
}));
|
|
42445
42885
|
setTimeout(applyWebProviders, 0);
|
|
42446
42886
|
});
|
|
42447
42887
|
const disposePreferenceWatch = preferences.watch((next) => applyWebProviders(next));
|