@apifuse/provider-sdk 2.2.0-beta.62 → 2.2.0-beta.63
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/AUTHORING.md +268 -9
- package/CHANGELOG.md +4 -0
- package/README.md +1 -0
- package/bin/apifuse-check.ts +84 -2
- package/bin/apifuse-dev.ts +11 -31
- package/bin/apifuse-migrate-deployment.ts +200 -0
- package/bin/apifuse-pack-check.ts +11 -4
- package/bin/apifuse-pack-types.ts +187 -4
- package/bin/apifuse-record.ts +44 -32
- package/bin/apifuse-test.ts +3 -1
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +11 -0
- package/dist/cli/deployment-intent.d.ts +178 -0
- package/dist/cli/deployment-intent.js +277 -0
- package/dist/cli/migrate-deployment-intent.d.ts +132 -0
- package/dist/cli/migrate-deployment-intent.js +1552 -0
- package/dist/cli/migrate-provider-shape.d.ts +5 -1
- package/dist/cli/migrate-provider-shape.js +4 -2
- package/dist/cli/resolver-runtime.d.ts +21 -0
- package/dist/cli/resolver-runtime.js +31 -0
- package/dist/define.d.ts +3 -0
- package/dist/define.js +5 -0
- package/dist/dev.d.ts +3 -1
- package/dist/dev.js +6 -1
- package/dist/engine.d.ts +35 -1
- package/dist/engine.js +21 -2
- package/dist/error-resolution.d.ts +1 -0
- package/dist/error-resolution.js +54 -0
- package/dist/errors.d.ts +39 -1
- package/dist/errors.js +50 -0
- package/dist/i18n/error-messages.d.ts +86 -0
- package/dist/i18n/error-messages.js +192 -0
- package/dist/i18n/index.d.ts +1 -0
- package/dist/i18n/index.js +1 -0
- package/dist/i18n/keys.d.ts +12 -0
- package/dist/i18n/keys.js +76 -5
- package/dist/index.d.ts +7 -1
- package/dist/index.js +6 -0
- package/dist/lint.d.ts +10 -0
- package/dist/lint.js +412 -49
- package/dist/provider.d.ts +5 -5
- package/dist/provider.js +2 -2
- package/dist/runtime/browser-telemetry-binding.d.ts +5 -0
- package/dist/runtime/browser-telemetry-binding.js +83 -0
- package/dist/runtime/browser-telemetry.d.ts +76 -0
- package/dist/runtime/browser-telemetry.js +165 -0
- package/dist/runtime/browser.d.ts +3 -0
- package/dist/runtime/browser.js +208 -47
- package/dist/runtime/cache-telemetry.d.ts +71 -0
- package/dist/runtime/cache-telemetry.js +177 -0
- package/dist/runtime/cache.js +67 -7
- package/dist/runtime/capability-response.d.ts +3 -0
- package/dist/runtime/capability-response.js +56 -0
- package/dist/runtime/capability-telemetry.d.ts +46 -0
- package/dist/runtime/capability-telemetry.js +68 -0
- package/dist/runtime/diagnostic-env.d.ts +3 -0
- package/dist/runtime/diagnostic-env.js +10 -1
- package/dist/runtime/engine-mode.d.ts +74 -0
- package/dist/runtime/engine-mode.js +152 -0
- package/dist/runtime/http-telemetry-guard.d.ts +10 -0
- package/dist/runtime/http-telemetry-guard.js +62 -10
- package/dist/runtime/http.js +141 -35
- package/dist/runtime/instrumentation.js +92 -13
- package/dist/runtime/ocr-telemetry.d.ts +82 -0
- package/dist/runtime/ocr-telemetry.js +192 -0
- package/dist/runtime/ocr.js +30 -8
- package/dist/runtime/request-telemetry.d.ts +2 -0
- package/dist/runtime/request-telemetry.js +2 -1
- package/dist/runtime/resolver-runtime-options.d.ts +14 -0
- package/dist/runtime/resolver-runtime-options.js +33 -0
- package/dist/runtime/resolver-shared.d.ts +3 -2
- package/dist/runtime/resolver-shared.js +4 -4
- package/dist/runtime/resolver-telemetry.d.ts +1 -0
- package/dist/runtime/resolver-telemetry.js +1 -1
- package/dist/runtime/resolver-vendors/browser.d.ts +2 -0
- package/dist/runtime/resolver-vendors/browser.js +46 -0
- package/dist/runtime/resolver-vendors/hosts.d.ts +21 -0
- package/dist/runtime/resolver-vendors/hosts.js +35 -0
- package/dist/runtime/resolver-vendors/hypersolutions.js +2 -2
- package/dist/runtime/resolver-vendors/types.d.ts +5 -4
- package/dist/runtime/resolver.d.ts +4 -1
- package/dist/runtime/resolver.js +46 -13
- package/dist/runtime/state-telemetry.d.ts +79 -0
- package/dist/runtime/state-telemetry.js +194 -0
- package/dist/runtime/state.d.ts +3 -0
- package/dist/runtime/state.js +26 -7
- package/dist/runtime/stealth-cookies.d.ts +5 -1
- package/dist/runtime/stealth-cookies.js +21 -1
- package/dist/runtime/stealth-owned-headers.d.ts +26 -0
- package/dist/runtime/stealth-owned-headers.js +37 -0
- package/dist/runtime/stealth-telemetry.d.ts +92 -0
- package/dist/runtime/stealth-telemetry.js +179 -0
- package/dist/runtime/stealth.d.ts +4 -1
- package/dist/runtime/stealth.js +228 -85
- package/dist/runtime/stt-telemetry.d.ts +77 -0
- package/dist/runtime/stt-telemetry.js +176 -0
- package/dist/runtime/stt.js +15 -6
- package/dist/server/error-stack-frames.d.ts +35 -0
- package/dist/server/error-stack-frames.js +219 -0
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.js +4 -0
- package/dist/server/sdk-version.d.ts +3 -0
- package/dist/server/sdk-version.js +19 -0
- package/dist/server/self-test.js +1 -18
- package/dist/server/sensitive-values.d.ts +1 -1
- package/dist/server/sensitive-values.js +9 -0
- package/dist/server/serve-implementation.d.ts +51 -2
- package/dist/server/serve-implementation.js +337 -90
- package/dist/server/types.d.ts +1 -0
- package/dist/server/types.js +7 -0
- package/dist/stateful/stateful-provider-owner-forwarder.js +22 -0
- package/dist/stateful/stateful-provider-runtime-context.d.ts +3 -0
- package/dist/stateful/stateful-provider-runtime-context.js +1 -0
- package/dist/types.d.ts +94 -1
- package/package.json +6 -1
- package/src/cli/commands.ts +14 -0
- package/src/cli/deployment-intent.ts +364 -0
- package/src/cli/migrate-deployment-intent.ts +1908 -0
- package/src/cli/migrate-provider-shape.ts +10 -4
- package/src/cli/resolver-runtime.ts +72 -0
- package/src/define.ts +13 -0
- package/src/dev.ts +9 -2
- package/src/engine.ts +38 -2
- package/src/error-resolution.ts +58 -0
- package/src/errors.ts +77 -1
- package/src/i18n/error-messages.ts +255 -0
- package/src/i18n/index.ts +1 -0
- package/src/i18n/keys.ts +80 -6
- package/src/index.ts +72 -0
- package/src/lint.ts +522 -50
- package/src/provider.ts +10 -0
- package/src/runtime/browser-telemetry-binding.ts +92 -0
- package/src/runtime/browser-telemetry.ts +289 -0
- package/src/runtime/browser.ts +249 -65
- package/src/runtime/cache-telemetry.ts +247 -0
- package/src/runtime/cache.ts +68 -17
- package/src/runtime/capability-response.ts +53 -0
- package/src/runtime/capability-telemetry.ts +101 -0
- package/src/runtime/diagnostic-env.ts +11 -1
- package/src/runtime/engine-mode.ts +233 -0
- package/src/runtime/http-telemetry-guard.ts +66 -13
- package/src/runtime/http.ts +168 -37
- package/src/runtime/instrumentation.ts +105 -16
- package/src/runtime/ocr-telemetry.ts +308 -0
- package/src/runtime/ocr.ts +109 -74
- package/src/runtime/request-telemetry.ts +2 -1
- package/src/runtime/resolver-runtime-options.ts +50 -0
- package/src/runtime/resolver-shared.ts +9 -4
- package/src/runtime/resolver-telemetry.ts +1 -1
- package/src/runtime/resolver-vendors/browser.ts +65 -7
- package/src/runtime/resolver-vendors/hosts.ts +43 -0
- package/src/runtime/resolver-vendors/hypersolutions.ts +2 -2
- package/src/runtime/resolver-vendors/types.ts +5 -4
- package/src/runtime/resolver.ts +64 -21
- package/src/runtime/state-telemetry.ts +298 -0
- package/src/runtime/state.ts +46 -45
- package/src/runtime/stealth-cookies.ts +24 -1
- package/src/runtime/stealth-owned-headers.ts +41 -0
- package/src/runtime/stealth-telemetry.ts +304 -0
- package/src/runtime/stealth.ts +355 -150
- package/src/runtime/stt-telemetry.ts +282 -0
- package/src/runtime/stt.ts +93 -71
- package/src/server/error-stack-frames.ts +214 -0
- package/src/server/index.ts +53 -0
- package/src/server/sdk-version.ts +22 -0
- package/src/server/self-test.ts +1 -21
- package/src/server/sensitive-values.ts +14 -0
- package/src/server/serve-implementation.ts +569 -108
- package/src/server/types.ts +7 -0
- package/src/stateful/stateful-provider-owner-forwarder.ts +23 -0
- package/src/stateful/stateful-provider-runtime-context.ts +4 -0
- package/src/types.ts +99 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +0 -10
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-a.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-b.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-barrel.ts.txt +0 -2
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-computed.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-a.ts.txt +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-b.ts.txt +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-barrel.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-catalog.ts.txt +0 -19
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-docs.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-stores.ts.txt +0 -8
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-indirect-config.ts.txt +0 -5
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-nonstatic.ts.txt +0 -4
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-package-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-leaf.ts.txt +0 -10
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-one.ts.txt +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-two.ts.txt +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-one.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-two.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-mixed-index.ts.txt +0 -14
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-registry.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-barrel.ts.txt +0 -2
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-leaf.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +0 -8
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-hoisted-array-spread.ts.txt +0 -15
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-imported-array-spread.ts.txt +0 -9
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-kebab-operation-id.ts.txt +0 -17
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +0 -5
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-mixed-array-spread.ts.txt +0 -24
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +0 -16
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-snake-operation-id.ts.txt +0 -21
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-duplicate-provider.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-id-unresolved.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-many.ts.txt +0 -18
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-one.ts.txt +0 -13
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +0 -31
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +0 -21
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +0 -14
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-en.json +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ja.json +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ko.json +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-existing-operation-namespace-en.json +0 -8
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-binding-ambiguous.ts.txt +0 -12
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported-other.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported.ts.txt +0 -5
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-key-ambiguous.ts.txt +0 -19
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-shorthand.ts.txt +0 -9
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-typed.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-unrelated.ts.txt +0 -13
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +0 -15
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +0 -4
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/verbatim-template.ts.txt +0 -12
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bindCapabilityRoot } from "../runtime/capability-telemetry.js";
|
|
1
2
|
import { STATUS_CODES } from "node:http";
|
|
2
3
|
import { readDiagnosticEnv, withDiagnosticEnv } from "../runtime/diagnostic-env.js";
|
|
3
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -11,14 +12,22 @@ import { z } from "zod";
|
|
|
11
12
|
import { AuthAbortError, createAuthFlowHelpers } from "../auth.js";
|
|
12
13
|
import { validateFailClosedDeclaration } from "../declaration-validation.js";
|
|
13
14
|
import {
|
|
14
|
-
createInProcessProviderEngine,
|
|
15
15
|
ENGINE_OWNED_PROXY_CREDENTIAL_ENV_NAMES,
|
|
16
16
|
isEngineOwnedEnvName,
|
|
17
17
|
type ProviderEngine,
|
|
18
18
|
type ProviderEngineBindingCandidates,
|
|
19
|
+
type ProviderEngineMode,
|
|
19
20
|
readEngineProxyCredentials,
|
|
20
21
|
} from "../engine.js";
|
|
22
|
+
import {
|
|
23
|
+
createEngineForMode,
|
|
24
|
+
isUnavailableProviderEngine,
|
|
25
|
+
type ProviderEngineModeResolution,
|
|
26
|
+
resolveProviderEngineMode,
|
|
27
|
+
} from "../runtime/engine-mode.js";
|
|
28
|
+
import { SDK_VERSION } from "./sdk-version.js";
|
|
21
29
|
import { safeProviderErrorObservability } from "../error-observability.js";
|
|
30
|
+
import { providerErrorStackFrames } from "./error-stack-frames.js";
|
|
22
31
|
import {
|
|
23
32
|
SDK_OWNED_PROVIDER_ERROR_CODES,
|
|
24
33
|
SDK_RUNTIME_OWNED_ERROR_CODES,
|
|
@@ -41,6 +50,15 @@ import {
|
|
|
41
50
|
localizeAuthTurn,
|
|
42
51
|
type ProviderLocaleCatalogMap,
|
|
43
52
|
} from "../i18n/catalog.js";
|
|
53
|
+
import {
|
|
54
|
+
acceptLanguageHeaderValue,
|
|
55
|
+
DEFAULT_PROVIDER_ERROR_LOCALE,
|
|
56
|
+
localizeProviderErrorText,
|
|
57
|
+
PROVIDER_ERROR_LOCALES,
|
|
58
|
+
type ProviderErrorMessageParams,
|
|
59
|
+
type ProviderErrorTextCandidate,
|
|
60
|
+
resolveProviderErrorLocale,
|
|
61
|
+
} from "../i18n/error-messages.js";
|
|
44
62
|
import type { ProviderLocale } from "../i18n/keys.js";
|
|
45
63
|
import {
|
|
46
64
|
categoryForStatus,
|
|
@@ -53,6 +71,7 @@ import {
|
|
|
53
71
|
import { createScratchpad } from "../runtime/auth-flow.js";
|
|
54
72
|
import type * as BrowserRuntimeModule from "../runtime/browser.js";
|
|
55
73
|
import { createProviderCache } from "../runtime/cache.js";
|
|
74
|
+
import { bindCacheTelemetry, CacheTelemetryCollector } from "../runtime/cache-telemetry.js";
|
|
56
75
|
import type { HandleTelemetryEvent } from "../handle.js";
|
|
57
76
|
import { createHandleContext } from "../runtime/handle.js";
|
|
58
77
|
import { createCredentialContext } from "../runtime/credential.js";
|
|
@@ -86,6 +105,7 @@ import { HttpTelemetryCollector, type HttpTelemetryLogPayload } from "../runtime
|
|
|
86
105
|
import { wrapWithInstrumentation } from "../runtime/instrumentation.js";
|
|
87
106
|
import type * as NativeNetworkRuntimeModule from "../runtime/native-network.js";
|
|
88
107
|
import { createOcrClientFromEnv } from "../runtime/ocr.js";
|
|
108
|
+
import { bindOcrTelemetry, OcrTelemetryCollector } from "../runtime/ocr-telemetry.js";
|
|
89
109
|
import { getProviderBaseUrl } from "../runtime/provider.js";
|
|
90
110
|
import {
|
|
91
111
|
PROXY_AUTH_IP_DENIED_CODE,
|
|
@@ -116,6 +136,10 @@ import {
|
|
|
116
136
|
ResolverTelemetryCollector,
|
|
117
137
|
type ResolverTelemetryLogPayload,
|
|
118
138
|
} from "../runtime/resolver-telemetry.js";
|
|
139
|
+
import {
|
|
140
|
+
createResolverRuntimeOptions,
|
|
141
|
+
resolveNativeProxyPolicy,
|
|
142
|
+
} from "../runtime/resolver-runtime-options.js";
|
|
119
143
|
import {
|
|
120
144
|
assertRequiredSecretsPresent,
|
|
121
145
|
listMissingRequiredSecrets,
|
|
@@ -125,10 +149,22 @@ import {
|
|
|
125
149
|
createProviderRuntimeStateFromEnv,
|
|
126
150
|
createUnsupportedProviderRuntimeState,
|
|
127
151
|
} from "../runtime/state.js";
|
|
152
|
+
import {
|
|
153
|
+
StateTelemetryCollector,
|
|
154
|
+
instrumentProviderRuntimeState,
|
|
155
|
+
} from "../runtime/state-telemetry.js";
|
|
128
156
|
import type * as StealthRuntimeModule from "../runtime/stealth.js";
|
|
129
157
|
import type { StealthChallengeRuntime } from "../runtime/stealth-akamai-sbsd.js";
|
|
130
158
|
import { StealthCookieJar } from "../runtime/stealth-cookies.js";
|
|
159
|
+
import { StealthTelemetryCollector } from "../runtime/stealth-telemetry.js";
|
|
160
|
+
import { BrowserTelemetryCollector } from "../runtime/browser-telemetry.js";
|
|
161
|
+
import {
|
|
162
|
+
bindBrowserTelemetry,
|
|
163
|
+
isSupportedBrowserBinding,
|
|
164
|
+
warnUnsupportedBrowserBinding,
|
|
165
|
+
} from "../runtime/browser-telemetry-binding.js";
|
|
131
166
|
import { createSttClientFromEnv } from "../runtime/stt.js";
|
|
167
|
+
import { bindSttTelemetry, SttTelemetryCollector } from "../runtime/stt-telemetry.js";
|
|
132
168
|
import {
|
|
133
169
|
createTraceContext,
|
|
134
170
|
getTraceRecorder,
|
|
@@ -169,8 +205,8 @@ import type {
|
|
|
169
205
|
ProviderDefinition,
|
|
170
206
|
ProviderErrorStatus,
|
|
171
207
|
ProviderFilesContext,
|
|
172
|
-
ProviderProxyPolicy,
|
|
173
208
|
ProviderRuntimeState,
|
|
209
|
+
ProviderRuntimeTarget,
|
|
174
210
|
ProviderStreamEvent,
|
|
175
211
|
ResolverContext,
|
|
176
212
|
StealthClient,
|
|
@@ -238,7 +274,6 @@ function providerErrorCode(error: unknown): string | undefined {
|
|
|
238
274
|
}
|
|
239
275
|
}
|
|
240
276
|
|
|
241
|
-
const AUTH_FLOW_LOCALES = ["en", "ko", "ja"] as const;
|
|
242
277
|
const retryResponseMeta = new WeakMap<ProviderContext, HttpTelemetryCollector>();
|
|
243
278
|
const STATEFUL_INTERNAL_OPERATIONS_ROUTE = "/__apifuse/stateful/operations";
|
|
244
279
|
const STATEFUL_FORWARDING_SOURCE_POD_HEADER = "x-apifuse-stateful-source-pod";
|
|
@@ -272,6 +307,22 @@ export const ProviderServerStatefulForwardEnvelopeSchema = z
|
|
|
272
307
|
})
|
|
273
308
|
.strict();
|
|
274
309
|
|
|
310
|
+
// Inbound compatibility boundary for rolling deploys, mirroring the outbound
|
|
311
|
+
// one in stateful-provider-owner-forwarder.ts: emitted envelopes stay strict
|
|
312
|
+
// (above), while a received envelope's nested operation request keeps the
|
|
313
|
+
// gateway route's tolerance and strips unknown keys. A source pod on a newer
|
|
314
|
+
// SDK carrying an additive OperationRequestSchema field (tenantId, #164) must
|
|
315
|
+
// not be rejected with STATEFUL_FORWARDING_ENVELOPE_INVALID by an owner pod
|
|
316
|
+
// that predates the field: the rejection is terminal for the operation and the
|
|
317
|
+
// two pods coexist for the whole rolling update. The envelope's own keys stay
|
|
318
|
+
// strict on both sides, because they are protocol rather than payload.
|
|
319
|
+
const ReceivedStatefulForwardEnvelopeSchema =
|
|
320
|
+
ProviderServerStatefulForwardEnvelopeSchema.extend({
|
|
321
|
+
operationRequest: z.object(
|
|
322
|
+
ProviderServerStatefulForwardEnvelopeSchema.shape.operationRequest.shape,
|
|
323
|
+
),
|
|
324
|
+
});
|
|
325
|
+
|
|
275
326
|
export type ProviderServerStatefulForwardEnvelope = Readonly<
|
|
276
327
|
z.infer<typeof ProviderServerStatefulForwardEnvelopeSchema>
|
|
277
328
|
>;
|
|
@@ -599,36 +650,6 @@ function getProviderStealthProfile(provider: ProviderDefinition) {
|
|
|
599
650
|
|
|
600
651
|
type ResolverRuntimeOptions = ResolverRuntimeModule.ResolverRuntimeOptions;
|
|
601
652
|
|
|
602
|
-
/** One option set for ctx.resolver and the automatic SBSD solve, so both share identity scope, cache, proxy intent, and telemetry. */
|
|
603
|
-
function createResolverRuntimeOptions(
|
|
604
|
-
provider: ProviderDefinition,
|
|
605
|
-
cache: ReturnType<typeof createProviderCache>,
|
|
606
|
-
identityScope: string,
|
|
607
|
-
proxyPolicy: ProviderProxyPolicy | undefined,
|
|
608
|
-
proxyClientOptions: Omit<
|
|
609
|
-
NonNullable<ResolverRuntimeOptions["proxyIntent"]>,
|
|
610
|
-
"mode" | "userAgent"
|
|
611
|
-
>,
|
|
612
|
-
stealthProfile: { readonly userAgent: string } | undefined,
|
|
613
|
-
telemetry: ResolverTelemetryCollector,
|
|
614
|
-
): ResolverRuntimeOptions {
|
|
615
|
-
return {
|
|
616
|
-
allowedHosts: provider.allowedHosts,
|
|
617
|
-
cache,
|
|
618
|
-
telemetry,
|
|
619
|
-
identityScope,
|
|
620
|
-
...(proxyPolicy
|
|
621
|
-
? {
|
|
622
|
-
proxyIntent: {
|
|
623
|
-
mode: proxyPolicy.mode,
|
|
624
|
-
...proxyClientOptions,
|
|
625
|
-
...(stealthProfile ? { userAgent: stealthProfile.userAgent } : {}),
|
|
626
|
-
},
|
|
627
|
-
}
|
|
628
|
-
: {}),
|
|
629
|
-
};
|
|
630
|
-
}
|
|
631
|
-
|
|
632
653
|
/** Shared predicate for the SBSD challenge runtime and the ceremony egress lease. */
|
|
633
654
|
function declaresAkamaiSbsdResolver(provider: ProviderDefinition): boolean {
|
|
634
655
|
return provider.resolver?.kinds.some((kind) => kind === "akamai_sbsd") === true;
|
|
@@ -660,12 +681,17 @@ function createStealthChallengeDetection(
|
|
|
660
681
|
async solve(challenge, transport, solveSignal) {
|
|
661
682
|
const resolver = resolverRuntime.bindResolverSignal(
|
|
662
683
|
(resolverOverride
|
|
663
|
-
? bindResolverTelemetry(
|
|
684
|
+
? bindResolverTelemetry(
|
|
685
|
+
resolverOverride,
|
|
686
|
+
resolverOptions.telemetry,
|
|
687
|
+
resolverOptions.browserTelemetry,
|
|
688
|
+
)
|
|
664
689
|
: undefined) ??
|
|
665
690
|
resolverRuntime.createResolverClientFromEnv(provider.resolver, undefined, {
|
|
666
691
|
allowedHosts: resolverOptions.allowedHosts,
|
|
667
692
|
cache: resolverOptions.cache,
|
|
668
693
|
telemetry: resolverOptions.telemetry,
|
|
694
|
+
browserTelemetry: resolverOptions.browserTelemetry,
|
|
669
695
|
identityScope: resolverOptions.identityScope,
|
|
670
696
|
// No proxyIntent: the transport is already bound to the initiating
|
|
671
697
|
// request's lease, and a second identity resolution would allocate
|
|
@@ -800,20 +826,18 @@ function resolveOperationConnectionId(
|
|
|
800
826
|
// absent so it can never override a valid id or key a real scope. Requests
|
|
801
827
|
// without any usable id fall back to the documented missing-connection
|
|
802
828
|
// sentinel scope instead of scoping context/affinity/state under "".
|
|
803
|
-
return (
|
|
804
|
-
normalizeConnectionId(request.connection?.id) ?? normalizeConnectionId(request.connectionId)
|
|
805
|
-
);
|
|
829
|
+
return normalizeIdentifier(request.connection?.id) ?? normalizeIdentifier(request.connectionId);
|
|
806
830
|
}
|
|
807
831
|
|
|
808
|
-
function
|
|
809
|
-
|
|
832
|
+
function resolveOperationTenantId(request: Pick<OperationRequest, "tenantId">): string | undefined {
|
|
833
|
+
// Only the gateway-asserted envelope field is consulted; caller headers can
|
|
834
|
+
// never populate the principal scope. Same rule as connection ids: "" is a
|
|
835
|
+
// malformed identifier, not a principal.
|
|
836
|
+
return normalizeIdentifier(request.tenantId);
|
|
810
837
|
}
|
|
811
838
|
|
|
812
|
-
function
|
|
813
|
-
|
|
814
|
-
if (provider.proxy === true) return { mode: "optional" };
|
|
815
|
-
if (provider.proxy === false) return { mode: "disabled" };
|
|
816
|
-
return undefined;
|
|
839
|
+
function normalizeIdentifier(id: string | undefined): string | undefined {
|
|
840
|
+
return id === "" ? undefined : id;
|
|
817
841
|
}
|
|
818
842
|
|
|
819
843
|
function providerSecretNames(provider: ProviderDefinition): string[] {
|
|
@@ -823,12 +847,19 @@ function providerSecretNames(provider: ProviderDefinition): string[] {
|
|
|
823
847
|
}
|
|
824
848
|
|
|
825
849
|
type RequestScopeContext = {
|
|
850
|
+
run<T>(fn: () => Promise<T>): Promise<T>;
|
|
826
851
|
redact: DiagnosticRedactor;
|
|
827
852
|
trace: RuntimeTraceContext;
|
|
828
853
|
telemetry: RequestTelemetry;
|
|
829
854
|
resolverTelemetry: ResolverTelemetryCollector;
|
|
830
855
|
nativeTelemetry: NativeTelemetryCollector;
|
|
831
856
|
httpTelemetry: HttpTelemetryCollector;
|
|
857
|
+
stealthTelemetry: StealthTelemetryCollector;
|
|
858
|
+
ocrTelemetry: OcrTelemetryCollector;
|
|
859
|
+
sttTelemetry: SttTelemetryCollector;
|
|
860
|
+
browserTelemetry: BrowserTelemetryCollector;
|
|
861
|
+
cacheTelemetry: CacheTelemetryCollector;
|
|
862
|
+
stateTelemetry: StateTelemetryCollector;
|
|
832
863
|
};
|
|
833
864
|
|
|
834
865
|
function createProviderContext(
|
|
@@ -856,7 +887,9 @@ function createProviderContext(
|
|
|
856
887
|
proxyClientOptions.affinityKey,
|
|
857
888
|
request.requestId,
|
|
858
889
|
);
|
|
859
|
-
const cache = createProviderCache(
|
|
890
|
+
const cache = createProviderCache(
|
|
891
|
+
bindCacheTelemetry({ providerId: provider.id }, scope.cacheTelemetry),
|
|
892
|
+
);
|
|
860
893
|
const { capabilityModules } = options;
|
|
861
894
|
const resolverOptions = createResolverRuntimeOptions(
|
|
862
895
|
provider,
|
|
@@ -866,6 +899,7 @@ function createProviderContext(
|
|
|
866
899
|
proxyClientOptions,
|
|
867
900
|
stealthProfile,
|
|
868
901
|
scope.resolverTelemetry,
|
|
902
|
+
scope.browserTelemetry,
|
|
869
903
|
);
|
|
870
904
|
const challengeRuntime = createStealthChallengeDetection(
|
|
871
905
|
provider,
|
|
@@ -876,6 +910,7 @@ function createProviderContext(
|
|
|
876
910
|
signal,
|
|
877
911
|
);
|
|
878
912
|
const stealthClientOptions = {
|
|
913
|
+
stealthTelemetry: scope.stealthTelemetry,
|
|
879
914
|
upstream: proxyClientOptions.upstream,
|
|
880
915
|
affinityKey: proxyClientOptions.affinityKey,
|
|
881
916
|
telemetry: scope.telemetry.proxy,
|
|
@@ -911,11 +946,19 @@ function createProviderContext(
|
|
|
911
946
|
scopes: request.connection?.scopes,
|
|
912
947
|
values: request.connection?.secrets,
|
|
913
948
|
});
|
|
949
|
+
const operationTenantId = resolveOperationTenantId(request);
|
|
914
950
|
const requestContext = {
|
|
915
951
|
connectionId: resolveOperationConnectionId(request),
|
|
952
|
+
// Absent stays absent: an own property with an undefined value would
|
|
953
|
+
// change the shape of ctx.request for every existing consumer.
|
|
954
|
+
...(operationTenantId !== undefined ? { tenantId: operationTenantId } : {}),
|
|
916
955
|
headers: request.headers ?? {},
|
|
917
956
|
};
|
|
918
|
-
const requestState =
|
|
957
|
+
const requestState = instrumentProviderRuntimeState(
|
|
958
|
+
state,
|
|
959
|
+
scope.stateTelemetry,
|
|
960
|
+
scope.redact,
|
|
961
|
+
).forConnection(requestContext.connectionId);
|
|
919
962
|
const bindings: ProviderEngineBindingCandidates = {
|
|
920
963
|
env,
|
|
921
964
|
credential,
|
|
@@ -942,6 +985,7 @@ function createProviderContext(
|
|
|
942
985
|
requireCdpPool: isProductionProviderBrowserMode(provider),
|
|
943
986
|
stealth: true,
|
|
944
987
|
engine: provider.browser?.engine,
|
|
988
|
+
telemetry: scope.browserTelemetry,
|
|
945
989
|
})
|
|
946
990
|
: createBrowserStub(),
|
|
947
991
|
...(provider.native
|
|
@@ -962,12 +1006,22 @@ function createProviderContext(
|
|
|
962
1006
|
: {}),
|
|
963
1007
|
trace: scope.trace,
|
|
964
1008
|
auth: createAuthStub(),
|
|
965
|
-
ocr:
|
|
966
|
-
|
|
1009
|
+
ocr: bindCapabilityRoot(
|
|
1010
|
+
bindOcrTelemetry(options.ocr ?? createOcrClientFromEnv(provider.ocr), scope.ocrTelemetry),
|
|
1011
|
+
scope.run,
|
|
1012
|
+
),
|
|
1013
|
+
stt: bindCapabilityRoot(
|
|
1014
|
+
bindSttTelemetry(options.stt ?? createSttClientFromEnv(provider.stt), scope.sttTelemetry),
|
|
1015
|
+
scope.run,
|
|
1016
|
+
),
|
|
967
1017
|
resolver: capabilityModules.resolver
|
|
968
1018
|
? capabilityModules.resolver.bindResolverSignal(
|
|
969
1019
|
(options.resolver
|
|
970
|
-
? bindResolverTelemetry(
|
|
1020
|
+
? bindResolverTelemetry(
|
|
1021
|
+
options.resolver,
|
|
1022
|
+
resolverOptions.telemetry,
|
|
1023
|
+
resolverOptions.browserTelemetry,
|
|
1024
|
+
)
|
|
971
1025
|
: undefined) ??
|
|
972
1026
|
capabilityModules.resolver.createResolverClientFromEnv(
|
|
973
1027
|
provider.resolver,
|
|
@@ -978,7 +1032,11 @@ function createProviderContext(
|
|
|
978
1032
|
)
|
|
979
1033
|
: bindResolverSignalWithoutRuntime(
|
|
980
1034
|
(options.resolver
|
|
981
|
-
? bindResolverTelemetry(
|
|
1035
|
+
? bindResolverTelemetry(
|
|
1036
|
+
options.resolver,
|
|
1037
|
+
resolverOptions.telemetry,
|
|
1038
|
+
resolverOptions.browserTelemetry,
|
|
1039
|
+
)
|
|
982
1040
|
: undefined) ??
|
|
983
1041
|
createUnsupportedResolverClient("Provider does not declare resolver capability"),
|
|
984
1042
|
signal,
|
|
@@ -996,6 +1054,7 @@ function createProviderContext(
|
|
|
996
1054
|
}),
|
|
997
1055
|
};
|
|
998
1056
|
const candidateHttp = bindings.http;
|
|
1057
|
+
const candidateBrowser = bindings.browser;
|
|
999
1058
|
const attached = options.engine.attach({ provider, bindings }) as ProviderContext;
|
|
1000
1059
|
const hostHttp = provider.http ? attached.http : undefined;
|
|
1001
1060
|
const supportedHostHttp = provider.http && isSupportedHttpBinding(hostHttp);
|
|
@@ -1004,19 +1063,31 @@ function createProviderContext(
|
|
|
1004
1063
|
supportedHostHttp && hostHttp !== candidateHttp
|
|
1005
1064
|
? bindHttpTelemetry(hostHttp, scope.httpTelemetry)
|
|
1006
1065
|
: undefined;
|
|
1007
|
-
const
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1066
|
+
const hostBrowser = provider.runtime === "browser" ? attached.browser : undefined;
|
|
1067
|
+
const supportedHostBrowser =
|
|
1068
|
+
provider.runtime === "browser" && isSupportedBrowserBinding(hostBrowser);
|
|
1069
|
+
if (provider.runtime === "browser" && !supportedHostBrowser) warnUnsupportedBrowserBinding();
|
|
1070
|
+
const observedBrowser =
|
|
1071
|
+
supportedHostBrowser && hostBrowser !== candidateBrowser
|
|
1072
|
+
? bindBrowserTelemetry(hostBrowser, scope.browserTelemetry)
|
|
1073
|
+
: undefined;
|
|
1074
|
+
const contextTarget =
|
|
1075
|
+
observedBrowser || observedHttp
|
|
1014
1076
|
? new Proxy(attached, {
|
|
1015
1077
|
get(target, property, receiver) {
|
|
1016
|
-
|
|
1078
|
+
if (property === "http" && observedHttp) return observedHttp;
|
|
1079
|
+
if (property === "http" && provider.http && !supportedHostHttp) return undefined;
|
|
1080
|
+
if (property === "browser" && observedBrowser) return observedBrowser;
|
|
1081
|
+
return Reflect.get(target, property, receiver);
|
|
1017
1082
|
},
|
|
1018
1083
|
})
|
|
1019
|
-
:
|
|
1084
|
+
: provider.http && !supportedHostHttp
|
|
1085
|
+
? new Proxy(attached, {
|
|
1086
|
+
get(target, property, receiver) {
|
|
1087
|
+
return property === "http" ? undefined : Reflect.get(target, property, receiver);
|
|
1088
|
+
},
|
|
1089
|
+
})
|
|
1090
|
+
: attached;
|
|
1020
1091
|
const context = wrapWithInstrumentation(contextTarget);
|
|
1021
1092
|
retryResponseMeta.set(context, scope.httpTelemetry);
|
|
1022
1093
|
return context;
|
|
@@ -1133,7 +1204,9 @@ function createAuthFlowContext(
|
|
|
1133
1204
|
proxyClientOptions.affinityKey,
|
|
1134
1205
|
request.requestId,
|
|
1135
1206
|
);
|
|
1136
|
-
const cache = createProviderCache(
|
|
1207
|
+
const cache = createProviderCache(
|
|
1208
|
+
bindCacheTelemetry({ providerId: provider.id }, scope.cacheTelemetry),
|
|
1209
|
+
);
|
|
1137
1210
|
const { capabilityModules } = options;
|
|
1138
1211
|
const resolverOptions = createResolverRuntimeOptions(
|
|
1139
1212
|
provider,
|
|
@@ -1143,6 +1216,7 @@ function createAuthFlowContext(
|
|
|
1143
1216
|
proxyClientOptions,
|
|
1144
1217
|
stealthProfile,
|
|
1145
1218
|
scope.resolverTelemetry,
|
|
1219
|
+
scope.browserTelemetry,
|
|
1146
1220
|
);
|
|
1147
1221
|
const challengeRuntime = createStealthChallengeDetection(
|
|
1148
1222
|
provider,
|
|
@@ -1153,6 +1227,7 @@ function createAuthFlowContext(
|
|
|
1153
1227
|
signal,
|
|
1154
1228
|
);
|
|
1155
1229
|
const stealthClientOptions = {
|
|
1230
|
+
stealthTelemetry: scope.stealthTelemetry,
|
|
1156
1231
|
upstream: proxyClientOptions.upstream,
|
|
1157
1232
|
affinityKey: proxyClientOptions.affinityKey,
|
|
1158
1233
|
telemetry: scope.telemetry.proxy,
|
|
@@ -1201,12 +1276,28 @@ function createAuthFlowContext(
|
|
|
1201
1276
|
...(signal ? { signal } : {}),
|
|
1202
1277
|
httpTelemetry: scope.httpTelemetry,
|
|
1203
1278
|
}),
|
|
1204
|
-
state:
|
|
1279
|
+
state: instrumentProviderRuntimeState(
|
|
1280
|
+
state,
|
|
1281
|
+
scope.stateTelemetry,
|
|
1282
|
+
scope.redact,
|
|
1283
|
+
).forConnection(resolveOperationConnectionId(request)),
|
|
1205
1284
|
stealth: stealthBaseUrl
|
|
1206
1285
|
? capabilityModules.stealth
|
|
1207
1286
|
? capabilityModules.stealth.createStealthClient(stealthBaseUrl, stealthClientOptions)
|
|
1208
1287
|
: createLazyStealthClient(logStealthCleanupError, stealthBaseUrl, stealthClientOptions)
|
|
1209
1288
|
: createStealthStub(),
|
|
1289
|
+
browser:
|
|
1290
|
+
provider.runtime === "browser"
|
|
1291
|
+
? capabilityModules.browser!.createBrowserClient({
|
|
1292
|
+
allowedHosts: provider.allowedHosts,
|
|
1293
|
+
cdpUrl: readDiagnosticEnv("APIFUSE__CDP_POOL__URL"),
|
|
1294
|
+
headless: true,
|
|
1295
|
+
requireCdpPool: isProductionProviderBrowserMode(provider),
|
|
1296
|
+
stealth: true,
|
|
1297
|
+
engine: provider.browser?.engine,
|
|
1298
|
+
telemetry: scope.browserTelemetry,
|
|
1299
|
+
})
|
|
1300
|
+
: createBrowserStub(),
|
|
1210
1301
|
...(provider.native
|
|
1211
1302
|
? {
|
|
1212
1303
|
native: {
|
|
@@ -1229,12 +1320,22 @@ function createAuthFlowContext(
|
|
|
1229
1320
|
]),
|
|
1230
1321
|
credential,
|
|
1231
1322
|
context: flowContextStore.context,
|
|
1232
|
-
ocr:
|
|
1233
|
-
|
|
1323
|
+
ocr: bindCapabilityRoot(
|
|
1324
|
+
bindOcrTelemetry(options.ocr ?? createOcrClientFromEnv(provider.ocr), scope.ocrTelemetry),
|
|
1325
|
+
scope.run,
|
|
1326
|
+
),
|
|
1327
|
+
stt: bindCapabilityRoot(
|
|
1328
|
+
bindSttTelemetry(options.stt ?? createSttClientFromEnv(provider.stt), scope.sttTelemetry),
|
|
1329
|
+
scope.run,
|
|
1330
|
+
),
|
|
1234
1331
|
resolver: capabilityModules.resolver
|
|
1235
1332
|
? capabilityModules.resolver.bindResolverSignal(
|
|
1236
1333
|
(options.resolver
|
|
1237
|
-
? bindResolverTelemetry(
|
|
1334
|
+
? bindResolverTelemetry(
|
|
1335
|
+
options.resolver,
|
|
1336
|
+
resolverOptions.telemetry,
|
|
1337
|
+
resolverOptions.browserTelemetry,
|
|
1338
|
+
)
|
|
1238
1339
|
: undefined) ??
|
|
1239
1340
|
capabilityModules.resolver.createResolverClientFromEnv(
|
|
1240
1341
|
provider.resolver,
|
|
@@ -1245,7 +1346,11 @@ function createAuthFlowContext(
|
|
|
1245
1346
|
)
|
|
1246
1347
|
: bindResolverSignalWithoutRuntime(
|
|
1247
1348
|
(options.resolver
|
|
1248
|
-
? bindResolverTelemetry(
|
|
1349
|
+
? bindResolverTelemetry(
|
|
1350
|
+
options.resolver,
|
|
1351
|
+
resolverOptions.telemetry,
|
|
1352
|
+
resolverOptions.browserTelemetry,
|
|
1353
|
+
)
|
|
1249
1354
|
: undefined) ??
|
|
1250
1355
|
createUnsupportedResolverClient("Provider does not declare resolver capability"),
|
|
1251
1356
|
signal,
|
|
@@ -1309,6 +1414,7 @@ export type ProviderServerLogEvent =
|
|
|
1309
1414
|
retryable?: boolean;
|
|
1310
1415
|
providerObservability?: ProviderErrorObservability;
|
|
1311
1416
|
causeChain?: ProviderErrorCauseFrame[];
|
|
1417
|
+
stack?: string[];
|
|
1312
1418
|
signal?: "unregistered_provider_error_code";
|
|
1313
1419
|
signalFix?: string;
|
|
1314
1420
|
issues?: Array<{ path: string; code: string; message: string }>;
|
|
@@ -1319,6 +1425,13 @@ export type ProviderServerLogEvent =
|
|
|
1319
1425
|
providerId: string;
|
|
1320
1426
|
missingSecrets: string[];
|
|
1321
1427
|
}
|
|
1428
|
+
| {
|
|
1429
|
+
/** Emitted once at boot when `locales/en.json` exists but cannot be read. */
|
|
1430
|
+
level: "warn";
|
|
1431
|
+
event: "provider_locale_catalogs_unavailable";
|
|
1432
|
+
providerId: string;
|
|
1433
|
+
reason: string;
|
|
1434
|
+
}
|
|
1322
1435
|
| ({
|
|
1323
1436
|
level: "info";
|
|
1324
1437
|
event: "provider_handle";
|
|
@@ -1342,6 +1455,27 @@ export type ProviderServerLogEvent =
|
|
|
1342
1455
|
errorClass: string;
|
|
1343
1456
|
message: string;
|
|
1344
1457
|
}
|
|
1458
|
+
| {
|
|
1459
|
+
/** `warn` when the requested attachment could not be honoured, otherwise `info`. */
|
|
1460
|
+
level: "info" | "warn";
|
|
1461
|
+
event: "provider_engine_mode";
|
|
1462
|
+
providerId: string;
|
|
1463
|
+
/** Resolved attachment; `custom` for an opaque host engine. */
|
|
1464
|
+
mode: ProviderEngineMode | "custom";
|
|
1465
|
+
source: "engine" | "env" | "option" | "default";
|
|
1466
|
+
/** True for every non-remote attachment, whose removal is scheduled by ADR-0011. */
|
|
1467
|
+
deprecated: boolean;
|
|
1468
|
+
/** False when the engine refuses to attach; `/readyz` reports 503. */
|
|
1469
|
+
attached: boolean;
|
|
1470
|
+
sdkVersion: string;
|
|
1471
|
+
runtimeTarget?: ProviderRuntimeTarget;
|
|
1472
|
+
/**
|
|
1473
|
+
* Non-fatal selection findings, e.g. `invalid_env_value`,
|
|
1474
|
+
* `env_overrode_option`, `engine_object_overrode_requested_mode`,
|
|
1475
|
+
* `engine_env_ignored`. Absent when the selection was unambiguous.
|
|
1476
|
+
*/
|
|
1477
|
+
warnings?: readonly string[];
|
|
1478
|
+
}
|
|
1345
1479
|
| SelfTestCancellationLogEvent;
|
|
1346
1480
|
|
|
1347
1481
|
export type ProviderServerLogger = (event: ProviderServerLogEvent) => void;
|
|
@@ -1350,6 +1484,17 @@ export type ProviderServerOptions<TContext extends Partial<ProviderContext> = Pr
|
|
|
1350
1484
|
logger?: ProviderServerLogger;
|
|
1351
1485
|
/** Capability attachment boundary. Defaults to the local in-process engine. */
|
|
1352
1486
|
engine?: ProviderEngine;
|
|
1487
|
+
/**
|
|
1488
|
+
* Explicit engine attachment mode. Lowest precedence of the three inputs:
|
|
1489
|
+
* an explicit `engine` object wins, then `APIFUSE__ENGINE__MODE` (the
|
|
1490
|
+
* deployment outranks provider code), then this option, then the in-process
|
|
1491
|
+
* default. Disagreement is never fatal — it is reported on the
|
|
1492
|
+
* `provider_engine_mode` boot event (`env_overrode_option`,
|
|
1493
|
+
* `engine_object_overrode_requested_mode`), which only `serve` emits. A value
|
|
1494
|
+
* that is not a known mode throws `ValidationError`, because unlike the env it
|
|
1495
|
+
* can only come from calling code.
|
|
1496
|
+
*/
|
|
1497
|
+
engineMode?: ProviderEngineMode;
|
|
1353
1498
|
/** Request-file resolver supplied by the engine host when `files` is declared. */
|
|
1354
1499
|
files?: ProviderFilesContext;
|
|
1355
1500
|
/** Optional provider-specific operation executor. Stateful providers use this to preserve provider-local runtime semantics. */
|
|
@@ -1373,6 +1518,15 @@ export type ProviderServerOptions<TContext extends Partial<ProviderContext> = Pr
|
|
|
1373
1518
|
* resolver is reported as one custom invocation; its internal vendor work is not exposed.
|
|
1374
1519
|
*/
|
|
1375
1520
|
resolver?: ResolverContext;
|
|
1521
|
+
/**
|
|
1522
|
+
* Pre-loaded provider locale catalogs used to localize auth turns and the
|
|
1523
|
+
* client-facing `message`/`fix` of provider errors.
|
|
1524
|
+
*
|
|
1525
|
+
* Defaults to reading `locales/{en,ko,ja}.json` from the provider directory
|
|
1526
|
+
* once at server construction. Supply this to bundle catalogs instead of
|
|
1527
|
+
* probing the filesystem, or to pin catalogs in tests.
|
|
1528
|
+
*/
|
|
1529
|
+
localeCatalogs?: ProviderLocaleCatalogMap;
|
|
1376
1530
|
/** Optional runtime state override for tests or custom hosts. Production resolves Redis from env and fails closed when unavailable. */
|
|
1377
1531
|
state?: ProviderRuntimeState;
|
|
1378
1532
|
/** Allow process-local runtime state only for local development and tests. */
|
|
@@ -1467,11 +1621,116 @@ function publicErrorSource(error: unknown, category: ProviderErrorCategory): Pro
|
|
|
1467
1621
|
return sourceForCategory(category);
|
|
1468
1622
|
}
|
|
1469
1623
|
|
|
1624
|
+
/**
|
|
1625
|
+
* Serve-time inputs for localizing the client-facing error envelope.
|
|
1626
|
+
*
|
|
1627
|
+
* Only `error.message` and `error.fix` of the response body are localized.
|
|
1628
|
+
* Logs, cause frames, OTLP attributes and the error-observability header keep
|
|
1629
|
+
* the English literal the provider threw.
|
|
1630
|
+
*/
|
|
1631
|
+
type ErrorEnvelopeLocalization = {
|
|
1632
|
+
readonly catalogs?: ProviderLocaleCatalogMap;
|
|
1633
|
+
readonly locale: ProviderLocale;
|
|
1634
|
+
readonly declaredErrorCode?: OperationErrorCode;
|
|
1635
|
+
};
|
|
1636
|
+
|
|
1637
|
+
function localeKeyOption(error: unknown, key: "messageKey" | "fixKey"): string | undefined {
|
|
1638
|
+
const value = providerErrorOption(error, key);
|
|
1639
|
+
return typeof value === "string" ? value : undefined;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
function declaredLocaleKey(
|
|
1643
|
+
declaration: OperationErrorCode | undefined,
|
|
1644
|
+
key: "messageKey" | "fixKey",
|
|
1645
|
+
): string | undefined {
|
|
1646
|
+
const value = declaration?.[key];
|
|
1647
|
+
return typeof value === "string" ? value : undefined;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
function providerErrorParams(error: unknown): ProviderErrorMessageParams | undefined {
|
|
1651
|
+
const params = providerErrorOption(error, "params");
|
|
1652
|
+
return params !== null && typeof params === "object" && !Array.isArray(params)
|
|
1653
|
+
? (params as ProviderErrorMessageParams)
|
|
1654
|
+
: undefined;
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
/**
|
|
1658
|
+
* Resolution order for one localized envelope field:
|
|
1659
|
+
*
|
|
1660
|
+
* 1. the throw site's `messageKey`/`fixKey`;
|
|
1661
|
+
* 2. the declared `errorCodes[]` entry's key for the thrown code;
|
|
1662
|
+
* 3. the derived `errors.<code>.<field>` convention;
|
|
1663
|
+
* 4. the English literal the provider threw.
|
|
1664
|
+
*
|
|
1665
|
+
* Steps 2 and 3 are skipped for SDK-owned failures (transport, Zod, stateful
|
|
1666
|
+
* deadline, SDK runtime codes): their text is the SDK's contract, and a
|
|
1667
|
+
* provider catalog entry must not be able to relabel one. An explicit key on
|
|
1668
|
+
* the thrown error still wins, because that is the provider deliberately
|
|
1669
|
+
* describing an error it constructed itself.
|
|
1670
|
+
*/
|
|
1671
|
+
function localizedErrorField(
|
|
1672
|
+
error: unknown,
|
|
1673
|
+
field: "message" | "fix",
|
|
1674
|
+
literal: string | undefined,
|
|
1675
|
+
localization: ErrorEnvelopeLocalization | undefined,
|
|
1676
|
+
): string | undefined {
|
|
1677
|
+
if (!localization?.catalogs) return literal;
|
|
1678
|
+
const optionKey = field === "message" ? "messageKey" : "fixKey";
|
|
1679
|
+
const candidates: ProviderErrorTextCandidate[] = [
|
|
1680
|
+
{ kind: "key", key: localeKeyOption(error, optionKey) },
|
|
1681
|
+
];
|
|
1682
|
+
if (!sdkOwnsErrorResolution(error)) {
|
|
1683
|
+
candidates.push(
|
|
1684
|
+
{ kind: "key", key: declaredLocaleKey(localization.declaredErrorCode, optionKey) },
|
|
1685
|
+
{ kind: "derived", code: providerErrorCode(error), field },
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
return localizeProviderErrorText({
|
|
1689
|
+
catalogs: localization.catalogs,
|
|
1690
|
+
locale: localization.locale,
|
|
1691
|
+
candidates,
|
|
1692
|
+
params: providerErrorParams(error),
|
|
1693
|
+
fallback: literal,
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
/**
|
|
1698
|
+
* Reads `accept-language` out of the request envelope's `headers` map.
|
|
1699
|
+
*
|
|
1700
|
+
* The gateway stamps the negotiated locale there for auth flows (it persists
|
|
1701
|
+
* the start locale on the flow state so continue/poll turns stay in one
|
|
1702
|
+
* language), and the operation route merges HTTP headers into the same map, so
|
|
1703
|
+
* the envelope is the higher-precedence source.
|
|
1704
|
+
*/
|
|
1705
|
+
function requestBodyAcceptLanguage(rawBody: unknown): string | undefined {
|
|
1706
|
+
if (!rawBody || typeof rawBody !== "object") return undefined;
|
|
1707
|
+
const headers = Object.getOwnPropertyDescriptor(rawBody, "headers")?.value;
|
|
1708
|
+
if (!headers || typeof headers !== "object" || Array.isArray(headers)) return undefined;
|
|
1709
|
+
return acceptLanguageHeaderValue(headers as Record<string, unknown>);
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
function errorEnvelopeLocalization(options: {
|
|
1713
|
+
readonly catalogs: ProviderLocaleCatalogMap | undefined;
|
|
1714
|
+
readonly requestHeaders?: Headers;
|
|
1715
|
+
readonly rawBody?: unknown;
|
|
1716
|
+
readonly declaredErrorCode?: OperationErrorCode;
|
|
1717
|
+
}): ErrorEnvelopeLocalization {
|
|
1718
|
+
return {
|
|
1719
|
+
...(options.catalogs ? { catalogs: options.catalogs } : {}),
|
|
1720
|
+
locale: resolveProviderErrorLocale(
|
|
1721
|
+
requestBodyAcceptLanguage(options.rawBody),
|
|
1722
|
+
options.requestHeaders?.get("accept-language"),
|
|
1723
|
+
),
|
|
1724
|
+
...(options.declaredErrorCode ? { declaredErrorCode: options.declaredErrorCode } : {}),
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1470
1728
|
function toErrorResponse(
|
|
1471
1729
|
error: unknown,
|
|
1472
1730
|
requestId: string | undefined,
|
|
1473
1731
|
observabilityDetails: ErrorObservabilityDetails,
|
|
1474
1732
|
redact?: DiagnosticRedactor,
|
|
1733
|
+
localization?: ErrorEnvelopeLocalization,
|
|
1475
1734
|
): OperationErrorResponse {
|
|
1476
1735
|
const observability = observabilityDetails;
|
|
1477
1736
|
const source = publicErrorSource(error, observability.category);
|
|
@@ -1489,14 +1748,23 @@ function toErrorResponse(
|
|
|
1489
1748
|
|
|
1490
1749
|
if (isProviderError(error)) {
|
|
1491
1750
|
const details = providerErrorOption(error, "details");
|
|
1751
|
+
const literalFix = providerErrorOption(error, "fix");
|
|
1752
|
+
const fix = localizedErrorField(
|
|
1753
|
+
error,
|
|
1754
|
+
"fix",
|
|
1755
|
+
typeof literalFix === "string" && literalFix.length > 0 ? literalFix : undefined,
|
|
1756
|
+
localization,
|
|
1757
|
+
);
|
|
1758
|
+
const literalMessage = publicProviderErrorMessage(error);
|
|
1492
1759
|
return {
|
|
1493
1760
|
error: {
|
|
1494
1761
|
code: providerErrorCode(error) ?? "provider_error",
|
|
1495
|
-
message:
|
|
1762
|
+
message:
|
|
1763
|
+
localizedErrorField(error, "message", literalMessage, localization) ?? literalMessage,
|
|
1496
1764
|
...(requestId ? { requestId } : {}),
|
|
1497
1765
|
retryable: observability.retryable,
|
|
1498
1766
|
source,
|
|
1499
|
-
...(
|
|
1767
|
+
...(fix ? { fix } : {}),
|
|
1500
1768
|
...(details !== undefined ? { details } : {}),
|
|
1501
1769
|
},
|
|
1502
1770
|
};
|
|
@@ -1763,6 +2031,11 @@ function publicProviderErrorMessage(error: ProviderError): string {
|
|
|
1763
2031
|
}
|
|
1764
2032
|
if (providerErrorCode(error) === "transport_timeout") return "Request timed out";
|
|
1765
2033
|
if (providerErrorCode(error) === "transport_network_error") return "Network error";
|
|
2034
|
+
// Provider-local fault: no request was issued, so an upstream-flavored
|
|
2035
|
+
// message would misattribute it (category is provider_error).
|
|
2036
|
+
if (providerErrorCode(error) === "http_header_factory_failed") {
|
|
2037
|
+
return "Request header preparation failed";
|
|
2038
|
+
}
|
|
1766
2039
|
if (providerErrorCode(error) === "upstream_http_error" && error.status) {
|
|
1767
2040
|
return `Upstream request failed with status ${error.status}`;
|
|
1768
2041
|
}
|
|
@@ -1933,6 +2206,11 @@ function logProviderError(
|
|
|
1933
2206
|
const errorClass = error instanceof Error ? error.name : typeof error;
|
|
1934
2207
|
const message = error instanceof Error ? error.message : String(error);
|
|
1935
2208
|
const causeChain = providerErrorCauseChain(error, redact);
|
|
2209
|
+
// Masked 500 bodies carry only errorClass, so the log is the sole channel
|
|
2210
|
+
// that can locate the throw. Declared 5xx (502/504) are included too: their
|
|
2211
|
+
// body carries code+message, but frame[0] is still the throw site inside
|
|
2212
|
+
// provider code. 4xx failures keep the lean record.
|
|
2213
|
+
const stack = status >= 500 ? providerErrorStackFrames(error, redact) : undefined;
|
|
1936
2214
|
const details = observabilityDetails;
|
|
1937
2215
|
const isUnregisteredProviderErrorCode =
|
|
1938
2216
|
status === 500 &&
|
|
@@ -1975,6 +2253,7 @@ function logProviderError(
|
|
|
1975
2253
|
errorClass: redactDiagnosticText(errorClass, redact),
|
|
1976
2254
|
message: sanitizeDiagnosticText(redactDiagnosticText(message, redact)),
|
|
1977
2255
|
...(causeChain ? { causeChain } : {}),
|
|
2256
|
+
...(stack ? { stack } : {}),
|
|
1978
2257
|
...(providerObservability ? { providerObservability } : {}),
|
|
1979
2258
|
...(details.upstreamStatus ? { upstreamStatus: details.upstreamStatus } : {}),
|
|
1980
2259
|
errorCategory: details.category,
|
|
@@ -2247,6 +2526,7 @@ function createRequestScope(input: {
|
|
|
2247
2526
|
),
|
|
2248
2527
|
finished: false,
|
|
2249
2528
|
staticValues: input.staticSensitiveValues,
|
|
2529
|
+
register: sensitiveRegistry.add,
|
|
2250
2530
|
};
|
|
2251
2531
|
const traceConfig = withDiagnosticEnv(observeEnv, () => resolveTraceConfigFromEnv());
|
|
2252
2532
|
const details = {
|
|
@@ -2279,11 +2559,23 @@ function createRequestScope(input: {
|
|
|
2279
2559
|
const resolverCollector = new ResolverTelemetryCollector({ redact });
|
|
2280
2560
|
const nativeCollector = new NativeTelemetryCollector({ redact });
|
|
2281
2561
|
const httpCollector = new HttpTelemetryCollector({ redact });
|
|
2562
|
+
const stealthCollector = new StealthTelemetryCollector({ redact });
|
|
2563
|
+
const ocrCollector = new OcrTelemetryCollector({ redact });
|
|
2564
|
+
const sttCollector = new SttTelemetryCollector({ redact });
|
|
2565
|
+
const browserCollector = new BrowserTelemetryCollector({ redact });
|
|
2566
|
+
const cacheTelemetry = new CacheTelemetryCollector({ redact });
|
|
2567
|
+
const stateTelemetry = new StateTelemetryCollector({ redact });
|
|
2282
2568
|
const telemetry = new RequestTelemetry(trace);
|
|
2283
2569
|
telemetry.register(proxyCollector);
|
|
2284
2570
|
telemetry.register(resolverCollector);
|
|
2285
2571
|
telemetry.register(nativeCollector);
|
|
2286
2572
|
telemetry.register(httpCollector);
|
|
2573
|
+
telemetry.register(stealthCollector);
|
|
2574
|
+
telemetry.register(browserCollector);
|
|
2575
|
+
telemetry.register(ocrCollector);
|
|
2576
|
+
telemetry.register(sttCollector);
|
|
2577
|
+
telemetry.register(cacheTelemetry);
|
|
2578
|
+
telemetry.register(stateTelemetry);
|
|
2287
2579
|
let rootRunner: <T>(fn: () => Promise<T>) => Promise<T> = (fn) => fn();
|
|
2288
2580
|
let resolveRoot!: (outcome: RequestTerminalOutcome) => void;
|
|
2289
2581
|
const rootTerminal = new Promise<RequestTerminalOutcome>((resolve) => {
|
|
@@ -2367,6 +2659,12 @@ function createRequestScope(input: {
|
|
|
2367
2659
|
resolverTelemetry: resolverCollector,
|
|
2368
2660
|
nativeTelemetry: nativeCollector,
|
|
2369
2661
|
httpTelemetry: httpCollector,
|
|
2662
|
+
stealthTelemetry: stealthCollector,
|
|
2663
|
+
ocrTelemetry: ocrCollector,
|
|
2664
|
+
sttTelemetry: sttCollector,
|
|
2665
|
+
browserTelemetry: browserCollector,
|
|
2666
|
+
cacheTelemetry,
|
|
2667
|
+
stateTelemetry,
|
|
2370
2668
|
seedCredentials(rawBody, kind): void {
|
|
2371
2669
|
const harvested = rawRequestCredentials(rawBody, kind);
|
|
2372
2670
|
sensitiveRegistry.add(harvested.values);
|
|
@@ -2925,42 +3223,44 @@ function toAuthFlowResponse(
|
|
|
2925
3223
|
};
|
|
2926
3224
|
}
|
|
2927
3225
|
|
|
2928
|
-
function authFlowLocaleFromHeaders(headers?: Record<string, string>): ProviderLocale {
|
|
2929
|
-
const header = Object.entries(headers ?? {}).find(
|
|
2930
|
-
([key]) => key.toLowerCase() === "accept-language",
|
|
2931
|
-
)?.[1];
|
|
2932
|
-
for (const token of (header ?? "").split(",")) {
|
|
2933
|
-
const language = token.trim().split(";")[0]?.split("-")[0]?.toLowerCase();
|
|
2934
|
-
if (isAuthFlowLocale(language)) {
|
|
2935
|
-
return language;
|
|
2936
|
-
}
|
|
2937
|
-
}
|
|
2938
|
-
return "en";
|
|
2939
|
-
}
|
|
2940
|
-
|
|
2941
|
-
function isAuthFlowLocale(value: string | undefined): value is ProviderLocale {
|
|
2942
|
-
return value === "en" || value === "ko" || value === "ja";
|
|
2943
|
-
}
|
|
2944
|
-
|
|
2945
3226
|
function isAuthTurn(value: unknown): value is AuthTurn {
|
|
2946
3227
|
return !!value && typeof value === "object" && "kind" in value && "turnId" in value;
|
|
2947
3228
|
}
|
|
2948
3229
|
|
|
2949
|
-
|
|
3230
|
+
/**
|
|
3231
|
+
* Loads the provider's locale catalogs once, at `createServerApp` time.
|
|
3232
|
+
*
|
|
3233
|
+
* Only the locales that actually exist on disk are read: a provider with
|
|
3234
|
+
* `en`+`ko` but no `ja.json` used to make the whole load throw and silently
|
|
3235
|
+
* disable localization for every turn. Absence of catalogs is not an error —
|
|
3236
|
+
* every localized field falls back to its English literal — but a catalog that
|
|
3237
|
+
* exists and cannot be read is logged once so a packaging mistake is visible at
|
|
3238
|
+
* boot instead of per request.
|
|
3239
|
+
*/
|
|
3240
|
+
function resolveProviderLocaleCatalogs(
|
|
2950
3241
|
provider: ProviderDefinition,
|
|
3242
|
+
options: ProviderServerOptions,
|
|
3243
|
+
logger: ProviderServerLogger,
|
|
2951
3244
|
): ProviderLocaleCatalogMap | undefined {
|
|
3245
|
+
if (options.localeCatalogs) return options.localeCatalogs;
|
|
2952
3246
|
for (const providerDir of [
|
|
2953
3247
|
process.cwd(),
|
|
2954
3248
|
join(process.cwd(), "providers", provider.id),
|
|
2955
3249
|
join(process.cwd(), "providers-staging", provider.id),
|
|
2956
3250
|
]) {
|
|
2957
|
-
|
|
3251
|
+
const locales = PROVIDER_ERROR_LOCALES.filter((locale) =>
|
|
3252
|
+
existsSync(join(providerDir, "locales", `${locale}.json`)),
|
|
3253
|
+
);
|
|
3254
|
+
if (!locales.includes(DEFAULT_PROVIDER_ERROR_LOCALE)) continue;
|
|
2958
3255
|
try {
|
|
2959
|
-
return loadProviderLocaleCatalogs({
|
|
2960
|
-
|
|
2961
|
-
|
|
3256
|
+
return loadProviderLocaleCatalogs({ providerDir, locales });
|
|
3257
|
+
} catch (error) {
|
|
3258
|
+
logger({
|
|
3259
|
+
level: "warn",
|
|
3260
|
+
event: "provider_locale_catalogs_unavailable",
|
|
3261
|
+
providerId: provider.id,
|
|
3262
|
+
reason: sanitizeDiagnosticText(error instanceof Error ? error.message : String(error)),
|
|
2962
3263
|
});
|
|
2963
|
-
} catch {
|
|
2964
3264
|
return undefined;
|
|
2965
3265
|
}
|
|
2966
3266
|
}
|
|
@@ -2968,16 +3268,12 @@ function loadAuthFlowLocaleCatalogs(
|
|
|
2968
3268
|
}
|
|
2969
3269
|
|
|
2970
3270
|
function materializeAuthFlowTurn(
|
|
2971
|
-
provider: ProviderDefinition,
|
|
2972
|
-
request: AuthFlowRequest,
|
|
2973
3271
|
turn: AuthTurn,
|
|
3272
|
+
catalogs: ProviderLocaleCatalogMap | undefined,
|
|
3273
|
+
locale: ProviderLocale,
|
|
2974
3274
|
): AuthTurn {
|
|
2975
|
-
const catalogs = loadAuthFlowLocaleCatalogs(provider);
|
|
2976
3275
|
if (!catalogs) return turn;
|
|
2977
|
-
return localizeAuthTurn(turn, {
|
|
2978
|
-
catalogs,
|
|
2979
|
-
locale: authFlowLocaleFromHeaders(request.headers),
|
|
2980
|
-
});
|
|
3276
|
+
return localizeAuthTurn(turn, { catalogs, locale });
|
|
2981
3277
|
}
|
|
2982
3278
|
|
|
2983
3279
|
function withAuthRequestHeaders(request: AuthFlowRequest, headers: Headers): AuthFlowRequest {
|
|
@@ -3088,6 +3384,14 @@ async function handleAuthFlow(
|
|
|
3088
3384
|
options: ProviderServerRuntimeOptions,
|
|
3089
3385
|
state: ProviderRuntimeState,
|
|
3090
3386
|
scope: RequestScopeContext,
|
|
3387
|
+
/**
|
|
3388
|
+
* Negotiated once per request by the route so a successful turn and an error
|
|
3389
|
+
* envelope from the same request can never disagree. `withAuthRequestHeaders`
|
|
3390
|
+
* lets the HTTP header shadow the envelope's `Accept-Language`, but the
|
|
3391
|
+
* gateway stamps the flow's start locale into the envelope precisely so
|
|
3392
|
+
* continue/poll turns stay in one language, so the envelope wins here.
|
|
3393
|
+
*/
|
|
3394
|
+
locale: ProviderLocale,
|
|
3091
3395
|
signal?: AbortSignal,
|
|
3092
3396
|
): Promise<Response | AuthFlowResponse> {
|
|
3093
3397
|
const flow = provider.auth?.flow;
|
|
@@ -3144,7 +3448,7 @@ async function handleAuthFlow(
|
|
|
3144
3448
|
!(result instanceof Response) &&
|
|
3145
3449
|
!(result instanceof ReadableStream) &&
|
|
3146
3450
|
isAuthTurn(result)
|
|
3147
|
-
? materializeAuthFlowTurn(
|
|
3451
|
+
? materializeAuthFlowTurn(result, options.localeCatalogs, locale)
|
|
3148
3452
|
: result;
|
|
3149
3453
|
return toAuthFlowResponse(materializedResult, getPatch(), getEngineState());
|
|
3150
3454
|
} catch (error) {
|
|
@@ -3290,7 +3594,7 @@ function parseStatefulForwardingEnvelope(
|
|
|
3290
3594
|
rawBody: unknown,
|
|
3291
3595
|
redact: DiagnosticRedactor,
|
|
3292
3596
|
): ProviderServerStatefulForwardEnvelope {
|
|
3293
|
-
const parsed =
|
|
3597
|
+
const parsed = ReceivedStatefulForwardEnvelopeSchema.safeParse(rawBody);
|
|
3294
3598
|
if (parsed.success) return parsed.data;
|
|
3295
3599
|
throw new ProviderError("Stateful forwarding envelope is invalid.", {
|
|
3296
3600
|
code: "STATEFUL_FORWARDING_ENVELOPE_INVALID",
|
|
@@ -3340,16 +3644,78 @@ export function createServerApp<TContext extends Partial<ProviderContext> = Prov
|
|
|
3340
3644
|
);
|
|
3341
3645
|
}
|
|
3342
3646
|
|
|
3647
|
+
/**
|
|
3648
|
+
* Resolve the engine once at boot, before any route exists. An explicit engine
|
|
3649
|
+
* object is used as given; otherwise the resolved mode selects the engine.
|
|
3650
|
+
*
|
|
3651
|
+
* Deliberately never throws for deployment-authored input: a mode this release
|
|
3652
|
+
* cannot serve yields an engine that fails every request and a `/readyz` 503,
|
|
3653
|
+
* because a boot crash would trade a structured signal for CrashLoopBackOff
|
|
3654
|
+
* across the fleet (same trade-off as `provider_secrets_missing` above).
|
|
3655
|
+
*/
|
|
3656
|
+
function resolveServerEngine(serverOptions: ProviderServerOptions): {
|
|
3657
|
+
engine: ProviderEngine;
|
|
3658
|
+
resolution: ProviderEngineModeResolution;
|
|
3659
|
+
} {
|
|
3660
|
+
const resolution = resolveProviderEngineMode({
|
|
3661
|
+
option: serverOptions.engineMode,
|
|
3662
|
+
engine: serverOptions.engine,
|
|
3663
|
+
});
|
|
3664
|
+
if (serverOptions.engine !== undefined) return { engine: serverOptions.engine, resolution };
|
|
3665
|
+
return { engine: createEngineForMode(resolution.mode), resolution };
|
|
3666
|
+
}
|
|
3667
|
+
|
|
3668
|
+
/**
|
|
3669
|
+
* `engine_env_ignored` is expected for the whole migration window: the manifest
|
|
3670
|
+
* generator projects the remote engine env fleet-wide before any mode flips, so
|
|
3671
|
+
* escalating on it would turn every healthy boot into a warn line. The
|
|
3672
|
+
* disagreement warnings still escalate.
|
|
3673
|
+
*/
|
|
3674
|
+
const ENGINE_MODE_INFO_WARNINGS: ReadonlySet<string> = new Set(["engine_env_ignored"]);
|
|
3675
|
+
|
|
3676
|
+
function engineModeLogEvent(
|
|
3677
|
+
provider: ProviderDefinition,
|
|
3678
|
+
resolution: ProviderEngineModeResolution,
|
|
3679
|
+
attached: boolean,
|
|
3680
|
+
): ProviderServerLogEvent {
|
|
3681
|
+
const escalating = resolution.warnings.filter(
|
|
3682
|
+
(warning) => !ENGINE_MODE_INFO_WARNINGS.has(warning),
|
|
3683
|
+
);
|
|
3684
|
+
return {
|
|
3685
|
+
level: attached && escalating.length === 0 ? "info" : "warn",
|
|
3686
|
+
event: "provider_engine_mode",
|
|
3687
|
+
providerId: provider.id,
|
|
3688
|
+
mode: resolution.mode,
|
|
3689
|
+
source: resolution.source,
|
|
3690
|
+
deprecated: resolution.deprecated,
|
|
3691
|
+
attached,
|
|
3692
|
+
sdkVersion: SDK_VERSION,
|
|
3693
|
+
...(provider.runtimeTarget === undefined ? {} : { runtimeTarget: provider.runtimeTarget }),
|
|
3694
|
+
...(resolution.warnings.length === 0 ? {} : { warnings: resolution.warnings }),
|
|
3695
|
+
};
|
|
3696
|
+
}
|
|
3697
|
+
|
|
3343
3698
|
function createServerAppWithCapabilityModules(
|
|
3344
3699
|
provider: ProviderDefinition,
|
|
3345
3700
|
serverOptions: ProviderServerOptions,
|
|
3346
3701
|
capabilityModules: ProviderCapabilityModules,
|
|
3347
3702
|
): Hono {
|
|
3703
|
+
const engineSelection = resolveServerEngine(serverOptions);
|
|
3704
|
+
// Read the provider's locale catalogs once here rather than per turn: the
|
|
3705
|
+
// previous auth-flow loader re-read all three JSON files on every turn and
|
|
3706
|
+
// threw the whole map away when one locale file was absent.
|
|
3707
|
+
const localeCatalogs = resolveProviderLocaleCatalogs(
|
|
3708
|
+
provider,
|
|
3709
|
+
serverOptions,
|
|
3710
|
+
serverOptions.logger ?? defaultProviderServerLogger,
|
|
3711
|
+
);
|
|
3348
3712
|
const options: ProviderServerRuntimeOptions = {
|
|
3349
3713
|
...serverOptions,
|
|
3714
|
+
...(localeCatalogs ? { localeCatalogs } : {}),
|
|
3350
3715
|
capabilityModules,
|
|
3351
|
-
engine:
|
|
3716
|
+
engine: engineSelection.engine,
|
|
3352
3717
|
};
|
|
3718
|
+
const engineAttached = !isUnavailableProviderEngine(options.engine);
|
|
3353
3719
|
const app = new Hono();
|
|
3354
3720
|
// Compile the startup inventory once, composed with the shared outside-context fallback.
|
|
3355
3721
|
const staticSensitiveValues = compileProcessDiagnosticSensitiveValues(
|
|
@@ -3392,11 +3758,27 @@ function createServerAppWithCapabilityModules(
|
|
|
3392
3758
|
const error = new ProviderError("Not found", { code: "not_found", retryable: false });
|
|
3393
3759
|
const observabilityDetails = errorObservabilityDetails(error);
|
|
3394
3760
|
return responseWithErrorObservability(
|
|
3395
|
-
c.json(
|
|
3761
|
+
c.json(
|
|
3762
|
+
toErrorResponse(
|
|
3763
|
+
error,
|
|
3764
|
+
undefined,
|
|
3765
|
+
observabilityDetails,
|
|
3766
|
+
undefined,
|
|
3767
|
+
errorEnvelopeLocalization({
|
|
3768
|
+
catalogs: localeCatalogs,
|
|
3769
|
+
requestHeaders: c.req.raw.headers,
|
|
3770
|
+
}),
|
|
3771
|
+
),
|
|
3772
|
+
404,
|
|
3773
|
+
),
|
|
3396
3774
|
observabilityDetails,
|
|
3397
3775
|
);
|
|
3398
3776
|
});
|
|
3399
3777
|
|
|
3778
|
+
// Liveness. Deliberately static and engine-blind: generated manifests point
|
|
3779
|
+
// liveness at this route, so making it depend on engine reachability would
|
|
3780
|
+
// restart every provider pod on one engine incident. Engine state belongs to
|
|
3781
|
+
// /readyz, which manifests wire to readiness and startup only.
|
|
3400
3782
|
app.get("/health", (c) =>
|
|
3401
3783
|
c.json({
|
|
3402
3784
|
status: "ok",
|
|
@@ -3405,6 +3787,25 @@ function createServerAppWithCapabilityModules(
|
|
|
3405
3787
|
}),
|
|
3406
3788
|
);
|
|
3407
3789
|
|
|
3790
|
+
// Readiness. Reports the boot-resolved attachment; does not probe the engine
|
|
3791
|
+
// per call, so it cannot amplify an engine incident into request load.
|
|
3792
|
+
app.get("/readyz", (c) =>
|
|
3793
|
+
c.json(
|
|
3794
|
+
{
|
|
3795
|
+
status: engineAttached ? "ok" : "unavailable",
|
|
3796
|
+
provider: provider.id,
|
|
3797
|
+
version: provider.version,
|
|
3798
|
+
engine: {
|
|
3799
|
+
// The resolution, not the raw engine `kind`: normalized, so this
|
|
3800
|
+
// body and the `provider_engine_mode` boot event never disagree.
|
|
3801
|
+
mode: engineSelection.resolution.mode,
|
|
3802
|
+
attached: engineAttached,
|
|
3803
|
+
},
|
|
3804
|
+
},
|
|
3805
|
+
engineAttached ? 200 : 503,
|
|
3806
|
+
),
|
|
3807
|
+
);
|
|
3808
|
+
|
|
3408
3809
|
app.post(STATEFUL_INTERNAL_OPERATIONS_ROUTE, async (c) => {
|
|
3409
3810
|
let rawBodyText = "";
|
|
3410
3811
|
let rawBody: unknown;
|
|
@@ -3535,7 +3936,10 @@ function createServerAppWithCapabilityModules(
|
|
|
3535
3936
|
requestId: request.requestId,
|
|
3536
3937
|
operationId,
|
|
3537
3938
|
headers: request.headers ?? {},
|
|
3538
|
-
correlation: {
|
|
3939
|
+
correlation: {
|
|
3940
|
+
connectionId: envelope.connectionId,
|
|
3941
|
+
tenantId: resolveOperationTenantId(request),
|
|
3942
|
+
},
|
|
3539
3943
|
});
|
|
3540
3944
|
const response = await requestScope.run(async () => {
|
|
3541
3945
|
const ctx = createProviderContext(
|
|
@@ -3580,7 +3984,18 @@ function createServerAppWithCapabilityModules(
|
|
|
3580
3984
|
});
|
|
3581
3985
|
const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
|
|
3582
3986
|
const response = c.json(
|
|
3583
|
-
toErrorResponse(
|
|
3987
|
+
toErrorResponse(
|
|
3988
|
+
error,
|
|
3989
|
+
requestId,
|
|
3990
|
+
observabilityDetails,
|
|
3991
|
+
requestScope.redact,
|
|
3992
|
+
errorEnvelopeLocalization({
|
|
3993
|
+
catalogs: localeCatalogs,
|
|
3994
|
+
requestHeaders: c.req.raw.headers,
|
|
3995
|
+
rawBody,
|
|
3996
|
+
...(declaredErrorCode ? { declaredErrorCode } : {}),
|
|
3997
|
+
}),
|
|
3998
|
+
),
|
|
3584
3999
|
status,
|
|
3585
4000
|
);
|
|
3586
4001
|
return finalizeRequestResponse(requestScope, response, {
|
|
@@ -3617,7 +4032,10 @@ function createServerAppWithCapabilityModules(
|
|
|
3617
4032
|
requestScope.enrich({
|
|
3618
4033
|
requestId: body.requestId,
|
|
3619
4034
|
headers: body.headers,
|
|
3620
|
-
correlation: {
|
|
4035
|
+
correlation: {
|
|
4036
|
+
connectionId: resolveOperationConnectionId(body),
|
|
4037
|
+
tenantId: resolveOperationTenantId(body),
|
|
4038
|
+
},
|
|
3621
4039
|
});
|
|
3622
4040
|
const streaming = provider.operations[operation]?.transport?.kind
|
|
3623
4041
|
? provider.operations[operation]?.transport?.kind !== "json"
|
|
@@ -3658,7 +4076,18 @@ function createServerAppWithCapabilityModules(
|
|
|
3658
4076
|
requestScope.enrich({ ...(requestId ? { requestId } : {}) });
|
|
3659
4077
|
const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
|
|
3660
4078
|
const response = c.json(
|
|
3661
|
-
toErrorResponse(
|
|
4079
|
+
toErrorResponse(
|
|
4080
|
+
error,
|
|
4081
|
+
requestId,
|
|
4082
|
+
observabilityDetails,
|
|
4083
|
+
requestScope.redact,
|
|
4084
|
+
errorEnvelopeLocalization({
|
|
4085
|
+
catalogs: localeCatalogs,
|
|
4086
|
+
requestHeaders: c.req.raw.headers,
|
|
4087
|
+
rawBody,
|
|
4088
|
+
...(declaredErrorCode ? { declaredErrorCode } : {}),
|
|
4089
|
+
}),
|
|
4090
|
+
),
|
|
3662
4091
|
status,
|
|
3663
4092
|
);
|
|
3664
4093
|
return finalizeRequestResponse(requestScope, response, {
|
|
@@ -3684,6 +4113,12 @@ function createServerAppWithCapabilityModules(
|
|
|
3684
4113
|
for (const { path, flowRoute, logRoute } of authRoutes) {
|
|
3685
4114
|
app.post(path, async (c) => {
|
|
3686
4115
|
let rawBody: unknown;
|
|
4116
|
+
// Negotiated once and shared by the success and failure paths so an
|
|
4117
|
+
// auth turn and an error envelope from the same request agree.
|
|
4118
|
+
let localization = errorEnvelopeLocalization({
|
|
4119
|
+
catalogs: localeCatalogs,
|
|
4120
|
+
requestHeaders: c.req.raw.headers,
|
|
4121
|
+
});
|
|
3687
4122
|
const requestScope = createRequestScope({
|
|
3688
4123
|
provider,
|
|
3689
4124
|
staticSensitiveValues,
|
|
@@ -3698,6 +4133,11 @@ function createServerAppWithCapabilityModules(
|
|
|
3698
4133
|
.clone()
|
|
3699
4134
|
.json()
|
|
3700
4135
|
.catch(() => undefined);
|
|
4136
|
+
localization = errorEnvelopeLocalization({
|
|
4137
|
+
catalogs: localeCatalogs,
|
|
4138
|
+
requestHeaders: c.req.raw.headers,
|
|
4139
|
+
rawBody,
|
|
4140
|
+
});
|
|
3701
4141
|
requestScope.seedCredentials(rawBody, "auth");
|
|
3702
4142
|
const body = withAuthRequestHeaders(
|
|
3703
4143
|
AuthFlowRequestSchema.parse(rawBody),
|
|
@@ -3710,7 +4150,7 @@ function createServerAppWithCapabilityModules(
|
|
|
3710
4150
|
correlation: {
|
|
3711
4151
|
connectionId: resolveOperationConnectionId(body),
|
|
3712
4152
|
flowId: body.flowId,
|
|
3713
|
-
tenantId: body.tenantId,
|
|
4153
|
+
tenantId: normalizeIdentifier(body.tenantId),
|
|
3714
4154
|
requestedProviderId:
|
|
3715
4155
|
body.providerId !== undefined && body.providerId !== provider.id
|
|
3716
4156
|
? body.providerId
|
|
@@ -3725,6 +4165,7 @@ function createServerAppWithCapabilityModules(
|
|
|
3725
4165
|
options,
|
|
3726
4166
|
state,
|
|
3727
4167
|
requestScope as RequestScope,
|
|
4168
|
+
localization.locale,
|
|
3728
4169
|
c.req.raw.signal,
|
|
3729
4170
|
);
|
|
3730
4171
|
return handled instanceof Response ? handled : c.json(handled);
|
|
@@ -3739,7 +4180,13 @@ function createServerAppWithCapabilityModules(
|
|
|
3739
4180
|
requestScope.enrich({ ...(requestId ? { requestId } : {}) });
|
|
3740
4181
|
const observabilityDetails = errorObservabilityDetails(error);
|
|
3741
4182
|
const response = c.json(
|
|
3742
|
-
toErrorResponse(
|
|
4183
|
+
toErrorResponse(
|
|
4184
|
+
error,
|
|
4185
|
+
requestId,
|
|
4186
|
+
observabilityDetails,
|
|
4187
|
+
requestScope.redact,
|
|
4188
|
+
localization,
|
|
4189
|
+
),
|
|
3743
4190
|
status,
|
|
3744
4191
|
);
|
|
3745
4192
|
return finalizeRequestResponse(requestScope, response, {
|
|
@@ -3876,13 +4323,18 @@ export async function serve<TContext extends Partial<ProviderContext> = Provider
|
|
|
3876
4323
|
);
|
|
3877
4324
|
const configuredSignals = resolveShutdownSignals(options.shutdown?.signals ?? true);
|
|
3878
4325
|
const selfTestSecrets = resolveSelfTestMasterSecrets();
|
|
4326
|
+
// Resolve the engine here so the boot event reports the truthful source
|
|
4327
|
+
// (option/env/default) and the app is handed the engine already selected —
|
|
4328
|
+
// one resolution per process, not one per app construction.
|
|
4329
|
+
const engineSelection = resolveServerEngine(options as unknown as ProviderServerOptions);
|
|
3879
4330
|
const serverAppOptions: ProviderServerOptions<TContext> = {
|
|
3880
4331
|
logger: options.logger,
|
|
3881
|
-
engine:
|
|
4332
|
+
engine: engineSelection.engine,
|
|
3882
4333
|
files: options.files,
|
|
3883
4334
|
ocr: options.ocr,
|
|
3884
4335
|
stt: options.stt,
|
|
3885
4336
|
resolver: options.resolver,
|
|
4337
|
+
localeCatalogs: options.localeCatalogs,
|
|
3886
4338
|
state: options.state,
|
|
3887
4339
|
allowMemoryStateFallback: options.allowMemoryStateFallback,
|
|
3888
4340
|
operationExecutor: options.operationExecutor,
|
|
@@ -3893,6 +4345,15 @@ export async function serve<TContext extends Partial<ProviderContext> = Provider
|
|
|
3893
4345
|
createServerAppAsync(provider, serverAppOptions),
|
|
3894
4346
|
selfTestSecrets ? import("./self-test.js") : undefined,
|
|
3895
4347
|
]);
|
|
4348
|
+
// One structured line per process boot, before the listener binds: the fleet
|
|
4349
|
+
// audit (ADR-0011 migration) counts these.
|
|
4350
|
+
logger(
|
|
4351
|
+
engineModeLogEvent(
|
|
4352
|
+
provider as unknown as ProviderDefinition,
|
|
4353
|
+
engineSelection.resolution,
|
|
4354
|
+
!isUnavailableProviderEngine(engineSelection.engine),
|
|
4355
|
+
),
|
|
4356
|
+
);
|
|
3896
4357
|
|
|
3897
4358
|
const servers: BunServerHandle[] = [];
|
|
3898
4359
|
try {
|