aisubs 0.3.1 → 0.3.2

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 CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.2 - 2026-08-31
4
+
5
+ - Normalize ChatGPT Responses requests by removing unsupported explicit
6
+ prompt-cache controls while preserving prompt-cache routing.
7
+ - Apply provider request normalization before authorization for direct and
8
+ proxied requests.
9
+
3
10
  ## 0.3.1 - 2026-08-20
4
11
 
5
12
  - Add dashboard-managed Codex Desktop integration with callable model discovery,
package/dist/auth.js CHANGED
@@ -384,7 +384,8 @@ export class SubscriptionAuth {
384
384
  const original = createRequest(input, init);
385
385
  const accountKey = normalizeAccountKey(account);
386
386
  const send = async (credential) => {
387
- const authorized = await adapter.authorize(original.clone(), credential);
387
+ const normalized = (await adapter.normalizeRequest?.(original.clone())) ?? original.clone();
388
+ const authorized = await adapter.authorize(normalized, credential);
388
389
  const headers = new Headers(authorized.headers);
389
390
  headers.set("cache-control", "no-store");
390
391
  const request = new Request(authorized, { cache: "no-store", headers });
@@ -436,7 +437,9 @@ export class SubscriptionAuth {
436
437
  if (!baseUrl)
437
438
  throw new Error(`${provider} does not expose a direct API endpoint`);
438
439
  const base = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
439
- return adapter.authorize(createRequest(new URL(path, base), init), credential);
440
+ const request = createRequest(new URL(path, base), init);
441
+ const normalized = (await adapter.normalizeRequest?.(request.clone())) ?? request;
442
+ return adapter.authorize(normalized, credential);
440
443
  }
441
444
  async getUsage(provider, account = DEFAULT_ACCOUNT, callerSignal) {
442
445
  const adapter = this.adapter(provider);
@@ -98,6 +98,32 @@ function normalizeModel(value) {
98
98
  priority: numberValue(value.priority) ?? Number.MAX_SAFE_INTEGER,
99
99
  };
100
100
  }
101
+ async function normalizeChatGptRequest(request) {
102
+ if (request.method !== "POST" || !new URL(request.url).pathname.endsWith("/responses")) {
103
+ return request;
104
+ }
105
+ const raw = await request
106
+ .clone()
107
+ .json()
108
+ .catch(() => null);
109
+ if (!isRecord(raw))
110
+ return request;
111
+ const body = { ...raw };
112
+ delete body.prompt_cache_options;
113
+ delete body.prompt_cache_retention;
114
+ const stripBreakpoints = (value) => Array.isArray(value)
115
+ ? value.map((item) => isRecord(item)
116
+ ? Object.fromEntries(Object.entries(item).filter(([key]) => key !== "prompt_cache_breakpoint"))
117
+ : item)
118
+ : value;
119
+ body.input = stripBreakpoints(body.input);
120
+ body.tools = stripBreakpoints(body.tools);
121
+ return new Request(request, {
122
+ method: request.method,
123
+ body: JSON.stringify(body),
124
+ headers: { ...Object.fromEntries(request.headers), "content-type": "application/json" },
125
+ });
126
+ }
101
127
  export function chatGptProvider(options = {}) {
102
128
  const clientId = options.clientId ?? DEFAULT_CLIENT_ID;
103
129
  const compatibilityVersion = options.compatibilityVersion ?? "0.144.2";
@@ -350,6 +376,7 @@ export function chatGptProvider(options = {}) {
350
376
  throw new Error("ChatGPT refresh returned invalid JSON");
351
377
  return credentialFromTokens(raw, credential);
352
378
  },
379
+ normalizeRequest: normalizeChatGptRequest,
353
380
  authorize(request, credential) {
354
381
  requireAllowedHost(request, ["chatgpt.com"]);
355
382
  const accountId = credential.account?.id;
package/dist/types.d.ts CHANGED
@@ -143,6 +143,7 @@ export interface ProviderAdapter {
143
143
  readonly proxyBaseUrl?: string | ((credential: OAuthCredential) => string | undefined);
144
144
  /** Optional local OpenAI-compatible bridge for providers without a pass-through API. */
145
145
  proxy?(request: Request, credential: OAuthCredential): Promise<Response>;
146
+ normalizeRequest?(request: Request): Request | Promise<Request>;
146
147
  normalizeResponse?(request: Request, response: Response): Response | Promise<Response>;
147
148
  startLogin(signal: AbortSignal, options?: Record<string, unknown>): Promise<ProviderLogin>;
148
149
  refresh(credential: OAuthCredential, signal: AbortSignal): Promise<OAuthCredential>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aisubs",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Connect AI provider accounts and use those subscriptions from any local tool or as api.",
5
5
  "keywords": [
6
6
  "ai",