openai 7.7.0 → 7.9.0
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 +39 -0
- package/README.md +15 -30
- package/auth/index.d.mts +1 -1
- package/auth/index.d.mts.map +1 -1
- package/auth/index.d.ts +1 -1
- package/auth/index.d.ts.map +1 -1
- package/auth/index.js.map +1 -1
- package/auth/index.mjs.map +1 -1
- package/auth/types.d.mts +9 -4
- package/auth/types.d.mts.map +1 -1
- package/auth/types.d.ts +9 -4
- package/auth/types.d.ts.map +1 -1
- package/auth/workload-identity-auth.d.mts +1 -1
- package/auth/workload-identity-auth.d.mts.map +1 -1
- package/auth/workload-identity-auth.d.ts +1 -1
- package/auth/workload-identity-auth.d.ts.map +1 -1
- package/auth/workload-identity-auth.js +23 -6
- package/auth/workload-identity-auth.js.map +1 -1
- package/auth/workload-identity-auth.mjs +23 -6
- package/auth/workload-identity-auth.mjs.map +1 -1
- package/auth/x509-transport.d.mts +36 -1
- package/auth/x509-transport.d.mts.map +1 -1
- package/auth/x509-transport.d.ts +36 -1
- package/auth/x509-transport.d.ts.map +1 -1
- package/auth/x509-transport.js +224 -1
- package/auth/x509-transport.js.map +1 -1
- package/auth/x509-transport.mjs +222 -1
- package/auth/x509-transport.mjs.map +1 -1
- package/azure.d.mts +3 -1
- package/azure.d.mts.map +1 -1
- package/azure.d.ts +3 -1
- package/azure.d.ts.map +1 -1
- package/azure.js.map +1 -1
- package/azure.mjs.map +1 -1
- package/bedrock.d.mts +3 -1
- package/bedrock.d.mts.map +1 -1
- package/bedrock.d.ts +3 -1
- package/bedrock.d.ts.map +1 -1
- package/bedrock.js.map +1 -1
- package/bedrock.mjs.map +1 -1
- package/beta/realtime/ws.d.mts.map +1 -1
- package/beta/realtime/ws.d.ts.map +1 -1
- package/beta/realtime/ws.js +2 -0
- package/beta/realtime/ws.js.map +1 -1
- package/beta/realtime/ws.mjs +2 -0
- package/beta/realtime/ws.mjs.map +1 -1
- package/client.d.mts +3 -1
- package/client.d.mts.map +1 -1
- package/client.d.ts +3 -1
- package/client.d.ts.map +1 -1
- package/client.js +31 -38
- package/client.js.map +1 -1
- package/client.mjs +31 -38
- package/client.mjs.map +1 -1
- package/core/EventEmitter.d.mts.map +1 -1
- package/core/EventEmitter.d.ts.map +1 -1
- package/core/EventEmitter.js +14 -1
- package/core/EventEmitter.js.map +1 -1
- package/core/EventEmitter.mjs +14 -1
- package/core/EventEmitter.mjs.map +1 -1
- package/internal/auth/x509-api-origin.d.mts +5 -0
- package/internal/auth/x509-api-origin.d.mts.map +1 -0
- package/internal/auth/x509-api-origin.d.ts +5 -0
- package/internal/auth/x509-api-origin.d.ts.map +1 -0
- package/internal/auth/x509-api-origin.js +28 -0
- package/internal/auth/x509-api-origin.js.map +1 -0
- package/internal/auth/x509-api-origin.mjs +24 -0
- package/internal/auth/x509-api-origin.mjs.map +1 -0
- package/internal/auth/x509-credential-options.d.mts +13 -0
- package/internal/auth/x509-credential-options.d.mts.map +1 -0
- package/internal/auth/x509-credential-options.d.ts +13 -0
- package/internal/auth/x509-credential-options.d.ts.map +1 -0
- package/internal/auth/x509-credential-options.js +122 -0
- package/internal/auth/x509-credential-options.js.map +1 -0
- package/internal/auth/x509-credential-options.mjs +118 -0
- package/internal/auth/x509-credential-options.mjs.map +1 -0
- package/internal/auth/x509-token-exchange.js +1 -1
- package/internal/auth/x509-token-exchange.mjs +1 -1
- package/internal/auth/x509-transport-capability.d.mts +12 -5
- package/internal/auth/x509-transport-capability.d.mts.map +1 -1
- package/internal/auth/x509-transport-capability.d.ts +12 -5
- package/internal/auth/x509-transport-capability.d.ts.map +1 -1
- package/internal/auth/x509-transport-capability.js +6 -5
- package/internal/auth/x509-transport-capability.js.map +1 -1
- package/internal/auth/x509-transport-capability.mjs +6 -5
- package/internal/auth/x509-transport-capability.mjs.map +1 -1
- package/internal/auth/x509-transport-registry.d.mts +8 -0
- package/internal/auth/x509-transport-registry.d.mts.map +1 -1
- package/internal/auth/x509-transport-registry.d.ts +8 -0
- package/internal/auth/x509-transport-registry.d.ts.map +1 -1
- package/internal/auth/x509-transport-registry.js +1 -1
- package/internal/auth/x509-transport-registry.js.map +1 -1
- package/internal/auth/x509-transport-registry.mjs +1 -1
- package/internal/auth/x509-transport-registry.mjs.map +1 -1
- package/internal/auth/x509-transport-state-browser.d.mts +4 -0
- package/internal/auth/x509-transport-state-browser.d.mts.map +1 -1
- package/internal/auth/x509-transport-state-browser.d.ts +4 -0
- package/internal/auth/x509-transport-state-browser.d.ts.map +1 -1
- package/internal/auth/x509-transport-state-browser.js +6 -1
- package/internal/auth/x509-transport-state-browser.js.map +1 -1
- package/internal/auth/x509-transport-state-browser.mjs +5 -0
- package/internal/auth/x509-transport-state-browser.mjs.map +1 -1
- package/internal/auth/x509-transport-state.cjs +6 -1
- package/internal/auth/x509-transport-state.cjs.map +1 -1
- package/internal/auth/x509-transport-state.d.cts +4 -0
- package/internal/auth/x509-transport-state.d.cts.map +1 -1
- package/internal/auth/x509-transport-state.js +36 -0
- package/internal/auth/x509-transport-state.mjs +19 -0
- package/internal/auth/x509-workload-identity-auth.d.mts +2 -4
- package/internal/auth/x509-workload-identity-auth.d.mts.map +1 -1
- package/internal/auth/x509-workload-identity-auth.d.ts +2 -4
- package/internal/auth/x509-workload-identity-auth.d.ts.map +1 -1
- package/internal/auth/x509-workload-identity-auth.js +44 -35
- package/internal/auth/x509-workload-identity-auth.js.map +1 -1
- package/internal/auth/x509-workload-identity-auth.mjs +40 -30
- package/internal/auth/x509-workload-identity-auth.mjs.map +1 -1
- package/internal/uploads.js +4 -2
- package/internal/uploads.js.map +1 -1
- package/internal/uploads.mjs +4 -2
- package/internal/uploads.mjs.map +1 -1
- package/internal/utils/log.d.mts +6 -0
- package/internal/utils/log.d.mts.map +1 -1
- package/internal/utils/log.d.ts +6 -0
- package/internal/utils/log.d.ts.map +1 -1
- package/internal/utils/log.js +72 -11
- package/internal/utils/log.js.map +1 -1
- package/internal/utils/log.mjs +69 -11
- package/internal/utils/log.mjs.map +1 -1
- package/lib/EventEmitter.d.mts.map +1 -1
- package/lib/EventEmitter.d.ts.map +1 -1
- package/lib/EventEmitter.js +14 -1
- package/lib/EventEmitter.js.map +1 -1
- package/lib/EventEmitter.mjs +14 -1
- package/lib/EventEmitter.mjs.map +1 -1
- package/package.json +10 -6
- package/realtime/ws.d.mts.map +1 -1
- package/realtime/ws.d.ts.map +1 -1
- package/realtime/ws.js +2 -0
- package/realtime/ws.js.map +1 -1
- package/realtime/ws.mjs +2 -0
- package/realtime/ws.mjs.map +1 -1
- package/resources/admin/organization/audit-logs.d.mts +2 -2
- package/resources/admin/organization/audit-logs.d.mts.map +1 -1
- package/resources/admin/organization/audit-logs.d.ts +2 -2
- package/resources/admin/organization/audit-logs.d.ts.map +1 -1
- package/resources/admin/organization/usage.d.mts +5 -0
- package/resources/admin/organization/usage.d.mts.map +1 -1
- package/resources/admin/organization/usage.d.ts +5 -0
- package/resources/admin/organization/usage.d.ts.map +1 -1
- package/resources/batches.d.mts +3 -3
- package/resources/batches.d.ts +3 -3
- package/resources/beta/responses/responses.d.mts +11 -23
- package/resources/beta/responses/responses.d.mts.map +1 -1
- package/resources/beta/responses/responses.d.ts +11 -23
- package/resources/beta/responses/responses.d.ts.map +1 -1
- package/resources/beta/responses/responses.js.map +1 -1
- package/resources/beta/responses/responses.mjs.map +1 -1
- package/resources/beta/responses/ws.d.mts.map +1 -1
- package/resources/beta/responses/ws.d.ts.map +1 -1
- package/resources/beta/responses/ws.js +3 -0
- package/resources/beta/responses/ws.js.map +1 -1
- package/resources/beta/responses/ws.mjs +3 -0
- package/resources/beta/responses/ws.mjs.map +1 -1
- package/resources/chat/completions/completions.d.mts +4 -4
- package/resources/chat/completions/completions.d.ts +4 -4
- package/resources/evals/evals.d.mts +4 -4
- package/resources/evals/evals.d.ts +4 -4
- package/resources/evals/runs/runs.d.mts +1 -1
- package/resources/evals/runs/runs.d.ts +1 -1
- package/resources/responses/responses.d.mts +13 -19
- package/resources/responses/responses.d.mts.map +1 -1
- package/resources/responses/responses.d.ts +13 -19
- package/resources/responses/responses.d.ts.map +1 -1
- package/resources/responses/responses.js.map +1 -1
- package/resources/responses/responses.mjs.map +1 -1
- package/resources/responses/ws.d.mts.map +1 -1
- package/resources/responses/ws.d.ts.map +1 -1
- package/resources/responses/ws.js +3 -0
- package/resources/responses/ws.js.map +1 -1
- package/resources/responses/ws.mjs +3 -0
- package/resources/responses/ws.mjs.map +1 -1
- package/resources/shared.d.mts +0 -2
- package/resources/shared.d.mts.map +1 -1
- package/resources/shared.d.ts +0 -2
- package/resources/shared.d.ts.map +1 -1
- package/src/_vendor/partial-json-parser/LICENSE +21 -0
- package/src/_vendor/partial-json-parser/README.md +11 -1
- package/src/auth/index.ts +1 -0
- package/src/auth/types.ts +10 -4
- package/src/auth/workload-identity-auth.ts +30 -6
- package/src/auth/x509-transport.ts +311 -2
- package/src/azure.ts +4 -1
- package/src/bedrock.ts +4 -1
- package/src/beta/realtime/ws.ts +2 -0
- package/src/client.ts +39 -34
- package/src/core/EventEmitter.ts +13 -1
- package/src/internal/auth/x509-api-origin.ts +27 -0
- package/src/internal/auth/x509-credential-options.ts +146 -0
- package/src/internal/auth/x509-transport-capability.ts +12 -5
- package/src/internal/auth/x509-transport-registry.ts +9 -0
- package/src/internal/auth/x509-transport-state-browser.ts +7 -0
- package/src/internal/auth/x509-transport-state.cts +7 -0
- package/src/internal/auth/x509-workload-identity-auth.ts +42 -33
- package/src/internal/uploads.ts +4 -2
- package/src/internal/utils/log.ts +76 -11
- package/src/lib/EventEmitter.ts +13 -1
- package/src/realtime/ws.ts +2 -0
- package/src/resources/admin/organization/audit-logs.ts +4 -0
- package/src/resources/admin/organization/usage.ts +6 -0
- package/src/resources/batches.ts +3 -3
- package/src/resources/beta/responses/responses.ts +11 -23
- package/src/resources/beta/responses/ws.ts +4 -0
- package/src/resources/chat/completions/completions.ts +4 -4
- package/src/resources/evals/evals.ts +4 -4
- package/src/resources/evals/runs/runs.ts +1 -1
- package/src/resources/responses/responses.ts +13 -19
- package/src/resources/responses/ws.ts +4 -0
- package/src/resources/shared.ts +0 -2
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
package/src/client.ts
CHANGED
|
@@ -17,17 +17,20 @@ import { resolveDataResidency, type DataResidency } from './internal/data-reside
|
|
|
17
17
|
export type { DataResidency } from './internal/data-residency';
|
|
18
18
|
import * as Errors from './core/error';
|
|
19
19
|
import * as Pagination from './core/pagination';
|
|
20
|
-
import type { WorkloadIdentity, X509WorkloadIdentity } from './auth/types';
|
|
20
|
+
import type { WorkloadIdentity, X509Credential, X509WorkloadIdentity } from './auth/types';
|
|
21
21
|
import { WorkloadIdentityAuth } from './auth/workload-identity-auth';
|
|
22
|
+
import { X509_API_BASE_URL, assertX509APIOrigin } from './internal/auth/x509-api-origin';
|
|
22
23
|
import {
|
|
23
|
-
X509_API_BASE_URL,
|
|
24
24
|
X509WorkloadIdentityAuth,
|
|
25
|
-
assertX509APIOrigin,
|
|
26
25
|
assertX509RequestOptions,
|
|
27
26
|
isX509WorkloadIdentity,
|
|
28
27
|
snapshotX509RequestOptions,
|
|
29
28
|
} from './internal/auth/x509-workload-identity-auth';
|
|
30
29
|
import type { X509Transport } from './internal/auth/x509-transport-registry';
|
|
30
|
+
import {
|
|
31
|
+
normalizeX509CredentialOptions,
|
|
32
|
+
prepareX509ClientClone,
|
|
33
|
+
} from './internal/auth/x509-credential-options';
|
|
31
34
|
import { isTransientX509ConnectionError, markApprovedX509Client } from '#x509-transport-state';
|
|
32
35
|
import { OAuthError, SubjectTokenProviderError } from './core/error';
|
|
33
36
|
import {
|
|
@@ -258,6 +261,7 @@ import {
|
|
|
258
261
|
formatRequestDetails,
|
|
259
262
|
loggerFor,
|
|
260
263
|
parseLogLevel,
|
|
264
|
+
redactURL,
|
|
261
265
|
} from './internal/utils/log';
|
|
262
266
|
import { isEmptyObj } from './internal/utils/values';
|
|
263
267
|
|
|
@@ -430,6 +434,9 @@ export interface ClientOptions {
|
|
|
430
434
|
/** Approved, frozen Node.js certificate transport required only for X.509 workload identity. */
|
|
431
435
|
x509Transport?: X509Transport | undefined;
|
|
432
436
|
|
|
437
|
+
/** First-class certificate credential created with `fromX509` from `openai/auth/x509-transport`. */
|
|
438
|
+
credential?: X509Credential | undefined;
|
|
439
|
+
|
|
433
440
|
/**
|
|
434
441
|
* Configure this client to use a third-party API provider.
|
|
435
442
|
* Mutually exclusive with top-level authentication and `baseURL` options.
|
|
@@ -457,6 +464,7 @@ export class OpenAI {
|
|
|
457
464
|
private fetch: Fetch;
|
|
458
465
|
#encoder: Opts.RequestEncoder;
|
|
459
466
|
#x509Authentication: X509WorkloadIdentityAuth | undefined;
|
|
467
|
+
#x509Credential: X509Credential | undefined;
|
|
460
468
|
#x509Fetch: Fetch | undefined;
|
|
461
469
|
// Preserve an explicit global selection without storing a second routing URL.
|
|
462
470
|
#explicitDataResidency = false;
|
|
@@ -495,6 +503,8 @@ export class OpenAI {
|
|
|
495
503
|
* @param {boolean} [opts.dangerouslyAllowBrowser=false] - By default, client-side use of this library is not allowed, as it risks exposing your secret API credentials to attackers.
|
|
496
504
|
*/
|
|
497
505
|
constructor(clientOptions: ClientOptions = {}) {
|
|
506
|
+
const { credential, options: normalizedOptions } = normalizeX509CredentialOptions(clientOptions);
|
|
507
|
+
clientOptions = normalizedOptions;
|
|
498
508
|
const residencyBaseURL = resolveDataResidency(clientOptions);
|
|
499
509
|
const provider = clientOptions.provider;
|
|
500
510
|
const {
|
|
@@ -508,6 +518,7 @@ export class OpenAI {
|
|
|
508
518
|
webhookSecret = readEnv('OPENAI_WEBHOOK_SECRET') ?? null,
|
|
509
519
|
workloadIdentity,
|
|
510
520
|
x509Transport,
|
|
521
|
+
credential: _credential,
|
|
511
522
|
...opts
|
|
512
523
|
} = clientOptions as InternalClientOptions;
|
|
513
524
|
if (provider) {
|
|
@@ -601,7 +612,7 @@ export class OpenAI {
|
|
|
601
612
|
this.fetch = options.fetch ?? Shims.getDefaultFetch();
|
|
602
613
|
this.#encoder = Opts.FallbackEncoder;
|
|
603
614
|
|
|
604
|
-
const customHeadersEnv = provider ? undefined : readEnv('OPENAI_CUSTOM_HEADERS');
|
|
615
|
+
const customHeadersEnv = provider || credential ? undefined : readEnv('OPENAI_CUSTOM_HEADERS');
|
|
605
616
|
if (customHeadersEnv) {
|
|
606
617
|
const parsed: Record<string, string> = {};
|
|
607
618
|
for (const line of customHeadersEnv.split('\n')) {
|
|
@@ -620,6 +631,7 @@ export class OpenAI {
|
|
|
620
631
|
const authentication = new X509WorkloadIdentityAuth(x509Identity, x509Transport, organization, project);
|
|
621
632
|
this._workloadIdentityAuth = authentication;
|
|
622
633
|
this.#x509Authentication = authentication;
|
|
634
|
+
this.#x509Credential = credential;
|
|
623
635
|
this.#x509Fetch = authentication.fetch();
|
|
624
636
|
this.fetch = this.#x509Fetch;
|
|
625
637
|
markApprovedX509Client(this);
|
|
@@ -639,8 +651,6 @@ export class OpenAI {
|
|
|
639
651
|
*/
|
|
640
652
|
withOptions(options: Partial<ClientOptions>): this {
|
|
641
653
|
const residencyBaseURL = resolveDataResidency(options);
|
|
642
|
-
const inheritedProvider = this._options.provider;
|
|
643
|
-
const provider = options.provider ?? inheritedProvider;
|
|
644
654
|
const x509Authentication = this.#x509Authentication;
|
|
645
655
|
const inheritedOptions: ClientOptions = {
|
|
646
656
|
...this._options,
|
|
@@ -659,47 +669,41 @@ export class OpenAI {
|
|
|
659
669
|
project: this.project,
|
|
660
670
|
webhookSecret: this.webhookSecret,
|
|
661
671
|
};
|
|
662
|
-
const
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
delete inheritedOptions.fetch;
|
|
669
|
-
delete inheritedOptions.baseURL;
|
|
670
|
-
if (nextX509) {
|
|
671
|
-
inheritedOptions.apiKey = null;
|
|
672
|
-
} else {
|
|
673
|
-
delete inheritedOptions.x509Transport;
|
|
674
|
-
}
|
|
675
|
-
}
|
|
672
|
+
const { credential, provider } = prepareX509ClientClone(
|
|
673
|
+
inheritedOptions,
|
|
674
|
+
options,
|
|
675
|
+
this.#x509Credential,
|
|
676
|
+
x509Authentication !== undefined,
|
|
677
|
+
);
|
|
676
678
|
if (residencyBaseURL !== undefined) {
|
|
677
679
|
delete inheritedOptions.baseURL;
|
|
678
680
|
}
|
|
679
|
-
if (provider) {
|
|
680
|
-
delete inheritedOptions.apiKey;
|
|
681
|
-
delete inheritedOptions.adminAPIKey;
|
|
682
|
-
delete inheritedOptions.workloadIdentity;
|
|
683
|
-
delete inheritedOptions.x509Transport;
|
|
684
|
-
delete inheritedOptions.baseURL;
|
|
685
|
-
if (provider !== inheritedProvider) {
|
|
686
|
-
delete inheritedOptions.organization;
|
|
687
|
-
delete inheritedOptions.project;
|
|
688
|
-
delete inheritedOptions.defaultHeaders;
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
681
|
|
|
692
682
|
const clientOptions: InternalClientOptions = {
|
|
693
683
|
...inheritedOptions,
|
|
694
684
|
...options,
|
|
685
|
+
credential,
|
|
695
686
|
provider,
|
|
696
687
|
[inheritedDataResidencySelection]:
|
|
697
688
|
this.#explicitDataResidency &&
|
|
698
689
|
residencyBaseURL === undefined &&
|
|
699
690
|
!hasOwn(options, 'baseURL') &&
|
|
691
|
+
options.credential === undefined &&
|
|
700
692
|
!provider,
|
|
701
693
|
};
|
|
702
694
|
const client = new (this.constructor as any as new (props: ClientOptions) => typeof this)(clientOptions);
|
|
695
|
+
if (provider && new URL(client.baseURL).origin !== new URL(this.baseURL).origin) {
|
|
696
|
+
Object.assign(client._options, {
|
|
697
|
+
defaultHeaders: options.defaultHeaders,
|
|
698
|
+
defaultQuery: options.defaultQuery,
|
|
699
|
+
fetchOptions: options.fetchOptions,
|
|
700
|
+
fetch: options.fetch,
|
|
701
|
+
});
|
|
702
|
+
client.fetchOptions = options.fetchOptions;
|
|
703
|
+
client.fetch = options.fetch ?? Shims.getDefaultFetch();
|
|
704
|
+
client.organization = options.organization ?? null;
|
|
705
|
+
client.project = options.project ?? null;
|
|
706
|
+
}
|
|
703
707
|
if (
|
|
704
708
|
this.#x509Authentication &&
|
|
705
709
|
client.#x509Authentication &&
|
|
@@ -1151,9 +1155,10 @@ export class OpenAI {
|
|
|
1151
1155
|
retriesRemaining = maxRetries;
|
|
1152
1156
|
}
|
|
1153
1157
|
|
|
1158
|
+
const x509Authentication = this.#x509Authentication;
|
|
1159
|
+
x509Authentication?.beginRequestPreparation();
|
|
1154
1160
|
await this.prepareOptions(options);
|
|
1155
1161
|
|
|
1156
|
-
const x509Authentication = this.#x509Authentication;
|
|
1157
1162
|
x509Authentication?.beginRequestPlanning();
|
|
1158
1163
|
let built: { req: FinalizedRequestInit; url: string; timeout: number };
|
|
1159
1164
|
try {
|
|
@@ -1346,7 +1351,7 @@ export class OpenAI {
|
|
|
1346
1351
|
.filter(([name]) => name === 'x-request-id')
|
|
1347
1352
|
.map(([name, value]) => ', ' + name + ': ' + JSON.stringify(value))
|
|
1348
1353
|
.join('');
|
|
1349
|
-
const responseInfo = `[${requestLogID}${retryLogStr}${specialHeaders}] ${req.method} ${url} ${
|
|
1354
|
+
const responseInfo = `[${requestLogID}${retryLogStr}${specialHeaders}] ${req.method} ${redactURL(url)} ${
|
|
1350
1355
|
response.ok ? 'succeeded' : 'failed'
|
|
1351
1356
|
} with status ${response.status} in ${headersTime - startTime}ms`;
|
|
1352
1357
|
|
package/src/core/EventEmitter.ts
CHANGED
|
@@ -186,11 +186,20 @@ export class EventEmitter<EventTypes extends Record<string, (...args: any) => an
|
|
|
186
186
|
}
|
|
187
187
|
return !listener.once && !listener.removed;
|
|
188
188
|
}) as any;
|
|
189
|
+
let listenerThrew = false;
|
|
190
|
+
let firstListenerError: unknown;
|
|
189
191
|
this.#listenerDispatchDepth += 1;
|
|
190
192
|
try {
|
|
191
193
|
for (const registration of listeners as any) {
|
|
192
194
|
if (!registration.removed) {
|
|
193
|
-
|
|
195
|
+
try {
|
|
196
|
+
registration.listener(...(args as any));
|
|
197
|
+
} catch (error) {
|
|
198
|
+
if (!listenerThrew) {
|
|
199
|
+
listenerThrew = true;
|
|
200
|
+
firstListenerError = error;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
194
203
|
}
|
|
195
204
|
}
|
|
196
205
|
} finally {
|
|
@@ -199,6 +208,9 @@ export class EventEmitter<EventTypes extends Record<string, (...args: any) => an
|
|
|
199
208
|
this.#cleanupEmittedListeners();
|
|
200
209
|
}
|
|
201
210
|
}
|
|
211
|
+
if (listenerThrew) {
|
|
212
|
+
throw firstListenerError;
|
|
213
|
+
}
|
|
202
214
|
}
|
|
203
215
|
}
|
|
204
216
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { OpenAIError } from '../../core/error';
|
|
2
|
+
import { isSensitiveQueryParameter } from '../utils/log';
|
|
3
|
+
|
|
4
|
+
/** Sole API authority approved for OpenAI X.509 workload-identity federation. */
|
|
5
|
+
export const X509_API_BASE_URL = 'https://mtls.api.openai.com/v1';
|
|
6
|
+
|
|
7
|
+
/** Validates the enrolled API authority and rejects credential-bearing query parameters. */
|
|
8
|
+
export function assertX509APIOrigin(value: string | URL): URL {
|
|
9
|
+
let target: URL;
|
|
10
|
+
try {
|
|
11
|
+
target = new URL(value);
|
|
12
|
+
} catch {
|
|
13
|
+
throw new OpenAIError('X.509 workload identity requires the approved global mTLS API origin.');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (target.origin !== 'https://mtls.api.openai.com' || target.username || target.password) {
|
|
17
|
+
throw new OpenAIError('X.509 workload identity requires the approved global mTLS API origin.');
|
|
18
|
+
}
|
|
19
|
+
for (const name of target.searchParams.keys()) {
|
|
20
|
+
if (isSensitiveQueryParameter(name)) {
|
|
21
|
+
throw new OpenAIError(
|
|
22
|
+
'X.509 workload identity cannot send conflicting query authentication credentials.',
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return target;
|
|
27
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { OpenAIError } from '../../core/error';
|
|
2
|
+
import type { ClientOptions } from '../../client';
|
|
3
|
+
import type { X509Credential } from '../../auth/types';
|
|
4
|
+
import { hasOwn } from '../utils/values';
|
|
5
|
+
import { isX509WorkloadIdentity } from './x509-workload-identity-auth';
|
|
6
|
+
import type { RegisteredX509Credential } from './x509-transport-registry';
|
|
7
|
+
import { findX509Credential } from '#x509-transport-state';
|
|
8
|
+
|
|
9
|
+
/** Validates one privately registered credential and suppresses ambient legacy authentication. */
|
|
10
|
+
export function normalizeX509CredentialOptions(options: ClientOptions): {
|
|
11
|
+
credential: X509Credential | undefined;
|
|
12
|
+
options: ClientOptions;
|
|
13
|
+
} {
|
|
14
|
+
const { credential } = options;
|
|
15
|
+
if (credential === undefined) {
|
|
16
|
+
return { credential, options };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const registered: RegisteredX509Credential | undefined = findX509Credential(credential);
|
|
20
|
+
if (!registered) {
|
|
21
|
+
throw new OpenAIError('An X.509 credential must be created by the SDK authentication helper.');
|
|
22
|
+
}
|
|
23
|
+
const conflicting = (['apiKey', 'adminAPIKey', 'workloadIdentity', 'x509Transport'] as const).filter(
|
|
24
|
+
(name) => {
|
|
25
|
+
const value = options[name];
|
|
26
|
+
return value !== null && value !== undefined;
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
if (conflicting.length > 0) {
|
|
30
|
+
throw new OpenAIError(
|
|
31
|
+
`The \`credential\` option cannot be combined with ${conflicting.map((name) => `\`${name}\``).join(', ')}.`,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
credential,
|
|
37
|
+
options: {
|
|
38
|
+
...options,
|
|
39
|
+
apiKey: null,
|
|
40
|
+
adminAPIKey: null,
|
|
41
|
+
baseURL: options.baseURL ?? null,
|
|
42
|
+
organization: options.organization ?? null,
|
|
43
|
+
project: options.project ?? null,
|
|
44
|
+
workloadIdentity: registered.identity,
|
|
45
|
+
x509Transport: registered.transport,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Distinguishes explicitly supplied ordinary credentials from nullish inheritance. */
|
|
51
|
+
function overridesOrdinaryAuthentication({ apiKey, adminAPIKey }: Partial<ClientOptions>): boolean {
|
|
52
|
+
return (apiKey !== null && apiKey !== undefined) || (adminAPIKey !== null && adminAPIKey !== undefined);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Keeps origin-bound configuration with its owning provider during a client clone. */
|
|
56
|
+
function prepareProviderClone(
|
|
57
|
+
inherited: ClientOptions,
|
|
58
|
+
overrides: Partial<ClientOptions>,
|
|
59
|
+
): ClientOptions['provider'] {
|
|
60
|
+
const inheritedProvider = inherited.provider;
|
|
61
|
+
const replacingProvider = overrides.credential ?? overrides.workloadIdentity;
|
|
62
|
+
const provider = overrides.provider ?? (replacingProvider ? undefined : inheritedProvider);
|
|
63
|
+
|
|
64
|
+
if (provider !== inheritedProvider) {
|
|
65
|
+
delete inherited.baseURL;
|
|
66
|
+
delete inherited.organization;
|
|
67
|
+
delete inherited.project;
|
|
68
|
+
delete inherited.defaultHeaders;
|
|
69
|
+
delete inherited.defaultQuery;
|
|
70
|
+
delete inherited.fetchOptions;
|
|
71
|
+
delete inherited.fetch;
|
|
72
|
+
}
|
|
73
|
+
if (provider) {
|
|
74
|
+
delete inherited.apiKey;
|
|
75
|
+
delete inherited.adminAPIKey;
|
|
76
|
+
delete inherited.credential;
|
|
77
|
+
delete inherited.workloadIdentity;
|
|
78
|
+
delete inherited.x509Transport;
|
|
79
|
+
delete inherited.baseURL;
|
|
80
|
+
}
|
|
81
|
+
return provider;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Reconciles one client's credential and provider ownership before cloning its options. */
|
|
85
|
+
export function prepareX509ClientClone(
|
|
86
|
+
inherited: ClientOptions,
|
|
87
|
+
overrides: Partial<ClientOptions>,
|
|
88
|
+
credential: X509Credential | undefined,
|
|
89
|
+
currentlyX509: boolean,
|
|
90
|
+
): { credential: X509Credential | undefined; provider: ClientOptions['provider'] } {
|
|
91
|
+
const nextIdentity = hasOwn(overrides, 'workloadIdentity')
|
|
92
|
+
? overrides.workloadIdentity
|
|
93
|
+
: inherited.workloadIdentity;
|
|
94
|
+
const dropping =
|
|
95
|
+
credential !== undefined &&
|
|
96
|
+
((overridesOrdinaryAuthentication(overrides) && overrides.workloadIdentity === undefined) ||
|
|
97
|
+
overrides.provider !== undefined);
|
|
98
|
+
if (credential !== undefined && hasOwn(overrides, 'workloadIdentity')) {
|
|
99
|
+
delete inherited.x509Transport;
|
|
100
|
+
}
|
|
101
|
+
const inheritedCredential =
|
|
102
|
+
credential !== undefined &&
|
|
103
|
+
!dropping &&
|
|
104
|
+
overrides.credential === undefined &&
|
|
105
|
+
!hasOwn(overrides, 'workloadIdentity') &&
|
|
106
|
+
!hasOwn(overrides, 'x509Transport')
|
|
107
|
+
? credential
|
|
108
|
+
: undefined;
|
|
109
|
+
const nextCredential = overrides.credential === undefined ? inheritedCredential : overrides.credential;
|
|
110
|
+
const nextX509 = nextCredential !== undefined || (!dropping && isX509WorkloadIdentity(nextIdentity));
|
|
111
|
+
|
|
112
|
+
if (currentlyX509 !== nextX509) {
|
|
113
|
+
delete inherited.fetch;
|
|
114
|
+
delete inherited.baseURL;
|
|
115
|
+
delete inherited.organization;
|
|
116
|
+
delete inherited.project;
|
|
117
|
+
delete inherited.defaultHeaders;
|
|
118
|
+
delete inherited.defaultQuery;
|
|
119
|
+
delete inherited.fetchOptions;
|
|
120
|
+
if (nextX509) {
|
|
121
|
+
inherited.apiKey = null;
|
|
122
|
+
} else {
|
|
123
|
+
delete inherited.x509Transport;
|
|
124
|
+
if (dropping) {
|
|
125
|
+
delete inherited.workloadIdentity;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (nextCredential !== undefined) {
|
|
131
|
+
delete inherited.apiKey;
|
|
132
|
+
delete inherited.adminAPIKey;
|
|
133
|
+
delete inherited.workloadIdentity;
|
|
134
|
+
delete inherited.x509Transport;
|
|
135
|
+
inherited.credential = nextCredential;
|
|
136
|
+
if (overrides.credential !== undefined) {
|
|
137
|
+
delete inherited.organization;
|
|
138
|
+
delete inherited.project;
|
|
139
|
+
delete inherited.defaultHeaders;
|
|
140
|
+
delete inherited.defaultQuery;
|
|
141
|
+
delete inherited.fetchOptions;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return { credential: nextCredential, provider: prepareProviderClone(inherited, overrides) };
|
|
146
|
+
}
|
|
@@ -15,7 +15,13 @@ export interface X509TransportOptions {
|
|
|
15
15
|
/** X.509 transport currently supports genuine Node.js runtimes only. */
|
|
16
16
|
runtime: 'node';
|
|
17
17
|
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Caller-owned Undici Agent or ProxyAgent; the SDK never closes or inspects it.
|
|
20
|
+
*
|
|
21
|
+
* The application attests that its dispatcher, factories, TLS verification,
|
|
22
|
+
* certificate selection, and CONNECT proxy configuration are trustworthy.
|
|
23
|
+
* Use `fromX509` when the SDK should own and enforce transport configuration.
|
|
24
|
+
*/
|
|
19
25
|
dispatcher: Agent | ProxyAgent;
|
|
20
26
|
|
|
21
27
|
/** Attests that the dispatcher uses one static workload-certificate identity. */
|
|
@@ -150,10 +156,11 @@ function assertConnectProxySupport(): void {
|
|
|
150
156
|
* `certificateIdentity: 'static'` is an application attestation: the SDK does
|
|
151
157
|
* not inspect certificates, private dispatcher internals, callbacks, or TLS
|
|
152
158
|
* options and cannot cryptographically prove certificate selection. Configure
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
159
|
+
* trusted dispatcher factories, verified target and proxy TLS, one static
|
|
160
|
+
* certificate identity, and independently scoped CONNECT credentials.
|
|
161
|
+
* Prefer the SDK-owned `fromX509` credential when these guarantees should be
|
|
162
|
+
* enforced at construction. Rotation requires a fresh caller-owned dispatcher
|
|
163
|
+
* and capability; the application remains responsible for draining it.
|
|
157
164
|
*
|
|
158
165
|
* This Node-only preview entrypoint requires the optional `undici` peer at
|
|
159
166
|
* version 5.2.0 or later. CONNECT proxy modes require version 5.5.1 or
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { OpenAIError } from '../../core/error';
|
|
2
2
|
import type { NullableHeaders } from '../headers';
|
|
3
|
+
import type { X509WorkloadIdentity } from '../../auth/types';
|
|
3
4
|
import type { ReadableStream } from '../shim-types';
|
|
4
5
|
import type { MergedRequestInit } from '../types';
|
|
5
6
|
import { findRegisteredX509Transport } from '#x509-transport-state';
|
|
@@ -49,6 +50,12 @@ export interface X509Transport {
|
|
|
49
50
|
readonly [x509TransportBrand]: true;
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
/** Immutable selectors and transport privately registered for an SDK-owned credential. */
|
|
54
|
+
export interface RegisteredX509Credential {
|
|
55
|
+
readonly identity: X509WorkloadIdentity;
|
|
56
|
+
readonly transport: X509Transport;
|
|
57
|
+
}
|
|
58
|
+
|
|
52
59
|
/** Validated short-lived token exchanged using a registered certificate identity. */
|
|
53
60
|
export interface X509ExchangedToken {
|
|
54
61
|
/** Header-safe, in-memory OpenAI bearer credential. */
|
|
@@ -63,6 +70,8 @@ export interface X509RequestScope {
|
|
|
63
70
|
wallStartedAt: number;
|
|
64
71
|
monotonicStartedAt: number;
|
|
65
72
|
deadlineArmed?: boolean;
|
|
73
|
+
preparationStartedAt?: number;
|
|
74
|
+
preparationWallStartedAt?: number;
|
|
66
75
|
request?: { signal: AbortSignal | null | undefined; timeout: number; fetchOptions: MergedRequestInit };
|
|
67
76
|
phase?: 'planning' | 'authorizing';
|
|
68
77
|
effectiveSignal?: AbortSignal;
|
|
@@ -4,6 +4,7 @@ const transientX509ConnectionErrors = new WeakSet();
|
|
|
4
4
|
const retryableX509IssuerErrors = new WeakSet();
|
|
5
5
|
const approvedX509Clients = new WeakSet();
|
|
6
6
|
const approvedX509OAuthErrors = new WeakMap();
|
|
7
|
+
const approvedX509Credentials = new WeakMap();
|
|
7
8
|
|
|
8
9
|
/** Looks up an opaque capability without exposing the registry itself. */
|
|
9
10
|
export const findRegisteredX509Transport = WeakMap.prototype.get.bind(registeredX509Transports);
|
|
@@ -34,3 +35,9 @@ export const rememberX509OAuthError = WeakMap.prototype.set.bind(approvedX509OAu
|
|
|
34
35
|
|
|
35
36
|
/** Retrieves trusted metadata when public OAuth errors cross module formats. */
|
|
36
37
|
export const findX509OAuthError = WeakMap.prototype.get.bind(approvedX509OAuthErrors);
|
|
38
|
+
|
|
39
|
+
/** Privately binds SDK-owned credentials without importing Node or optional transport peers. */
|
|
40
|
+
export const rememberX509Credential = WeakMap.prototype.set.bind(approvedX509Credentials);
|
|
41
|
+
|
|
42
|
+
/** Resolves only credentials registered by the optional Node authentication helper. */
|
|
43
|
+
export const findX509Credential = WeakMap.prototype.get.bind(approvedX509Credentials);
|
|
@@ -4,6 +4,7 @@ const transientX509ConnectionErrors = new WeakSet();
|
|
|
4
4
|
const retryableX509IssuerErrors = new WeakSet();
|
|
5
5
|
const approvedX509Clients = new WeakSet();
|
|
6
6
|
const approvedX509OAuthErrors = new WeakMap();
|
|
7
|
+
const approvedX509Credentials = new WeakMap();
|
|
7
8
|
|
|
8
9
|
/** Looks up an opaque capability without exposing the registry itself. */
|
|
9
10
|
export const findRegisteredX509Transport = WeakMap.prototype.get.bind(registeredX509Transports);
|
|
@@ -34,3 +35,9 @@ export const rememberX509OAuthError = WeakMap.prototype.set.bind(approvedX509OAu
|
|
|
34
35
|
|
|
35
36
|
/** Retrieves trusted OAuth metadata for public cross-module error normalization. */
|
|
36
37
|
export const findX509OAuthError = WeakMap.prototype.get.bind(approvedX509OAuthErrors);
|
|
38
|
+
|
|
39
|
+
/** Privately binds SDK-owned credentials to their immutable identity and approved transport. */
|
|
40
|
+
export const rememberX509Credential = WeakMap.prototype.set.bind(approvedX509Credentials);
|
|
41
|
+
|
|
42
|
+
/** Resolves only first-class credentials created by the optional Node transport helper. */
|
|
43
|
+
export const findX509Credential = WeakMap.prototype.get.bind(approvedX509Credentials);
|
|
@@ -6,7 +6,9 @@ import type { HeadersLike, NullableHeaders } from '../headers';
|
|
|
6
6
|
import type { FinalRequestOptions } from '../request-options';
|
|
7
7
|
import { CancelReadableStream } from '../shims';
|
|
8
8
|
import type { MergedRequestInit } from '../types';
|
|
9
|
+
import { isSensitiveHeader } from '../utils/log';
|
|
9
10
|
import { hasOwn } from '../utils/values';
|
|
11
|
+
import { assertX509APIOrigin } from './x509-api-origin';
|
|
10
12
|
import { resolveX509Transport } from './x509-transport-registry';
|
|
11
13
|
import {
|
|
12
14
|
isApprovedX509Client,
|
|
@@ -21,10 +23,6 @@ import type {
|
|
|
21
23
|
X509Transport,
|
|
22
24
|
} from './x509-transport-registry';
|
|
23
25
|
|
|
24
|
-
/** Sole API authority approved for OpenAI X.509 workload-identity federation. */
|
|
25
|
-
export const X509_API_BASE_URL = 'https://mtls.api.openai.com/v1';
|
|
26
|
-
|
|
27
|
-
const X509_API_ORIGIN = 'https://mtls.api.openai.com';
|
|
28
26
|
const FORBIDDEN_TRANSPORT_OPTIONS = ['dispatcher', 'agent', 'client', 'tls', 'proxy'];
|
|
29
27
|
const headerValue = (headers: Headers, name: string): string | null =>
|
|
30
28
|
Headers.prototype.get.call(headers, name);
|
|
@@ -39,12 +37,7 @@ const userAbortError = (signal: AbortSignal): APIUserAbortError => {
|
|
|
39
37
|
function assertSafeHeaders(headers: Headers): void {
|
|
40
38
|
for (const name of Headers.prototype.keys.call(headers)) {
|
|
41
39
|
const canonical = name.toLowerCase().split('_').join('-');
|
|
42
|
-
if (
|
|
43
|
-
canonical === 'api-key' ||
|
|
44
|
-
canonical === 'x-api-key' ||
|
|
45
|
-
canonical === 'proxy-authorization' ||
|
|
46
|
-
canonical === 'host'
|
|
47
|
-
) {
|
|
40
|
+
if ((canonical !== 'authorization' && isSensitiveHeader(canonical)) || canonical === 'host') {
|
|
48
41
|
throw new OpenAIError('X.509 workload identity cannot send conflicting authentication credentials.');
|
|
49
42
|
}
|
|
50
43
|
}
|
|
@@ -180,21 +173,6 @@ export function assertX509WebSocketSupported(client: unknown): void {
|
|
|
180
173
|
}
|
|
181
174
|
}
|
|
182
175
|
|
|
183
|
-
/** Rejects every destination outside the sole enrolled, global X.509 API authority. */
|
|
184
|
-
export function assertX509APIOrigin(value: string | URL): URL {
|
|
185
|
-
let target: URL;
|
|
186
|
-
try {
|
|
187
|
-
target = new URL(value);
|
|
188
|
-
} catch {
|
|
189
|
-
throw new OpenAIError('X.509 workload identity requires the approved global mTLS API origin.');
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (target.origin !== X509_API_ORIGIN || target.username || target.password) {
|
|
193
|
-
throw new OpenAIError('X.509 workload identity requires the approved global mTLS API origin.');
|
|
194
|
-
}
|
|
195
|
-
return target;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
176
|
/** Prevents caller options from replacing the immutable transport selected at construction. */
|
|
199
177
|
export function assertX509FetchOptions(options: MergedRequestInit | RequestInit | undefined): void {
|
|
200
178
|
if (!options) {
|
|
@@ -235,6 +213,7 @@ export class X509WorkloadIdentityAuth {
|
|
|
235
213
|
readonly #identityProviderId: string;
|
|
236
214
|
readonly #serviceAccountId: string;
|
|
237
215
|
readonly #configuredRefreshBufferMs: number | undefined;
|
|
216
|
+
readonly #configuredRefreshBufferSeconds: number | undefined;
|
|
238
217
|
readonly #organization: string | null;
|
|
239
218
|
readonly #project: string | null;
|
|
240
219
|
readonly #transport: RegisteredX509Transport;
|
|
@@ -254,15 +233,32 @@ export class X509WorkloadIdentityAuth {
|
|
|
254
233
|
this.#identityProviderId = identity.identityProviderId;
|
|
255
234
|
this.#serviceAccountId = identity.serviceAccountId;
|
|
256
235
|
this.#configuredRefreshBufferMs = identity.refreshBufferMs;
|
|
236
|
+
this.#configuredRefreshBufferSeconds = identity.refreshBufferSeconds;
|
|
257
237
|
this.#organization = organization;
|
|
258
238
|
this.#project = project;
|
|
239
|
+
if (this.#configuredRefreshBufferMs !== undefined && this.#configuredRefreshBufferSeconds !== undefined) {
|
|
240
|
+
throw new OpenAIError(
|
|
241
|
+
'X.509 workload identity cannot combine refreshBufferSeconds and refreshBufferMs.',
|
|
242
|
+
);
|
|
243
|
+
}
|
|
259
244
|
if (
|
|
260
245
|
this.#configuredRefreshBufferMs !== undefined &&
|
|
261
246
|
(!Number.isSafeInteger(this.#configuredRefreshBufferMs) || this.#configuredRefreshBufferMs < 0)
|
|
262
247
|
) {
|
|
263
248
|
throw new OpenAIError('X.509 workload identity requires a nonnegative integer refreshBufferMs.');
|
|
264
249
|
}
|
|
265
|
-
|
|
250
|
+
if (
|
|
251
|
+
this.#configuredRefreshBufferSeconds !== undefined &&
|
|
252
|
+
(!Number.isSafeInteger(this.#configuredRefreshBufferSeconds) ||
|
|
253
|
+
this.#configuredRefreshBufferSeconds < 0 ||
|
|
254
|
+
!Number.isSafeInteger(this.#configuredRefreshBufferSeconds * 1000))
|
|
255
|
+
) {
|
|
256
|
+
throw new OpenAIError('X.509 workload identity requires a nonnegative integer refreshBufferSeconds.');
|
|
257
|
+
}
|
|
258
|
+
this.#refreshBufferMs =
|
|
259
|
+
this.#configuredRefreshBufferSeconds === undefined
|
|
260
|
+
? (this.#configuredRefreshBufferMs ?? DEFAULT_REFRESH_BUFFER_MS)
|
|
261
|
+
: this.#configuredRefreshBufferSeconds * 1000;
|
|
266
262
|
}
|
|
267
263
|
|
|
268
264
|
/** Reconstructs the immutable selectors captured before caller-owned identity mutation. */
|
|
@@ -274,6 +270,9 @@ export class X509WorkloadIdentityAuth {
|
|
|
274
270
|
...(this.#configuredRefreshBufferMs === undefined
|
|
275
271
|
? {}
|
|
276
272
|
: { refreshBufferMs: this.#configuredRefreshBufferMs }),
|
|
273
|
+
...(this.#configuredRefreshBufferSeconds === undefined
|
|
274
|
+
? {}
|
|
275
|
+
: { refreshBufferSeconds: this.#configuredRefreshBufferSeconds }),
|
|
277
276
|
};
|
|
278
277
|
}
|
|
279
278
|
|
|
@@ -365,6 +364,15 @@ export class X509WorkloadIdentityAuth {
|
|
|
365
364
|
return request;
|
|
366
365
|
}
|
|
367
366
|
|
|
367
|
+
/** Suspends an already-running network budget during retry-local asynchronous preparation. */
|
|
368
|
+
beginRequestPreparation(): void {
|
|
369
|
+
const scope = this.#scope();
|
|
370
|
+
if (scope.deadlineArmed && scope.preparationStartedAt === undefined) {
|
|
371
|
+
scope.preparationStartedAt = performance.now();
|
|
372
|
+
scope.preparationWallStartedAt = Date.now();
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
368
376
|
/** Begins local request construction without charging protected hook latency to the network. */
|
|
369
377
|
beginRequestPlanning(): void {
|
|
370
378
|
this.#scope().phase = 'planning';
|
|
@@ -377,6 +385,11 @@ export class X509WorkloadIdentityAuth {
|
|
|
377
385
|
scope.wallStartedAt = Date.now();
|
|
378
386
|
scope.monotonicStartedAt = performance.now();
|
|
379
387
|
scope.deadlineArmed = true;
|
|
388
|
+
} else if (scope.preparationStartedAt !== undefined) {
|
|
389
|
+
scope.monotonicStartedAt += performance.now() - scope.preparationStartedAt;
|
|
390
|
+
scope.wallStartedAt += Date.now() - (scope.preparationWallStartedAt ?? Date.now());
|
|
391
|
+
delete scope.preparationStartedAt;
|
|
392
|
+
delete scope.preparationWallStartedAt;
|
|
380
393
|
}
|
|
381
394
|
}
|
|
382
395
|
|
|
@@ -550,6 +563,8 @@ export class X509WorkloadIdentityAuth {
|
|
|
550
563
|
delete scope.request;
|
|
551
564
|
delete scope.phase;
|
|
552
565
|
delete scope.deadlineArmed;
|
|
566
|
+
delete scope.preparationStartedAt;
|
|
567
|
+
delete scope.preparationWallStartedAt;
|
|
553
568
|
delete scope.effectiveSignal;
|
|
554
569
|
delete scope.materializedBody;
|
|
555
570
|
delete scope.apiURL;
|
|
@@ -820,13 +835,7 @@ export class X509WorkloadIdentityAuth {
|
|
|
820
835
|
'X.509 workload identity cannot override its enrolled organization or project.',
|
|
821
836
|
);
|
|
822
837
|
}
|
|
823
|
-
if (
|
|
824
|
-
canonical === 'authorization' ||
|
|
825
|
-
canonical === 'api-key' ||
|
|
826
|
-
canonical === 'x-api-key' ||
|
|
827
|
-
canonical === 'proxy-authorization' ||
|
|
828
|
-
canonical === 'host'
|
|
829
|
-
) {
|
|
838
|
+
if (isSensitiveHeader(canonical) || canonical === 'host') {
|
|
830
839
|
throw new OpenAIError(
|
|
831
840
|
'X.509 workload identity cannot use caller-supplied authentication credentials.',
|
|
832
841
|
);
|
package/src/internal/uploads.ts
CHANGED
|
@@ -348,7 +348,8 @@ const hasStreamingUploadableValue = (value: unknown): boolean => {
|
|
|
348
348
|
return value.some(hasStreamingUploadableValue);
|
|
349
349
|
}
|
|
350
350
|
if (value && typeof value === 'object' && !isBlob(value) && !(value instanceof Response)) {
|
|
351
|
-
|
|
351
|
+
// Own properties only, matching what form encoding serializes; inherited values are never encoded.
|
|
352
|
+
for (const k of Object.keys(value)) {
|
|
352
353
|
if (hasStreamingUploadableValue((value as Record<string, unknown>)[k])) {
|
|
353
354
|
return true;
|
|
354
355
|
}
|
|
@@ -365,7 +366,8 @@ const hasUploadableValue = (value: unknown): boolean => {
|
|
|
365
366
|
return value.some(hasUploadableValue);
|
|
366
367
|
}
|
|
367
368
|
if (value && typeof value === 'object') {
|
|
368
|
-
|
|
369
|
+
// Own properties only, matching what form encoding serializes; inherited values are never encoded.
|
|
370
|
+
for (const k of Object.keys(value)) {
|
|
369
371
|
if (hasUploadableValue((value as any)[k])) {
|
|
370
372
|
return true;
|
|
371
373
|
}
|