@bitkyc08/opencodex 2.58.0 → 2.60.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/README.md +28 -10
- package/gui/dist/assets/index-BTuCbqQd.css +1 -0
- package/gui/dist/assets/index-DoBVdPHP.js +134 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/crusoe.svg +1 -0
- package/gui/dist/provider-icons/opper.svg +3 -0
- package/package.json +4 -1
- package/src/adapters/anthropic-image-codec.ts +16 -2
- package/src/adapters/anthropic-image-normalize.ts +49 -2
- package/src/adapters/anthropic.ts +4 -1
- package/src/adapters/base.ts +34 -1
- package/src/adapters/coding-agent/turn.ts +22 -2
- package/src/adapters/command-code.ts +50 -3
- package/src/adapters/cursor/catalog.ts +11 -0
- package/src/adapters/cursor/checkpoint-store.ts +3 -0
- package/src/adapters/cursor/discovery.ts +11 -8
- package/src/adapters/cursor/effort-map.ts +16 -2
- package/src/adapters/cursor/envelope-echo.ts +55 -2
- package/src/adapters/cursor/live-transport.ts +26 -9
- package/src/adapters/cursor/message-mapper.ts +3 -2
- package/src/adapters/cursor/protobuf-request.ts +8 -5
- package/src/adapters/cursor/request-builder.ts +21 -4
- package/src/adapters/cursor/thread-continuity.ts +105 -31
- package/src/adapters/cursor/tool-guidance.ts +5 -4
- package/src/adapters/cursor/transport.ts +19 -0
- package/src/adapters/cursor.ts +45 -2
- package/src/adapters/devin/cloud-direct/chat.ts +14 -3
- package/src/adapters/devin/cloud-direct/index.ts +7 -0
- package/src/adapters/devin/cloud-direct/stated-reset-retry.ts +140 -0
- package/src/adapters/devin.ts +125 -26
- package/src/adapters/google-antigravity-replay.ts +1 -1
- package/src/adapters/google-antigravity-wire.ts +55 -4
- package/src/adapters/google-http.ts +57 -11
- package/src/adapters/google-tool-schema.ts +595 -31
- package/src/adapters/google-wire-compiler.ts +93 -10
- package/src/adapters/google-wire-shape.ts +461 -0
- package/src/adapters/google.ts +60 -10
- package/src/adapters/openai-chat/response-events.ts +61 -0
- package/src/adapters/openai-chat-images.ts +3 -1
- package/src/adapters/openai-chat.ts +10 -11
- package/src/adapters/openai-responses/image-gen.ts +8 -6
- package/src/adapters/openai-responses/passthrough.ts +25 -4
- package/src/adapters/openai-responses/reasoning.ts +7 -0
- package/src/adapters/openai-responses/tool-output-recovery.ts +75 -0
- package/src/adapters/openai-responses/tool-schema.ts +19 -7
- package/src/adapters/opencode-go-additional-tools.ts +12 -2
- package/src/adapters/responses-tool-schema.ts +76 -46
- package/src/adapters/run-turn-queue.ts +17 -4
- package/src/bridge/response-json.ts +1 -1
- package/src/bridge/sse.ts +28 -35
- package/src/claude/context-windows.ts +22 -0
- package/src/claude/outbound.ts +35 -4
- package/src/cli/account-api.ts +4 -3
- package/src/cli/account-extended.ts +26 -6
- package/src/cli/account-orca-import.ts +63 -0
- package/src/cli/account.ts +32 -4
- package/src/cli/capabilities.ts +40 -0
- package/src/cli/claude.ts +29 -1
- package/src/cli/codex-cli-update.ts +97 -2
- package/src/cli/dispatch.ts +57 -3
- package/src/cli/doctor.ts +218 -4
- package/src/cli/help.ts +4 -1
- package/src/cli/hub.ts +3 -2
- package/src/cli/index.ts +95 -22
- package/src/cli/models-runtime.ts +33 -4
- package/src/cli/opencode.ts +2 -2
- package/src/cli/provider.ts +13 -1
- package/src/cli/registry.ts +11 -1
- package/src/cli/runtime-api.ts +44 -0
- package/src/cli/start-args.ts +94 -0
- package/src/cli/system-command.ts +2 -0
- package/src/client/machine-api.ts +6 -5
- package/src/client/machine-listener.ts +16 -3
- package/src/client/runtime.ts +26 -2
- package/src/clients/config-export/constants.ts +2 -3
- package/src/clients/config-export.ts +5 -5
- package/src/codex/account-store.ts +146 -5
- package/src/codex/auth-api/account-list.ts +19 -11
- package/src/codex/auth-api/pool-quota-probe.ts +44 -10
- package/src/codex/auth-api/routes.ts +17 -2
- package/src/codex/auth-context.ts +16 -12
- package/src/codex/catalog/build-entries.ts +25 -4
- package/src/codex/catalog/derive-entry.ts +8 -1
- package/src/codex/catalog/effort.ts +10 -6
- package/src/codex/catalog/gather-capture.ts +22 -2
- package/src/codex/catalog/model-hints.ts +66 -33
- package/src/codex/catalog/parsing.ts +90 -5
- package/src/codex/catalog/provider-models.ts +19 -2
- package/src/codex/catalog/reserve-warn.ts +96 -0
- package/src/codex/catalog/retained-sync.ts +41 -26
- package/src/codex/catalog/routed-gather.ts +61 -3
- package/src/codex/cli-installation-identity.ts +210 -0
- package/src/codex/cli-installation-targets.ts +158 -0
- package/src/codex/context-compat.ts +5 -2
- package/src/codex/convergence.ts +5 -0
- package/src/codex/desired-state.ts +4 -1
- package/src/codex/history-job.ts +6 -6
- package/src/codex/history-provider.ts +24 -167
- package/src/codex/history-rollout-read.ts +174 -0
- package/src/codex/history-state-open.ts +105 -0
- package/src/codex/inject/config-toml.ts +44 -2
- package/src/codex/inject.ts +3 -2
- package/src/codex/internal/catalog-writer.ts +33 -1
- package/src/codex/lineage.ts +83 -32
- package/src/codex/loopback-target.ts +31 -0
- package/src/codex/main-account-hard-lock.ts +2 -1
- package/src/codex/main-account.ts +10 -3
- package/src/codex/main-device-reauth.ts +17 -9
- package/src/codex/model-cache.ts +47 -0
- package/src/codex/model-entitlements.ts +80 -2
- package/src/codex/observed-model-denials.ts +230 -0
- package/src/codex/orca-auth-source.ts +94 -0
- package/src/codex/orca-import.ts +219 -0
- package/src/codex/prompt-text-probe.ts +289 -16
- package/src/codex/quota-401-recovery.ts +12 -0
- package/src/codex/quota-types.ts +65 -0
- package/src/codex/quota.ts +24 -19
- package/src/codex/routing/cooldown-math.ts +8 -47
- package/src/codex/routing/pin-drain.ts +57 -0
- package/src/codex/routing.ts +20 -16
- package/src/codex/shim.ts +1 -1
- package/src/codex/subagent-model-fallback.ts +114 -2
- package/src/codex/windows-installation-files.ts +224 -0
- package/src/combos/failover.ts +125 -5
- package/src/config/admitted-identity.ts +222 -0
- package/src/config/diagnostics.ts +43 -1
- package/src/config/feature-flags.ts +5 -0
- package/src/config/load-degrade.ts +33 -0
- package/src/config/pending-teardown.ts +8 -0
- package/src/config/process-state.ts +36 -3
- package/src/config/provider-relative-send-path.ts +16 -0
- package/src/config/proxy-env.ts +31 -7
- package/src/config/schema/compaction-triggers.ts +11 -0
- package/src/config/schema/config-schema.ts +25 -0
- package/src/config/schema/leaf-validators.ts +76 -17
- package/src/config.ts +2 -2
- package/src/generated/compatibility-version.json +410 -258
- package/src/generated/model-metadata.ts +1 -1
- package/src/grok/reset-coupons.ts +38 -19
- package/src/images/loop.ts +6 -1
- package/src/integrations/aside-profile-context.ts +37 -3
- package/src/integrations/aside-profile-journal.ts +68 -3
- package/src/integrations/aside-profiles.ts +128 -3
- package/src/integrations/mutation-plan.ts +815 -0
- package/src/integrations/writer.ts +85 -99
- package/src/lab/live/transport.ts +4 -0
- package/src/lab/live/types.ts +5 -0
- package/src/lab/subject/behavior-fingerprint.ts +1 -1
- package/src/lib/admin-secrets.ts +9 -1
- package/src/lib/bounded-body.ts +4 -2
- package/src/lib/debug-log-buffer.ts +6 -1
- package/src/lib/debug.ts +23 -0
- package/src/lib/destination-policy.ts +48 -6
- package/src/lib/errors.ts +82 -15
- package/src/lib/http-response-semantics.ts +57 -0
- package/src/lib/lab-live-pinned-sender.ts +26 -12
- package/src/lib/local-destinations.ts +32 -5
- package/src/lib/pinned-http.ts +142 -2
- package/src/lib/plain-data.ts +103 -0
- package/src/lib/process-control.ts +13 -5
- package/src/lib/provider-outbound.ts +53 -5
- package/src/lib/proxy-env.ts +70 -3
- package/src/lib/request-execution-budget.ts +11 -3
- package/src/lib/response-body-inactivity.ts +193 -0
- package/src/lib/retry-delay.ts +69 -0
- package/src/lib/socks5-fetch.ts +741 -0
- package/src/lib/spend-ledger-owner.ts +364 -0
- package/src/lib/spend-reservation-ledger.ts +332 -35
- package/src/lib/windows-system-proxy.ts +16 -11
- package/src/lib/workflow-budget.ts +145 -8
- package/src/oauth/account-quota-rank.ts +72 -15
- package/src/oauth/callback-server.ts +4 -3
- package/src/oauth/generic-account-failover.ts +41 -27
- package/src/oauth/health.ts +12 -1
- package/src/oauth/index.ts +3 -107
- package/src/oauth/login-flow-state.ts +127 -0
- package/src/oauth/orcarouter.ts +15 -2
- package/src/oauth/store.ts +8 -0
- package/src/providers/codex-capacity.ts +9 -0
- package/src/providers/derive.ts +34 -17
- package/src/providers/devin-cli-authmode-migration.ts +14 -10
- package/src/providers/devin-provider-merge-migration.ts +33 -12
- package/src/providers/free-directory.ts +20 -2
- package/src/providers/key-failover.ts +327 -25
- package/src/providers/model-rename-migration.ts +56 -1
- package/src/providers/model-rename-startup.ts +7 -5
- package/src/providers/openai-sidecar.ts +4 -0
- package/src/providers/openai-virtual-models.ts +42 -2
- package/src/providers/opencode-go-transport.ts +14 -5
- package/src/providers/quota/antigravity.ts +22 -2
- package/src/providers/quota/report-cache.ts +3 -0
- package/src/providers/quota/vendor-probes-key.ts +1 -1
- package/src/providers/registry/entries-core.ts +39 -17
- package/src/providers/registry/entries-extended.ts +131 -1
- package/src/providers/registry/model-ids.ts +168 -0
- package/src/providers/registry/model-seeds.ts +87 -21
- package/src/providers/registry/types.ts +2 -0
- package/src/providers/resolved-model-policy-merge.ts +167 -0
- package/src/providers/resolved-model-policy.ts +406 -0
- package/src/providers/stale-vision-classification-migration.ts +137 -0
- package/src/responses/apply-patch-envelope.ts +32 -11
- package/src/responses/bridge-search-replay-cache.ts +152 -0
- package/src/responses/code-mode-helper-compat.ts +26 -16
- package/src/responses/custom-tool-compat.ts +1 -1
- package/src/responses/freeform-wrapper-scan.ts +279 -0
- package/src/responses/hosted-tool-policy.ts +85 -2
- package/src/responses/legacy-dotted-tool-name-repair.ts +134 -0
- package/src/responses/progressive-freeform-input.ts +130 -0
- package/src/responses/reasoning-envelope.ts +30 -0
- package/src/responses/schema.ts +9 -2
- package/src/responses/state.ts +5 -12
- package/src/responses/tool-name-aliases.ts +15 -1
- package/src/router.ts +91 -115
- package/src/routing/compatibility/behavior.ts +9 -0
- package/src/routing/compatibility/subject.ts +16 -1
- package/src/server/adapter-resolve.ts +9 -0
- package/src/server/auth-cors.ts +29 -0
- package/src/server/chat-completions.ts +13 -5
- package/src/server/chat-native-sse.ts +26 -9
- package/src/server/chat-native.ts +10 -4
- package/src/server/claude-messages.ts +30 -5
- package/src/server/effort-row.ts +11 -3
- package/src/server/grok-responses-control-frame.ts +160 -1
- package/src/server/gui-static.ts +36 -2
- package/src/server/inbound-body-admission.ts +187 -0
- package/src/server/index/serve-options.ts +86 -29
- package/src/server/index/spend-ledger-lifecycle.ts +66 -0
- package/src/server/index/websocket-handler.ts +6 -1
- package/src/server/index.ts +24 -25
- package/src/server/management/api-access.ts +3 -4
- package/src/server/management/aside-profile-routes.ts +266 -7
- package/src/server/management/config-routes.ts +48 -7
- package/src/server/management/context.ts +3 -0
- package/src/server/management/integration-routes.ts +287 -5
- package/src/server/management/metrics-routes.ts +20 -0
- package/src/server/management/model-rows.ts +224 -12
- package/src/server/management/provider-capability-config.ts +35 -7
- package/src/server/management/provider-routes.ts +70 -18
- package/src/server/management/route-registry.ts +14 -0
- package/src/server/management/shared.ts +10 -3
- package/src/server/management/system-restart.ts +7 -2
- package/src/server/management/system-routes.ts +2 -0
- package/src/server/management/usage-aggregate-cache.ts +4 -0
- package/src/server/management-api.ts +2 -0
- package/src/server/management-auth.ts +15 -1
- package/src/server/proxy-liveness.ts +97 -2
- package/src/server/readiness.ts +29 -10
- package/src/server/relay-eager.ts +24 -2
- package/src/server/relay.ts +136 -34
- package/src/server/request-log.ts +80 -3
- package/src/server/request-metrics.ts +236 -0
- package/src/server/responses/adapter-continuation.ts +74 -30
- package/src/server/responses/adapter-delivery.ts +39 -8
- package/src/server/responses/adapter-dispatch.ts +63 -30
- package/src/server/responses/compact.ts +89 -18
- package/src/server/responses/compaction-routing.ts +111 -0
- package/src/server/responses/core-codex-account.ts +90 -24
- package/src/server/responses/core-combo.ts +7 -7
- package/src/server/responses/core-normalize.ts +16 -15
- package/src/server/responses/core-opaque-recovery.ts +1 -0
- package/src/server/responses/core-options.ts +4 -0
- package/src/server/responses/encrypted-payload.ts +20 -2
- package/src/server/responses/fetch-helpers.ts +68 -2
- package/src/server/responses/passthrough-delivery.ts +41 -15
- package/src/server/responses/passthrough-dispatch.ts +145 -53
- package/src/server/responses/passthrough-execution.ts +11 -1
- package/src/server/responses/policy-fallback.ts +5 -13
- package/src/server/responses/request-prepare.ts +96 -17
- package/src/server/responses/request-send-budget.ts +89 -8
- package/src/server/responses/request-sidecar-auth.ts +17 -9
- package/src/server/responses/request-spend.ts +38 -9
- package/src/server/responses/request-transport.ts +15 -12
- package/src/server/responses/run-turn-execution.ts +45 -8
- package/src/server/responses/sidecar-execution.ts +19 -2
- package/src/server/responses/ws-upstream.ts +16 -28
- package/src/server/responses-custom-tool-repair.ts +29 -56
- package/src/server/responses-undeclared-tool-guard.ts +31 -1
- package/src/server/sse-frame-buffer.ts +12 -10
- package/src/server/sse-payload-rewrite.ts +37 -10
- package/src/server/system-env-shell.ts +5 -1
- package/src/server/system-env.ts +7 -1
- package/src/server/workflow-refusal.ts +56 -2
- package/src/service/cli.ts +16 -6
- package/src/service/guards.ts +10 -0
- package/src/service/health.ts +43 -0
- package/src/service/state.ts +7 -2
- package/src/tray/windows-tray.ps1 +155 -3
- package/src/types/accounts.ts +4 -0
- package/src/types/config.ts +111 -6
- package/src/types/provider.ts +37 -0
- package/src/types/request.ts +9 -1
- package/src/types/tools.ts +14 -0
- package/src/types/wire.ts +9 -1
- package/src/types.ts +1 -0
- package/src/usage/expected-prices.ts +28 -0
- package/src/usage/log.ts +87 -4
- package/src/vision/eligibility.ts +88 -9
- package/src/vision/plan.ts +34 -10
- package/src/web-search/executor.ts +41 -2
- package/src/web-search/loop.ts +6 -1
- package/src/web-search/passthrough-bridge.ts +39 -5
- package/gui/dist/assets/index-BbrHOIY0.js +0 -128
- package/gui/dist/assets/index-C5-RdDmD.css +0 -1
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
type WsData,
|
|
38
38
|
} from "../ws-bridge";
|
|
39
39
|
import { websocketsEnabled } from "../../config";
|
|
40
|
+
import { metricsExportEnabled } from "../../config/feature-flags";
|
|
40
41
|
import { grokDefaultReasoningEffort } from "../../grok/effort";
|
|
41
42
|
import { OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers";
|
|
42
43
|
import { providerCodexAccountMode } from "../../providers/registry";
|
|
@@ -84,6 +85,7 @@ import {
|
|
|
84
85
|
} from "../request-log";
|
|
85
86
|
import { sessionLaneIdFromRequest } from "../request-log-conversation";
|
|
86
87
|
import { responseWithDeferredRequestLog } from "../relay";
|
|
88
|
+
import { createRequestMetricsOwner } from "../request-metrics";
|
|
87
89
|
import {
|
|
88
90
|
corsHeaders,
|
|
89
91
|
managementCorsHeaders,
|
|
@@ -266,6 +268,11 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
266
268
|
port,
|
|
267
269
|
} = ctx;
|
|
268
270
|
void port;
|
|
271
|
+
const requestMetrics = metricsExportEnabled(config) ? createRequestMetricsOwner() : undefined;
|
|
272
|
+
const requestMetricsLogContext = requestMetrics ? { requestMetricsRecorder: requestMetrics } : {};
|
|
273
|
+
const requestManagementApiDeps: ManagementApiDeps = requestMetrics
|
|
274
|
+
? { ...managementApiDeps, requestMetrics: { snapshot: () => requestMetrics.snapshot() } }
|
|
275
|
+
: managementApiDeps;
|
|
269
276
|
const serveOptions = {
|
|
270
277
|
idleTimeout: 255,
|
|
271
278
|
// Bun rejects an oversized body before `fetch` runs, so the listener has to be raised
|
|
@@ -616,7 +623,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
616
623
|
}), req, config);
|
|
617
624
|
}
|
|
618
625
|
}
|
|
619
|
-
const mgmtResponse = await handleManagementAPI(req, url, config,
|
|
626
|
+
const mgmtResponse = await handleManagementAPI(req, url, config, requestManagementApiDeps, principal, managementSessionControl);
|
|
620
627
|
if (mgmtResponse) return withManagementCors(mgmtResponse, req, config);
|
|
621
628
|
return withManagementCors(formatErrorResponse(404, "not_found", `Unknown endpoint: ${req.method} ${url.pathname}`), req, config);
|
|
622
629
|
}
|
|
@@ -1197,6 +1204,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1197
1204
|
const logCtx: RequestLogContext = {
|
|
1198
1205
|
model: "unknown",
|
|
1199
1206
|
provider: "unknown",
|
|
1207
|
+
...requestMetricsLogContext,
|
|
1200
1208
|
...admissionFields(admission),
|
|
1201
1209
|
inboundProtocol: "responses",
|
|
1202
1210
|
};
|
|
@@ -1233,6 +1241,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1233
1241
|
const logCtx: RequestLogContext = {
|
|
1234
1242
|
model: "image_gen",
|
|
1235
1243
|
provider: "unknown",
|
|
1244
|
+
...requestMetricsLogContext,
|
|
1236
1245
|
...admissionFields(admission),
|
|
1237
1246
|
};
|
|
1238
1247
|
const endpoint = url.pathname.endsWith("/edits") ? "edits" as const : "generations" as const;
|
|
@@ -1290,6 +1299,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1290
1299
|
const logCtx: RequestLogContext = {
|
|
1291
1300
|
model: "context_history",
|
|
1292
1301
|
provider: "unknown",
|
|
1302
|
+
...requestMetricsLogContext,
|
|
1293
1303
|
...admissionFields(admission),
|
|
1294
1304
|
};
|
|
1295
1305
|
return runAdmittedHttpTurn(req, policy, async turnAdmissionLease => {
|
|
@@ -1316,6 +1326,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1316
1326
|
const logCtx: RequestLogContext = {
|
|
1317
1327
|
model: "web_search",
|
|
1318
1328
|
provider: "unknown",
|
|
1329
|
+
...requestMetricsLogContext,
|
|
1319
1330
|
...admissionFields(admission),
|
|
1320
1331
|
};
|
|
1321
1332
|
return runAdmittedHttpTurn(req, policy, async turnAdmissionLease => {
|
|
@@ -1340,6 +1351,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1340
1351
|
const logCtx: RequestLogContext = {
|
|
1341
1352
|
model: "unknown",
|
|
1342
1353
|
provider: "unknown",
|
|
1354
|
+
...requestMetricsLogContext,
|
|
1343
1355
|
...admissionFields(admission),
|
|
1344
1356
|
inboundProtocol: "responses",
|
|
1345
1357
|
};
|
|
@@ -1347,29 +1359,41 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1347
1359
|
let logged = false;
|
|
1348
1360
|
const finalizeNativePassthroughLog = (
|
|
1349
1361
|
status: number,
|
|
1350
|
-
meta:
|
|
1362
|
+
meta: Pick<RequestLogEntry, "terminalStatus" | "closeReason">,
|
|
1351
1363
|
) => {
|
|
1352
1364
|
if (logged) return;
|
|
1353
1365
|
logged = true;
|
|
1354
1366
|
addFinalRequestLog(requestId, start, logCtx, status, meta);
|
|
1355
1367
|
};
|
|
1356
1368
|
return runAdmittedHttpTurn(req, policy, async turnAdmissionLease => {
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1369
|
+
let response: Response;
|
|
1370
|
+
try {
|
|
1371
|
+
response = await handleResponses(req, config, logCtx, {
|
|
1372
|
+
turnAdmissionLease,
|
|
1373
|
+
admission,
|
|
1374
|
+
onRequestBodyRead: () => disableResponsesRequestTimeout(req, requestServer),
|
|
1375
|
+
abortSignal: req.signal,
|
|
1376
|
+
onFirstOutput: () => recordFirstOutput(logCtx, start),
|
|
1377
|
+
onNativePassthroughTerminal: status => {
|
|
1378
|
+
finalizeNativePassthroughLog(httpStatusForRequestLogTerminal(status, logCtx), {
|
|
1379
|
+
terminalStatus: status,
|
|
1380
|
+
closeReason: "terminal",
|
|
1381
|
+
});
|
|
1382
|
+
},
|
|
1383
|
+
onNativePassthroughCancel: () => {
|
|
1384
|
+
finalizeNativePassthroughLog(499, { closeReason: "client_cancel" });
|
|
1385
|
+
},
|
|
1386
|
+
});
|
|
1387
|
+
} catch (error) {
|
|
1388
|
+
// A bounded upstream body can fail before handleResponses returns a client response.
|
|
1389
|
+
// Finalize before rethrow so accounting observes the physical send while the caller
|
|
1390
|
+
// retains the existing reset/rejection instead of receiving a synthesized response.
|
|
1391
|
+
finalizeNativePassthroughLog(
|
|
1392
|
+
req.signal.aborted ? 499 : 502,
|
|
1393
|
+
{ closeReason: req.signal.aborted ? "client_cancel" : "non_stream" },
|
|
1394
|
+
);
|
|
1395
|
+
throw error;
|
|
1396
|
+
}
|
|
1373
1397
|
return withRequestLogId(
|
|
1374
1398
|
withCors(responseWithDeferredRequestLog(response, requestId, start, logCtx), req, policy),
|
|
1375
1399
|
requestId,
|
|
@@ -1414,6 +1438,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1414
1438
|
const logCtx: RequestLogContext = {
|
|
1415
1439
|
model: "unknown",
|
|
1416
1440
|
provider: "unknown",
|
|
1441
|
+
...requestMetricsLogContext,
|
|
1417
1442
|
...admissionFields(admission),
|
|
1418
1443
|
inboundProtocol: "messages",
|
|
1419
1444
|
};
|
|
@@ -1443,6 +1468,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1443
1468
|
const logCtx: RequestLogContext = {
|
|
1444
1469
|
model: "unknown",
|
|
1445
1470
|
provider: "unknown",
|
|
1471
|
+
...requestMetricsLogContext,
|
|
1446
1472
|
...admissionFields(admission),
|
|
1447
1473
|
inboundProtocol: "chat",
|
|
1448
1474
|
};
|
|
@@ -1466,7 +1492,12 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1466
1492
|
}
|
|
1467
1493
|
const start = Date.now();
|
|
1468
1494
|
const requestId = nextRequestLogId(start);
|
|
1469
|
-
const logCtx: RequestLogContext = {
|
|
1495
|
+
const logCtx: RequestLogContext = {
|
|
1496
|
+
model: TRANSCRIPTION_MODEL,
|
|
1497
|
+
provider: "unknown",
|
|
1498
|
+
...requestMetricsLogContext,
|
|
1499
|
+
...admissionFields(admission),
|
|
1500
|
+
};
|
|
1470
1501
|
return runAdmittedHttpTurn(req, policy, async lease => {
|
|
1471
1502
|
const response = await handleAudioTranscriptions(req, config, logCtx, admission, lease);
|
|
1472
1503
|
addFinalRequestLog(requestId, start, logCtx, response.status);
|
|
@@ -1497,6 +1528,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1497
1528
|
const logCtx: RequestLogContext = {
|
|
1498
1529
|
model: "gpt-live",
|
|
1499
1530
|
provider: "unknown",
|
|
1531
|
+
...requestMetricsLogContext,
|
|
1500
1532
|
...admissionFields(admission),
|
|
1501
1533
|
};
|
|
1502
1534
|
return runAdmittedHttpTurn(req, policy, async turnAdmissionLease => {
|
|
@@ -1544,10 +1576,23 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1544
1576
|
const logCtx: RequestLogContext = {
|
|
1545
1577
|
model: "gpt-live",
|
|
1546
1578
|
provider: "unknown",
|
|
1579
|
+
...requestMetricsLogContext,
|
|
1547
1580
|
...admissionFields(admission),
|
|
1548
1581
|
};
|
|
1582
|
+
let liveRequestFinalized = false;
|
|
1583
|
+
const finalizeLiveRequest = (
|
|
1584
|
+
status: number,
|
|
1585
|
+
meta?: Pick<RequestLogEntry, "terminalStatus" | "closeReason">,
|
|
1586
|
+
): void => {
|
|
1587
|
+
if (liveRequestFinalized) return;
|
|
1588
|
+
liveRequestFinalized = true;
|
|
1589
|
+
addFinalRequestLog(requestId, start, logCtx, status, meta);
|
|
1590
|
+
};
|
|
1549
1591
|
const turnAdmissionLease = tryAdmitTurn(sessionLaneIdFromRequest(req.headers));
|
|
1550
|
-
if (!turnAdmissionLease)
|
|
1592
|
+
if (!turnAdmissionLease) {
|
|
1593
|
+
finalizeLiveRequest(503);
|
|
1594
|
+
return serverBusyResponse(req, "active turns", policy);
|
|
1595
|
+
}
|
|
1551
1596
|
const audioController = audioClient ? new AbortController() : undefined;
|
|
1552
1597
|
if (audioController) registerTurn(audioController, turnAdmissionLease);
|
|
1553
1598
|
const acquisition = audioController
|
|
@@ -1567,18 +1612,26 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1567
1612
|
? await resolveLiveSidebandUpgrade(req, config, logCtx, liveSidebandTarget, turnAdmissionLease)
|
|
1568
1613
|
: formatErrorResponse(401, "authentication_error", "opencodex API key required");
|
|
1569
1614
|
} catch (error) {
|
|
1570
|
-
releaseAcquisition();
|
|
1615
|
+
try { releaseAcquisition(); }
|
|
1616
|
+
finally {
|
|
1617
|
+
finalizeLiveRequest(req.signal.aborted ? 499 : 500,
|
|
1618
|
+
req.signal.aborted ? { closeReason: "client_cancel" } : undefined);
|
|
1619
|
+
}
|
|
1571
1620
|
throw error;
|
|
1572
1621
|
}
|
|
1573
1622
|
if (acquisition?.signal.aborted) {
|
|
1623
|
+
const status = req.signal.aborted ? 499 : acquisition.didExpire() ? 504 : 503;
|
|
1574
1624
|
try { if (!(resolved instanceof Response) && "finish" in resolved) resolved.finish(); }
|
|
1575
|
-
finally {
|
|
1576
|
-
|
|
1625
|
+
finally {
|
|
1626
|
+
try { releaseAcquisition(); }
|
|
1627
|
+
finally { finalizeLiveRequest(status, status === 499 ? { closeReason: "client_cancel" } : undefined); }
|
|
1628
|
+
}
|
|
1629
|
+
return withCors(formatErrorResponse(status,
|
|
1577
1630
|
"upstream_error", acquisition.didExpire() ? "Audio connection timed out" : "Audio connection canceled"), req, policy);
|
|
1578
1631
|
}
|
|
1579
1632
|
if (resolved instanceof Response) {
|
|
1580
1633
|
releaseAcquisition();
|
|
1581
|
-
|
|
1634
|
+
finalizeLiveRequest(resolved.status);
|
|
1582
1635
|
return withCors(resolved, req, policy);
|
|
1583
1636
|
}
|
|
1584
1637
|
const audio = "finish" in resolved ? resolved : undefined;
|
|
@@ -1591,7 +1644,8 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1591
1644
|
else releaseAcquisition();
|
|
1592
1645
|
};
|
|
1593
1646
|
if (req.signal.aborted) {
|
|
1594
|
-
discardUpgrade();
|
|
1647
|
+
try { discardUpgrade(); }
|
|
1648
|
+
finally { finalizeLiveRequest(499, { closeReason: "client_cancel" }); }
|
|
1595
1649
|
return withCors(formatErrorResponse(499, "client_closed_request", "Audio connection canceled"), req, policy);
|
|
1596
1650
|
}
|
|
1597
1651
|
const upstreamHandshake = await openLiveSidebandUpstream(
|
|
@@ -1609,7 +1663,8 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1609
1663
|
} else {
|
|
1610
1664
|
discardUpgrade();
|
|
1611
1665
|
}
|
|
1612
|
-
|
|
1666
|
+
finalizeLiveRequest(upstreamHandshake.status,
|
|
1667
|
+
upstreamHandshake.status === 499 ? { closeReason: "client_cancel" } : undefined);
|
|
1613
1668
|
console.error("[live] sideband upstream handshake failed: " + upstreamHandshake.message);
|
|
1614
1669
|
return withCors(
|
|
1615
1670
|
formatErrorResponse(upstreamHandshake.status, upstreamHandshake.code, upstreamHandshake.message),
|
|
@@ -1625,7 +1680,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1625
1680
|
code: "upstream_error",
|
|
1626
1681
|
message: "voice upstream closed before client upgrade",
|
|
1627
1682
|
};
|
|
1628
|
-
|
|
1683
|
+
finalizeLiveRequest(failure.status);
|
|
1629
1684
|
return withCors(formatErrorResponse(failure.status, failure.code, failure.message), req, policy);
|
|
1630
1685
|
}
|
|
1631
1686
|
let upgraded = false;
|
|
@@ -1657,11 +1712,12 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1657
1712
|
/* ignore */
|
|
1658
1713
|
}
|
|
1659
1714
|
closeLiveSidebandBeforeUpgrade(upstreamHandshake.socket, () => discardUpgrade());
|
|
1715
|
+
finalizeLiveRequest(502);
|
|
1660
1716
|
return withCors(formatErrorResponse(502, "upstream_error", "Audio WebSocket upgrade failed"), req, policy);
|
|
1661
1717
|
}
|
|
1662
1718
|
if (upgraded) {
|
|
1663
1719
|
acquisition?.clear();
|
|
1664
|
-
|
|
1720
|
+
finalizeLiveRequest(101);
|
|
1665
1721
|
return undefined as unknown as Response;
|
|
1666
1722
|
}
|
|
1667
1723
|
try {
|
|
@@ -1670,6 +1726,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1670
1726
|
/* ignore */
|
|
1671
1727
|
}
|
|
1672
1728
|
closeLiveSidebandBeforeUpgrade(upstreamHandshake.socket, () => discardUpgrade());
|
|
1729
|
+
finalizeLiveRequest(426);
|
|
1673
1730
|
return withCors(formatErrorResponse(426, "upgrade_required", "WebSocket upgrade failed"), req, policy);
|
|
1674
1731
|
}
|
|
1675
1732
|
|
|
@@ -1760,7 +1817,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1760
1817
|
|
|
1761
1818
|
return withCors(formatErrorResponse(404, "not_found", `Unknown endpoint: ${req.method} ${url.pathname}`), req, config);
|
|
1762
1819
|
},
|
|
1763
|
-
websocket: createWebsocketHandler(ctx),
|
|
1820
|
+
websocket: createWebsocketHandler(ctx, requestMetrics),
|
|
1764
1821
|
} as const;
|
|
1765
1822
|
return serveOptions;
|
|
1766
1823
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { OcxSpendConfig } from "../../types/config";
|
|
2
|
+
import {
|
|
3
|
+
acquireSpendLedgerOwner,
|
|
4
|
+
type SpendLedgerOwnerLease,
|
|
5
|
+
} from "../../lib/spend-ledger-owner";
|
|
6
|
+
import {
|
|
7
|
+
configureSharedSpendLedger,
|
|
8
|
+
spendPolicyFromConfig,
|
|
9
|
+
} from "../../lib/spend-reservation-ledger";
|
|
10
|
+
|
|
11
|
+
export interface SpendLedgerServerLifecycle {
|
|
12
|
+
configure(spend: OcxSpendConfig | undefined): void;
|
|
13
|
+
track<T extends { stop(closeActiveConnections?: boolean): void | Promise<void> }>(server: T): T;
|
|
14
|
+
release(): void;
|
|
15
|
+
releaseAfterFailedStart(): void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Acquire before config loading so every later startup failure has one rollback owner. */
|
|
19
|
+
export function acquireSpendLedgerServerLifecycle(configDir: string): SpendLedgerServerLifecycle {
|
|
20
|
+
const owner: SpendLedgerOwnerLease = acquireSpendLedgerOwner(configDir);
|
|
21
|
+
// Each entry returns whatever the listener's own stop returned. Typed as void-or-promise
|
|
22
|
+
// because the rollback below has to WAIT on it: declaring it `() => void` let the call site
|
|
23
|
+
// compile while statically erasing the promise it needs to await.
|
|
24
|
+
const failedStartStops: Array<() => void | Promise<void>> = [];
|
|
25
|
+
let released = false;
|
|
26
|
+
const release = (): void => {
|
|
27
|
+
if (released) return;
|
|
28
|
+
released = true;
|
|
29
|
+
owner.release();
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
configure(spend): void {
|
|
33
|
+
configureSharedSpendLedger(spendPolicyFromConfig(spend));
|
|
34
|
+
},
|
|
35
|
+
track<T extends { stop(closeActiveConnections?: boolean): void | Promise<void> }>(server: T): T {
|
|
36
|
+
// Capture the raw stop before startServer replaces the public method with full teardown.
|
|
37
|
+
const stop = server.stop.bind(server);
|
|
38
|
+
failedStartStops.push(() => stop(true));
|
|
39
|
+
return server;
|
|
40
|
+
},
|
|
41
|
+
release,
|
|
42
|
+
releaseAfterFailedStart(): void {
|
|
43
|
+
// Every listener that came up is stopped, newest first, and the lease is held until those
|
|
44
|
+
// stops have actually SETTLED. Bun's Server.stop(true) returns a promise that resolves
|
|
45
|
+
// once connections are closed, so discarding it handed the state directory back while a
|
|
46
|
+
// listener could still be serving, which is the one thing single-writer ownership exists
|
|
47
|
+
// to prevent.
|
|
48
|
+
//
|
|
49
|
+
// This stays synchronous and returns void on purpose: startServer must not become async,
|
|
50
|
+
// so the wait is a continuation rather than an await. Rollback failures are contained
|
|
51
|
+
// because the startup error that brought us here is the one worth reporting.
|
|
52
|
+
const settling: Promise<unknown>[] = [];
|
|
53
|
+
for (const stop of failedStartStops.splice(0).reverse()) {
|
|
54
|
+
try {
|
|
55
|
+
const pending = stop();
|
|
56
|
+
if (pending !== undefined) settling.push(Promise.resolve(pending));
|
|
57
|
+
} catch { /* a rollback failure must not replace the startup error that caused it */ }
|
|
58
|
+
}
|
|
59
|
+
const finish = (): void => {
|
|
60
|
+
try { release(); } catch { /* same: the startup error is the one that matters */ }
|
|
61
|
+
};
|
|
62
|
+
if (settling.length === 0) { finish(); return; }
|
|
63
|
+
void Promise.allSettled(settling).then(finish);
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -85,13 +85,17 @@ import {
|
|
|
85
85
|
resolveLiveSidebandUpgrade,
|
|
86
86
|
} from "../live";
|
|
87
87
|
import type { ServeOptionsContext } from "./serve-options";
|
|
88
|
+
import type { RequestMetricsRecorder } from "../request-metrics";
|
|
88
89
|
|
|
89
90
|
/**
|
|
90
91
|
* The WebSocket half of the Bun.serve options, split out of serve-options.ts to keep that file
|
|
91
92
|
* under the 2,000-line ratchet threshold. The body is the original handler verbatim; it reads the
|
|
92
93
|
* same startServer context the HTTP half does, so it takes the same context object.
|
|
93
94
|
*/
|
|
94
|
-
export function createWebsocketHandler(
|
|
95
|
+
export function createWebsocketHandler(
|
|
96
|
+
ctx: ServeOptionsContext,
|
|
97
|
+
requestMetricsRecorder?: RequestMetricsRecorder,
|
|
98
|
+
) {
|
|
95
99
|
const { config, deps } = ctx;
|
|
96
100
|
return {
|
|
97
101
|
maxPayloadLength: MAX_WS_FRAME_BYTES,
|
|
@@ -283,6 +287,7 @@ export function createWebsocketHandler(ctx: ServeOptionsContext) {
|
|
|
283
287
|
const logCtx: RequestLogContext = {
|
|
284
288
|
model: "unknown",
|
|
285
289
|
provider: "unknown",
|
|
290
|
+
...(requestMetricsRecorder ? { requestMetricsRecorder } : {}),
|
|
286
291
|
...(wsAdmission ? admissionFields(wsAdmission) : {}),
|
|
287
292
|
inboundProtocol: "responses",
|
|
288
293
|
};
|
package/src/server/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { remoteWorkspaceEnabled } from "../remote-control/workspace-activation";
|
|
2
2
|
import { AuxiliaryListenerBindError } from "./ports";
|
|
3
|
+
import { runAdmittedBodyWork } from "./inbound-body-admission";
|
|
3
4
|
import {
|
|
4
5
|
buildWarmupCompletionFrames,
|
|
5
6
|
buildWsErrorFrame,
|
|
@@ -106,7 +107,6 @@ export {
|
|
|
106
107
|
unregisterTurn,
|
|
107
108
|
} from "./lifecycle";
|
|
108
109
|
import {
|
|
109
|
-
addFinalRequestLog,
|
|
110
110
|
hydrateRequestLogsFromDisk,
|
|
111
111
|
httpStatusForRequestLogTerminal,
|
|
112
112
|
inspectResponseLogSsePayload,
|
|
@@ -116,8 +116,7 @@ import {
|
|
|
116
116
|
type RequestLogEntry,
|
|
117
117
|
} from "./request-log";
|
|
118
118
|
import { sessionLaneIdFromRequest } from "./request-log-conversation";
|
|
119
|
-
import {
|
|
120
|
-
import { workflowRefusalResponse, type WorkflowRefusalLog } from "./workflow-refusal";
|
|
119
|
+
import { admitHttpWorkflowTurn, workflowDecisionRefusalResponse, type WorkflowRefusalLog } from "./workflow-refusal";
|
|
121
120
|
export {
|
|
122
121
|
addFinalRequestLog,
|
|
123
122
|
filterRequestLogs,
|
|
@@ -181,7 +180,7 @@ import {
|
|
|
181
180
|
type NativeMainStartupLifecycle,
|
|
182
181
|
} from "../codex/native-profile-startup";
|
|
183
182
|
import { EXTERNAL_CALL_PREFIX, LiveCallBindings } from "./live-call-bindings";
|
|
184
|
-
import {
|
|
183
|
+
import { contextEndpoint, contextRelayActivated } from "../codex/context-compat";
|
|
185
184
|
import { fetchAllModels, handleManagementAPI, VERSION, type ManagementApiDeps } from "./management-api";
|
|
186
185
|
import {
|
|
187
186
|
createManagementSessionControl,
|
|
@@ -205,8 +204,15 @@ import {
|
|
|
205
204
|
import { detectInstall } from "../update/index";
|
|
206
205
|
import { createServeOptions, type ServerIngress } from "./index/serve-options";
|
|
207
206
|
import { inspectStartupOwnership, setStartupCacheInvalidationWrite, warnAgentTaskRecoveryStartup, warnPlaintextV2AgentMessagesStartup, type StartServerDeps } from "./index/startup-warnings";
|
|
207
|
+
import { acquireSpendLedgerServerLifecycle, type SpendLedgerServerLifecycle } from "./index/spend-ledger-lifecycle";
|
|
208
208
|
|
|
209
209
|
export function startServer(port?: number, deps: StartServerDeps = {}): Server<WsData> {
|
|
210
|
+
const spendLedgerLifecycle = acquireSpendLedgerServerLifecycle(getConfigDir());
|
|
211
|
+
try { return startServerWithSpendLedgerOwner(port, deps, spendLedgerLifecycle); }
|
|
212
|
+
catch (error) { spendLedgerLifecycle.releaseAfterFailedStart(); throw error; }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function startServerWithSpendLedgerOwner(port: number | undefined, deps: StartServerDeps, spendLedgerLifecycle: SpendLedgerServerLifecycle): Server<WsData> {
|
|
210
216
|
const localAttestationSecret = deps.localAttestationSecret ?? createLocalAttestationSecret();
|
|
211
217
|
// Captured before loadConfig() starts the optional ACL flight so stop() drains the same dir
|
|
212
218
|
// even if OPENCODEX_HOME changes underneath a long-lived process.
|
|
@@ -225,7 +231,7 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
|
|
|
225
231
|
warnPlaintextV2AgentMessagesStartup(config);
|
|
226
232
|
warnAgentTaskRecoveryStartup(config);
|
|
227
233
|
setLiveStateStoreConfig(config);
|
|
228
|
-
applyProxyEnv(config);
|
|
234
|
+
applyProxyEnv(config, true);
|
|
229
235
|
assertServerAuthConfig(config);
|
|
230
236
|
const managementAuth = deps.managementAuthState ?? initializeManagementAuthState(config);
|
|
231
237
|
const managementSessionControl = createManagementSessionControl(managementAuth);
|
|
@@ -299,6 +305,8 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
|
|
|
299
305
|
registerAppOwnedMemorySweepFallback();
|
|
300
306
|
configureAppOwnedMemoryBudget(resolveAppOwnedMemoryBudgetBytes(config.appOwnedMemoryBudgetMb));
|
|
301
307
|
enforceAppOwnedMemoryBudget();
|
|
308
|
+
// Observe-only mode still journals physical sends, so every server owns before configuring.
|
|
309
|
+
spendLedgerLifecycle.configure(config.spend);
|
|
302
310
|
registerCodexCooldownRecoveryProbeWorker(config);
|
|
303
311
|
// Issue #42 Phase 3: opt-in archived auto-cleanup (default OFF). Unref'd hourly
|
|
304
312
|
// tick for daily/weekly; startup evaluation is fire-and-forget after listen.
|
|
@@ -498,28 +506,18 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
|
|
|
498
506
|
): Promise<Response> {
|
|
499
507
|
const lease = tryAdmitTurn(sessionLaneIdFromRequest(req.headers));
|
|
500
508
|
if (!lease) return serverBusyResponse(req, "active turns", policy);
|
|
501
|
-
//
|
|
502
|
-
|
|
503
|
-
// children. A request that names a parent is treated as that fan-out; a top-level request is
|
|
504
|
-
// the conversation and may use the reserved slots.
|
|
505
|
-
const workflowRootId = req.headers.get("x-codex-parent-thread-id")?.trim() || undefined;
|
|
506
|
-
const workflowThreadId = req.headers.get("thread-id")?.trim() || undefined;
|
|
507
|
-
const workflowLane: WorkflowLane = workflowRootId !== undefined
|
|
508
|
-
&& workflowThreadId !== undefined
|
|
509
|
-
&& workflowThreadId !== workflowRootId
|
|
510
|
-
? "worker"
|
|
511
|
-
: "interactive";
|
|
512
|
-
const workflow = admitWorkflowTurn(workflowRootId, workflowLane, undefined, workflowThreadId);
|
|
509
|
+
// Root, lane, and the refusal that follows from them, all live in ./workflow-refusal.
|
|
510
|
+
const workflow = admitHttpWorkflowTurn(req.headers);
|
|
513
511
|
if (workflow && !workflow.admitted) {
|
|
514
512
|
lease.release();
|
|
515
513
|
// withCors, because without Access-Control-Allow-Origin the exposed refusal header is
|
|
516
514
|
// still unreadable to a browser dashboard -- which made exposing it pointless.
|
|
517
|
-
return withCors(
|
|
515
|
+
return withCors(workflowDecisionRefusalResponse(workflow, undefined, refusalLog), req, policy);
|
|
518
516
|
}
|
|
519
517
|
const releaseWorkflow = (): void => { if (workflow?.admitted) workflow.lease.release(); };
|
|
520
518
|
let response: Response;
|
|
521
519
|
try {
|
|
522
|
-
response = await work(lease);
|
|
520
|
+
response = await runAdmittedBodyWork(req, policy, config.maxInboundBodyBytes, () => work(lease), refusalLog);
|
|
523
521
|
} catch (error) {
|
|
524
522
|
releaseWorkflow();
|
|
525
523
|
lease.release();
|
|
@@ -707,7 +705,7 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
|
|
|
707
705
|
get remoteWorkspaceStopping() { return remoteWorkspaceStopping; },
|
|
708
706
|
});
|
|
709
707
|
|
|
710
|
-
server = Bun.serve<WsData>({ ...serveOptions, port: listenPort, hostname: bindHost });
|
|
708
|
+
server = spendLedgerLifecycle.track(Bun.serve<WsData>({ ...serveOptions, port: listenPort, hostname: bindHost }));
|
|
711
709
|
|
|
712
710
|
// Both binds are one startup transaction (#1102). If the loopback bind fails after the
|
|
713
711
|
// public one succeeded, leaving the public listener up would strand it: the CLI's port
|
|
@@ -715,11 +713,11 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
|
|
|
715
713
|
// accumulating listeners. Roll back and rethrow the original error instead.
|
|
716
714
|
if (loopbackListenerPort !== null) {
|
|
717
715
|
try {
|
|
718
|
-
loopbackServer = Bun.serve<WsData>({
|
|
716
|
+
loopbackServer = spendLedgerLifecycle.track(Bun.serve<WsData>({
|
|
719
717
|
...serveOptions,
|
|
720
718
|
port: loopbackListenerPort,
|
|
721
719
|
hostname: "127.0.0.1",
|
|
722
|
-
});
|
|
720
|
+
}));
|
|
723
721
|
} catch (error) {
|
|
724
722
|
try {
|
|
725
723
|
// startServer is synchronous, so this rollback cannot await. Bun begins closing the
|
|
@@ -735,11 +733,11 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
|
|
|
735
733
|
}
|
|
736
734
|
if (managementIngressPort !== null) {
|
|
737
735
|
try {
|
|
738
|
-
managementIngressServer = Bun.serve<WsData>({
|
|
736
|
+
managementIngressServer = spendLedgerLifecycle.track(Bun.serve<WsData>({
|
|
739
737
|
...serveOptions,
|
|
740
738
|
port: managementIngressPort,
|
|
741
739
|
hostname: "127.0.0.1",
|
|
742
|
-
});
|
|
740
|
+
}));
|
|
743
741
|
} catch (error) {
|
|
744
742
|
// Preserve the management bind failure while synchronously initiating rollback of every
|
|
745
743
|
// listener already opened in this startup transaction. startServer must not become async.
|
|
@@ -796,7 +794,8 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
|
|
|
796
794
|
// removes the dir right after stop() settles would hit EPERM/EBUSY on Windows
|
|
797
795
|
// otherwise. Runs even when an earlier release rejected — that rejection still
|
|
798
796
|
// propagates, but not before the child is drained.
|
|
799
|
-
|
|
797
|
+
try { spendLedgerLifecycle.release(); }
|
|
798
|
+
finally { await flushConfigDirHardening(startupConfigDir); }
|
|
800
799
|
}
|
|
801
800
|
},
|
|
802
801
|
);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { OcxConfig } from "../../types";
|
|
2
2
|
import { isWildcardHostname } from "../../codex/loopback-target";
|
|
3
|
-
import { localInferenceDestination } from "../../lib/local-destinations";
|
|
4
|
-
import { probeHostname } from "../proxy-liveness";
|
|
3
|
+
import { localCredentialDestinationHostname, localInferenceDestination } from "../../lib/local-destinations";
|
|
5
4
|
import { isCanonicalOpenAiForwardProvider, OPENAI_API_PROVIDER_ID, OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers-destination";
|
|
6
5
|
import { LIVE_AUDIO_MODEL, TRANSCRIPTION_MODEL } from "../audio-upstream";
|
|
7
6
|
|
|
@@ -95,7 +94,7 @@ export function resolveApiAccessBaseUrl(
|
|
|
95
94
|
const port = config.port ?? 10100;
|
|
96
95
|
|
|
97
96
|
if (!isWildcardBindHost(config.hostname)) {
|
|
98
|
-
return `http://${
|
|
97
|
+
return `http://${localCredentialDestinationHostname(config.hostname)}:${port}/v1`;
|
|
99
98
|
}
|
|
100
99
|
|
|
101
100
|
const fromOrigin = opts.requestOrigin ? originBaseUrl(opts.requestOrigin) : null;
|
|
@@ -140,7 +139,7 @@ export function resolveApiAccessDisplayHost(
|
|
|
140
139
|
opts: BuildApiAccessEndpointsOptions = {},
|
|
141
140
|
): string {
|
|
142
141
|
if (!isWildcardBindHost(configHostname)) {
|
|
143
|
-
return
|
|
142
|
+
return localCredentialDestinationHostname(configHostname);
|
|
144
143
|
}
|
|
145
144
|
try {
|
|
146
145
|
return new URL(resolveApiAccessBaseUrl({ hostname: configHostname, port: 10100 }, opts)).hostname
|