@eddyskywalker/dsh-chatgpt-subscription 0.11.7 → 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/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; the URL hostname still reaches the proxy
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 ?? DEFAULT_MAX_RESPONSE_BYTES;
3212
- const maxBodyChars = options.maxBodyChars ?? DEFAULT_MAX_BODY_CHARS;
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) return;
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
- function toAntigravityToolSchema(schema) {
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
- return convert(root, /* @__PURE__ */ new Set());
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);
9023
9226
  }
9024
- function convertTools(tools) {
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-");
9237
+ }
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) } };
@@ -34312,14 +34527,94 @@ function offloadOldestRequestImages(options) {
34312
34527
  };
34313
34528
  }
34314
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
+ /**
34315
34602
  * Read every durable { type: 'image', attachment } block one request carries.
34316
34603
  * An unreadable image resolves to 'unavailable' rather than disappearing, so
34317
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.
34318
34612
  */
34319
34613
  async function resolveRequestImages(options, attachments, signal) {
34320
34614
  const refs = /* @__PURE__ */ new Map();
34321
34615
  for (const message of options.messages) collectImageRefs(message.content, refs);
34322
34616
  if (refs.size === 0) return NO_RESOLVED_IMAGES;
34617
+ const edge = requestImageEdgeLimit(options);
34323
34618
  const resolved = /* @__PURE__ */ new Map();
34324
34619
  await Promise.all([...refs].map(async ([attachmentId, ref]) => {
34325
34620
  if (!attachments) {
@@ -34327,6 +34622,16 @@ async function resolveRequestImages(options, attachments, signal) {
34327
34622
  return;
34328
34623
  }
34329
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
+ }
34330
34635
  const stored = await attachments.readImage(ref, signal);
34331
34636
  resolved.set(attachmentId, {
34332
34637
  kind: "inline",
@@ -42165,6 +42470,13 @@ function releaseListener(handle) {
42165
42470
  //#endregion
42166
42471
  //#region src/index.ts
42167
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),
42168
42480
  syncAgentPresets: z.boolean().default(true),
42169
42481
  subagentModelAuthorization: z.boolean().default(true),
42170
42482
  subagentModelTools: z.array(z.string()).default([]),
@@ -42181,6 +42493,7 @@ const inject = [
42181
42493
  "loader"
42182
42494
  ];
42183
42495
  function apply(ctx, pluginConfig = {}) {
42496
+ const fetchConfiguration = resolveFetchConfiguration(pluginConfig);
42184
42497
  if (pluginConfig.syncAgentPresets !== false) ctx.effect(() => installBundledPresets(ctx, installDispatchPreset(ctx)), "dsh-chatgpt-subscription: agent preset sync");
42185
42498
  const store = createPlatformTokenStore();
42186
42499
  const preferences = registerPreferenceStore(ctx.settings);
@@ -42517,9 +42830,22 @@ function apply(ctx, pluginConfig = {}) {
42517
42830
  signal: options?.signal
42518
42831
  })).catch(() => []));
42519
42832
  const searchSwitcher = new SearchProviderSwitcher(ctx.loader);
42833
+ let lastFetchDiagnostic;
42520
42834
  const applyWebProviders = (current = preferences.status()) => {
42521
42835
  const pluginFetch = proxyManager.resolveActiveProxyUrl() !== null;
42522
- searchSwitcher.select(current.searchProvider, { pluginFetch }).catch((error) => {
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) => {
42523
42849
  ctx.logger.warn(`[dsh-chatgpt-subscription] Web provider selection could not be applied: ${error instanceof Error ? error.message : String(error)}`);
42524
42850
  });
42525
42851
  };
@@ -42545,13 +42871,17 @@ function apply(ctx, pluginConfig = {}) {
42545
42871
  }))
42546
42872
  });
42547
42873
  };
42548
- const disposeRoutes = registerRoutes(ctx, oauth, usage, preferences, proxyManager, searchSwitcher, readRouteAudit, codexAccountPool);
42874
+ const disposeRoutes = registerRoutes(ctx, oauth, usage, preferences, proxyManager, searchSwitcher, readRouteAudit, codexAccountPool, fetchConfiguration);
42549
42875
  const disposeAdapter = ctx.llm.registerAdapter([PROVIDER_ID$7], adapter);
42550
42876
  const disposeImageTool = ctx.tools.register(createCodexImageTool(oauth, ctx.attachments, { fetchFn: proxyFetch }));
42551
42877
  const disposeVideoTool = ctx.tools.register(createKimiVideoTool(ctx, { fetchFn: proxyFetch }));
42552
42878
  ctx.inject(["web"], (ctx) => {
42553
42879
  ctx.web.registerSearchProvider(createCodexSearchProvider(oauth, { fetchFn: proxyFetch }));
42554
- ctx.web.registerFetchProvider(createCodexFetchProvider({ fetchFn: proxyFetch }));
42880
+ ctx.web.registerFetchProvider(createCodexFetchProvider({
42881
+ fetchFn: proxyFetch,
42882
+ maxBodyChars: fetchConfiguration.fetchMaxBodyChars,
42883
+ maxResponseBytes: fetchConfiguration.fetchMaxResponseBytes
42884
+ }));
42555
42885
  setTimeout(applyWebProviders, 0);
42556
42886
  });
42557
42887
  const disposePreferenceWatch = preferences.watch((next) => applyWebProviders(next));
@@ -5,6 +5,24 @@ type Props = PropsRuntime<'settings.section'> & PropsLocale<typeof NS>;
5
5
  type BusyAction = 'login' | 'token' | 'quota' | 'reset-credit' | 'test' | 'logout' | 'preferences' | 'set-primary' | 'delete' | 'clear-cooldown' | 'strategy' | null;
6
6
  type Translate = Props['t'];
7
7
  export declare function CodexSubscriptionSection({ t }: Props): React.JSX.Element;
8
+ /**
9
+ * Read-only view of the fetch selection, shown under the search source.
10
+ *
11
+ * Both blocks are optional on the status DTO, so every row is rendered only from
12
+ * a value the host actually reported. That distinction is load-bearing: a
13
+ * missing `switcher` means the host tells us nothing and the row stays hidden,
14
+ * while a reported `configuredFetchProvider: null` is a real answer — the host
15
+ * pinned no provider, which is the DSH default. Guessing an id there would claim
16
+ * a provider the settings page never observed.
17
+ *
18
+ * Nothing here is editable: the mode and the limits live in the plugin Config,
19
+ * not in the preferences this card sits in, so the numbers cannot be mistaken for
20
+ * a setting that this page can change.
21
+ */
22
+ export declare function FetchProviderStatusCard({ status, t }: {
23
+ status: PluginStatusDto | null | undefined;
24
+ t: Translate;
25
+ }): React.JSX.Element | null;
8
26
  export declare function storageLabel(storage: CredentialStorageDto | undefined, t: Translate): string;
9
27
  export declare function storageNotice(storage: CredentialStorageDto | undefined, t: Translate): string;
10
28
  export declare function ResetCreditsFact({ resetCredits, busy, onUse, t }: {
@@ -1 +1 @@
1
- {"version":3,"file":"CodexSubscriptionSection.d.ts","sourceRoot":"","sources":["../../../src/client/CodexSubscriptionSection.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACjF,OAAO,KAAK,EAAyB,oBAAoB,EAAE,eAAe,EAAkB,cAAc,EAA8D,MAAM,wBAAwB,CAAA;AAOtM,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,KAAK,KAAK,GAAG,YAAY,CAAC,kBAAkB,CAAC,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;AACtE,KAAK,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,cAAc,GAAG,MAAM,GAAG,QAAQ,GAAG,aAAa,GAC9F,aAAa,GAAG,QAAQ,GAAG,gBAAgB,GAAG,UAAU,GAAG,IAAI,CAAA;AACnE,KAAK,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;AAE3B,wBAAgB,wBAAwB,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAqiBxE;AAiBD,wBAAgB,YAAY,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CAO5F;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CAO7F;AAcD,wBAAgB,gBAAgB,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IAAE,YAAY,EAAE,WAAW,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAUzN;AAED,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAczH;AAMD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CAYpF;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOlE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,SAA4B,GAAG,MAAM,GAAG,IAAI,CAGxG;AAoCD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAgBtE"}
1
+ {"version":3,"file":"CodexSubscriptionSection.d.ts","sourceRoot":"","sources":["../../../src/client/CodexSubscriptionSection.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACjF,OAAO,KAAK,EAAyB,oBAAoB,EAAE,eAAe,EAAkB,cAAc,EAA8D,MAAM,wBAAwB,CAAA;AAOtM,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,KAAK,KAAK,GAAG,YAAY,CAAC,kBAAkB,CAAC,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;AACtE,KAAK,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,cAAc,GAAG,MAAM,GAAG,QAAQ,GAAG,aAAa,GAC9F,aAAa,GAAG,QAAQ,GAAG,gBAAgB,GAAG,UAAU,GAAG,IAAI,CAAA;AACnE,KAAK,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;AAE3B,wBAAgB,wBAAwB,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAsiBxE;AA+BD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;IAAE,MAAM,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,CAAC;IAAC,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CA4B7I;AAOD,wBAAgB,YAAY,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CAO5F;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CAO7F;AAcD,wBAAgB,gBAAgB,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IAAE,YAAY,EAAE,WAAW,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAUzN;AAED,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAczH;AAMD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CAYpF;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOlE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,SAA4B,GAAG,MAAM,GAAG,IAAI,CAGxG;AAoCD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAgBtE"}
@@ -70,10 +70,30 @@ export declare const zh: {
70
70
  readonly summaryConcise: "简洁(显示简要推理摘要)";
71
71
  readonly summaryDetailed: "详细(显示更详细的推理摘要)";
72
72
  readonly summaryNone: "无(不显示推理摘要)";
73
- readonly searchProvider: "搜索与抓取来源";
74
- readonly searchProviderHint: "选择 DSH 联网工具使用默认来源,或改用当前 ChatGPT 订阅的 Codex 来源(自动走代理隧道)。";
73
+ readonly searchProvider: "搜索来源";
74
+ readonly searchProviderHint: "选择 DSH 联网搜索使用默认来源,或改用当前 ChatGPT 订阅的 Codex 来源(自动走代理隧道)。抓取模式为 auto 时,抓取来源跟随此选择。";
75
75
  readonly searchProviderDsh: "DSH 默认";
76
76
  readonly searchProviderCodex: "ChatGPT(代理隧道)";
77
+ readonly fetchStatus: "抓取来源(只读)";
78
+ readonly fetchStatusHint: "以下内容由 Host 上报,只读展示。";
79
+ readonly fetchConfiguredProvider: "配置的抓取 Provider";
80
+ readonly fetchProviderDshDefault: "DSH 默认(Host 未固定抓取 provider)";
81
+ readonly switcherState: "抓取选择器状态";
82
+ readonly switcherStateIdle: "空闲(尚未应用选择)";
83
+ readonly switcherStateApplying: "应用中(正在切换 Host 的 web provider)";
84
+ readonly switcherStateApplied: "已应用";
85
+ readonly switcherStateMissing: "未找到可切换的 web 服务";
86
+ readonly switcherStateFailed: "切换失败,请检查 Host 日志";
87
+ readonly fetchMode: "抓取模式";
88
+ readonly fetchModeAuto: "auto(有可用代理或选了 ChatGPT 搜索时用插件抓取)";
89
+ readonly fetchModePlugin: "plugin(始终用本插件抓取)";
90
+ readonly fetchModeDsh: "dsh(保留 DSH 原有抓取来源)";
91
+ readonly fetchMaxBodyChars: "插件正文上限";
92
+ readonly fetchMaxResponseBytes: "插件响应体上限";
93
+ readonly characters: "字符";
94
+ readonly bytes: "字节";
95
+ readonly fetchLimitsInactive: "仅在实际使用插件抓取时生效;走 DSH 原有抓取来源时不适用这两个上限。";
96
+ readonly fetchConfigSource: "模式与上限在插件 Config 中修改(不是本页面偏好),修改后重载插件或重启 DSH 生效。";
77
97
  readonly providerDefault: "使用 Provider 默认值";
78
98
  readonly contextWindows: "模型上下文窗口";
79
99
  readonly contextWindowsHint: "只列出已勾选「可用模型」的条目。默认取模型目录值(GPT-6 系列 384K,最高 872K;其余最高 1M),该值用于 DSH 的压缩与溢出判断,可输入 872K、512K 等容量;改过的模型可单独或一键恢复默认。";
@@ -225,10 +245,30 @@ export declare const dictionaries: {
225
245
  readonly summaryConcise: "简洁(显示简要推理摘要)";
226
246
  readonly summaryDetailed: "详细(显示更详细的推理摘要)";
227
247
  readonly summaryNone: "无(不显示推理摘要)";
228
- readonly searchProvider: "搜索与抓取来源";
229
- readonly searchProviderHint: "选择 DSH 联网工具使用默认来源,或改用当前 ChatGPT 订阅的 Codex 来源(自动走代理隧道)。";
248
+ readonly searchProvider: "搜索来源";
249
+ readonly searchProviderHint: "选择 DSH 联网搜索使用默认来源,或改用当前 ChatGPT 订阅的 Codex 来源(自动走代理隧道)。抓取模式为 auto 时,抓取来源跟随此选择。";
230
250
  readonly searchProviderDsh: "DSH 默认";
231
251
  readonly searchProviderCodex: "ChatGPT(代理隧道)";
252
+ readonly fetchStatus: "抓取来源(只读)";
253
+ readonly fetchStatusHint: "以下内容由 Host 上报,只读展示。";
254
+ readonly fetchConfiguredProvider: "配置的抓取 Provider";
255
+ readonly fetchProviderDshDefault: "DSH 默认(Host 未固定抓取 provider)";
256
+ readonly switcherState: "抓取选择器状态";
257
+ readonly switcherStateIdle: "空闲(尚未应用选择)";
258
+ readonly switcherStateApplying: "应用中(正在切换 Host 的 web provider)";
259
+ readonly switcherStateApplied: "已应用";
260
+ readonly switcherStateMissing: "未找到可切换的 web 服务";
261
+ readonly switcherStateFailed: "切换失败,请检查 Host 日志";
262
+ readonly fetchMode: "抓取模式";
263
+ readonly fetchModeAuto: "auto(有可用代理或选了 ChatGPT 搜索时用插件抓取)";
264
+ readonly fetchModePlugin: "plugin(始终用本插件抓取)";
265
+ readonly fetchModeDsh: "dsh(保留 DSH 原有抓取来源)";
266
+ readonly fetchMaxBodyChars: "插件正文上限";
267
+ readonly fetchMaxResponseBytes: "插件响应体上限";
268
+ readonly characters: "字符";
269
+ readonly bytes: "字节";
270
+ readonly fetchLimitsInactive: "仅在实际使用插件抓取时生效;走 DSH 原有抓取来源时不适用这两个上限。";
271
+ readonly fetchConfigSource: "模式与上限在插件 Config 中修改(不是本页面偏好),修改后重载插件或重启 DSH 生效。";
232
272
  readonly providerDefault: "使用 Provider 默认值";
233
273
  readonly contextWindows: "模型上下文窗口";
234
274
  readonly contextWindowsHint: "只列出已勾选「可用模型」的条目。默认取模型目录值(GPT-6 系列 384K,最高 872K;其余最高 1M),该值用于 DSH 的压缩与溢出判断,可输入 872K、512K 等容量;改过的模型可单独或一键恢复默认。";
@@ -306,6 +346,6 @@ export declare const dictionaries: {
306
346
  readonly email: string;
307
347
  readonly lastUsed: string;
308
348
  };
309
- en: Record<"stale" | "quota" | "account" | "storage" | "rotationStrategy" | "pending" | "accountId" | "relogin" | "tokens" | "title" | "hubTitle" | "intro" | "signedOut" | "signedIn" | "plan" | "expires" | "storageWindows" | "storageMacKeychain" | "storageLinuxFile" | "storageMemory" | "storageUnavailable" | "securityWindows" | "securityMacKeychain" | "securityLinuxFile" | "securityMemory" | "securityUnavailable" | "signIn" | "signInAgain" | "cancel" | "signOut" | "refreshToken" | "popupBlocked" | "continueLogin" | "loading" | "connection" | "provider" | "connectionState" | "connected" | "untested" | "testConnection" | "testing" | "latency" | "enableProvider" | "selectAll" | "unselectAll" | "models" | "modelsHint" | "proxySettings" | "proxyMode" | "proxyModeHint" | "proxyModeAuto" | "proxyModeCustom" | "proxyModeDirect" | "customProxyUrl" | "customProxyUrlHint" | "customProxyUrlPlaceholder" | "saveProxyUrl" | "proxyDetected" | "proxyDetectedEffective" | "proxyNoneDetected" | "proxyDirectHint" | "enhancements" | "fastMode" | "fastModeHint" | "outputVerbosity" | "outputVerbosityHint" | "verbosityLow" | "verbosityMedium" | "verbosityHigh" | "reasoningSummary" | "reasoningSummaryHint" | "summaryAuto" | "summaryConcise" | "summaryDetailed" | "summaryNone" | "searchProvider" | "searchProviderHint" | "searchProviderDsh" | "searchProviderCodex" | "providerDefault" | "contextWindows" | "contextWindowsHint" | "contextWindow" | "contextWindowInvalid" | "contextWindowReset" | "contextWindowResetAll" | "contextWindowResetAllConfirm" | "contextWindowsNoneEnabled" | "saveContextWindow" | "save" | "quickQuota" | "quickQuotaHint" | "quotaIntro" | "refreshQuota" | "refreshing" | "noQuota" | "quotaSignedOut" | "updated" | "primary" | "secondary" | "limitWindow" | "used" | "remaining" | "available" | "unavailable" | "unlimited" | "credits" | "monthlySpend" | "resetCredits" | "resetCreditExpires" | "useResetCredit" | "usingResetCredit" | "useResetCreditConfirm" | "resetCreditUsed" | "spendControlReached" | "limit" | "exhausted" | "resets" | "quickQuotaLabel" | "quickQuotaLoading" | "imageToolRunning" | "imageToolDone" | "imageToolFailed" | "image" | "openImage" | "openNamedImage" | "imageLoading" | "imageLoadFailed" | "imagePreviewClose" | "imagePreviewOpenOriginal" | "retry" | "unknown" | "accountPool" | "addAccount" | "accountCount" | "primaryAccount" | "activeAccount" | "setPrimary" | "deleteAccount" | "cooling" | "cooldownLeft" | "clearCooldown" | "needsRelogin" | "strategySequential" | "strategyRoundRobin" | "strategySticky" | "noAccounts" | "storageNotice" | "email" | "lastUsed", string>;
349
+ en: Record<"stale" | "quota" | "account" | "storage" | "rotationStrategy" | "pending" | "accountId" | "relogin" | "tokens" | "title" | "hubTitle" | "intro" | "signedOut" | "signedIn" | "plan" | "expires" | "storageWindows" | "storageMacKeychain" | "storageLinuxFile" | "storageMemory" | "storageUnavailable" | "securityWindows" | "securityMacKeychain" | "securityLinuxFile" | "securityMemory" | "securityUnavailable" | "signIn" | "signInAgain" | "cancel" | "signOut" | "refreshToken" | "popupBlocked" | "continueLogin" | "loading" | "connection" | "provider" | "connectionState" | "connected" | "untested" | "testConnection" | "testing" | "latency" | "enableProvider" | "selectAll" | "unselectAll" | "models" | "modelsHint" | "proxySettings" | "proxyMode" | "proxyModeHint" | "proxyModeAuto" | "proxyModeCustom" | "proxyModeDirect" | "customProxyUrl" | "customProxyUrlHint" | "customProxyUrlPlaceholder" | "saveProxyUrl" | "proxyDetected" | "proxyDetectedEffective" | "proxyNoneDetected" | "proxyDirectHint" | "enhancements" | "fastMode" | "fastModeHint" | "outputVerbosity" | "outputVerbosityHint" | "verbosityLow" | "verbosityMedium" | "verbosityHigh" | "reasoningSummary" | "reasoningSummaryHint" | "summaryAuto" | "summaryConcise" | "summaryDetailed" | "summaryNone" | "searchProvider" | "searchProviderHint" | "searchProviderDsh" | "searchProviderCodex" | "fetchStatus" | "fetchStatusHint" | "fetchConfiguredProvider" | "fetchProviderDshDefault" | "switcherState" | "switcherStateIdle" | "switcherStateApplying" | "switcherStateApplied" | "switcherStateMissing" | "switcherStateFailed" | "fetchMode" | "fetchModeAuto" | "fetchModePlugin" | "fetchModeDsh" | "fetchMaxBodyChars" | "fetchMaxResponseBytes" | "characters" | "bytes" | "fetchLimitsInactive" | "fetchConfigSource" | "providerDefault" | "contextWindows" | "contextWindowsHint" | "contextWindow" | "contextWindowInvalid" | "contextWindowReset" | "contextWindowResetAll" | "contextWindowResetAllConfirm" | "contextWindowsNoneEnabled" | "saveContextWindow" | "save" | "quickQuota" | "quickQuotaHint" | "quotaIntro" | "refreshQuota" | "refreshing" | "noQuota" | "quotaSignedOut" | "updated" | "primary" | "secondary" | "limitWindow" | "used" | "remaining" | "available" | "unavailable" | "unlimited" | "credits" | "monthlySpend" | "resetCredits" | "resetCreditExpires" | "useResetCredit" | "usingResetCredit" | "useResetCreditConfirm" | "resetCreditUsed" | "spendControlReached" | "limit" | "exhausted" | "resets" | "quickQuotaLabel" | "quickQuotaLoading" | "imageToolRunning" | "imageToolDone" | "imageToolFailed" | "image" | "openImage" | "openNamedImage" | "imageLoading" | "imageLoadFailed" | "imagePreviewClose" | "imagePreviewOpenOriginal" | "retry" | "unknown" | "accountPool" | "addAccount" | "accountCount" | "primaryAccount" | "activeAccount" | "setPrimary" | "deleteAccount" | "cooling" | "cooldownLeft" | "clearCooldown" | "needsRelogin" | "strategySequential" | "strategyRoundRobin" | "strategySticky" | "noAccounts" | "storageNotice" | "email" | "lastUsed", string>;
310
350
  };
311
351
  //# sourceMappingURL=locales.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE,EAAG,0BAAmC,CAAA;AAIrD,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsIL,CAAA;AAEV,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,MAAM,OAAO,EAAE,EAAE,MAAM,CAoI9C,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,EAAE,CAAA;AACvC,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAa,CAAA"}
1
+ {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE,EAAG,0BAAmC,CAAA;AAIrD,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0JL,CAAA;AAEV,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,MAAM,OAAO,EAAE,EAAE,MAAM,CAwJ9C,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,EAAE,CAAA;AACvC,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAa,CAAA"}