@bitkyc08/opencodex 2.52.0-preview.20260912 → 2.53.0-preview.20260913
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/gui/dist/assets/index-BBOZWGB6.css +1 -0
- package/gui/dist/assets/index-D7ynYo2K.js +128 -0
- package/gui/dist/index.html +2 -2
- package/native/remote-workspace-helper/Cargo.lock +130 -0
- package/native/remote-workspace-helper/Cargo.toml +24 -0
- package/native/remote-workspace-helper/src/main.rs +49 -0
- package/native/remote-workspace-helper/src/protocol.rs +246 -0
- package/native/remote-workspace-helper/src/sandbox/macos.rs +19 -0
- package/native/remote-workspace-helper/src/sandbox/mod.rs +77 -0
- package/native/remote-workspace-helper/src/sandbox/windows.rs +15 -0
- package/package.json +6 -1
- package/src/adapters/anthropic-image-normalize.ts +30 -2
- package/src/adapters/anthropic.ts +1 -1
- package/src/adapters/base.ts +8 -2
- package/src/adapters/cursor/cursor-errors.ts +12 -0
- package/src/adapters/cursor/thread-continuity.ts +93 -0
- package/src/adapters/cursor.ts +104 -73
- package/src/adapters/devin/cloud-direct/chat.ts +312 -23
- package/src/adapters/devin/cloud-direct/metadata.ts +31 -2
- package/src/adapters/devin/live-models.ts +70 -3
- package/src/adapters/devin.ts +281 -21
- package/src/adapters/google-wire-compiler.ts +14 -6
- package/src/adapters/google.ts +22 -8
- package/src/adapters/kiro/adapter.ts +316 -0
- package/src/adapters/kiro/conversation.ts +136 -0
- package/src/adapters/kiro/payload.ts +432 -0
- package/src/adapters/kiro/reasoning.ts +56 -0
- package/src/adapters/kiro/stream.ts +1153 -0
- package/src/adapters/kiro/usage.ts +223 -0
- package/src/adapters/kiro/wire.ts +76 -0
- package/src/adapters/kiro.ts +8 -2319
- package/src/adapters/mimo-free.ts +1 -1
- package/src/adapters/openai-chat-images.ts +101 -0
- package/src/adapters/openai-chat.ts +201 -181
- package/src/adapters/openai-responses.ts +92 -224
- package/src/adapters/registry.ts +0 -7
- package/src/adapters/run-turn-queue.ts +13 -6
- package/src/bridge.ts +14 -15
- package/src/chat/inbound.ts +29 -4
- package/src/chat/outbound.ts +145 -107
- package/src/claude/desktop-profile.ts +4 -6
- package/src/cli/account-api.ts +14 -0
- package/src/cli/account-extended.ts +1 -1
- package/src/cli/account-history.ts +60 -0
- package/src/cli/account-main.ts +80 -0
- package/src/cli/account.ts +11 -3
- package/src/cli/capabilities.ts +113 -0
- package/src/cli/catalog.ts +109 -0
- package/src/cli/dispatch.ts +9 -0
- package/src/cli/help.ts +2 -0
- package/src/cli/index.ts +2 -2
- package/src/cli/observe.ts +28 -1
- package/src/cli/opencode.ts +42 -8
- package/src/cli/provider-runtime.ts +11 -1
- package/src/cli/provider.ts +22 -2
- package/src/cli/registry.ts +21 -0
- package/src/cli/remote-workspace.ts +154 -0
- package/src/cli/status.ts +39 -7
- package/src/cli/usage-report.ts +14 -2
- package/src/client/hub-client.ts +34 -0
- package/src/client/hub-state.ts +9 -1
- package/src/codex/account-store.ts +78 -0
- package/src/codex/auth-api.ts +81 -54
- package/src/codex/auth-context.ts +45 -16
- package/src/codex/catalog/effort.ts +1 -1
- package/src/codex/catalog/metadata.ts +3 -6
- package/src/codex/catalog/native-models.ts +4 -4
- package/src/codex/catalog/parsing.ts +2 -20
- package/src/codex/catalog/provider-fetch.ts +10 -1
- package/src/codex/catalog/remote.ts +233 -0
- package/src/codex/catalog/sync.ts +403 -35
- package/src/codex/convergence.ts +1 -1
- package/src/codex/history-manifest.ts +36 -0
- package/src/codex/history-provider.ts +32 -5
- package/src/codex/inject.ts +9 -0
- package/src/codex/main-account.ts +113 -0
- package/src/codex/main-device-reauth-api.ts +89 -0
- package/src/codex/main-device-reauth.ts +217 -0
- package/src/codex/native-residue.ts +9 -2
- package/src/codex/quota-auto-refresh.ts +3 -2
- package/src/codex/quota-capacity.ts +98 -0
- package/src/codex/quota-history.ts +160 -0
- package/src/codex/quota-types.ts +8 -0
- package/src/codex/quota.ts +118 -91
- package/src/codex/refresh.ts +2 -1
- package/src/codex/routing.ts +90 -17
- package/src/codex/sync.ts +33 -4
- package/src/combos/request.ts +19 -1
- package/src/config/multi-agent-surface.ts +61 -0
- package/src/config/provider-validation.ts +176 -0
- package/src/config.ts +213 -11
- package/src/generated/compatibility-version.json +436 -168
- package/src/images/loop.ts +119 -36
- package/src/lib/admission.ts +12 -6
- package/src/lib/redact.ts +7 -0
- package/src/lib/translator-budget.ts +4 -3
- package/src/lib/windows-atomic-replace.ts +1 -0
- package/src/lib/windows-elevation.ts +1 -1
- package/src/oauth/chatgpt-device.ts +62 -5
- package/src/oauth/devin/cli-import.ts +130 -0
- package/src/oauth/devin.ts +63 -8
- package/src/oauth/index.ts +29 -14
- package/src/oauth/kiro.ts +18 -6
- package/src/oauth/login-cli.ts +9 -1
- package/src/oauth/meta-muse-device.ts +464 -0
- package/src/oauth/meta-muse.ts +123 -32
- package/src/oauth/pool-kernel.ts +9 -0
- package/src/oauth/pool-settings-capability.ts +2 -2
- package/src/oauth/store.ts +57 -0
- package/src/oauth/types.ts +31 -0
- package/src/providers/derive.ts +13 -3
- package/src/providers/devin-cli-authmode-migration.ts +57 -35
- package/src/providers/devin-provider-merge-migration.ts +240 -0
- package/src/providers/muse-key-quota.ts +117 -0
- package/src/providers/muse-subscription-usage.ts +14 -2
- package/src/providers/openai-sidecar.ts +25 -3
- package/src/providers/opencode-zen-rate-limit.ts +58 -0
- package/src/providers/provider-id-rewrite.ts +20 -5
- package/src/providers/quota-types.ts +12 -0
- package/src/providers/quota.ts +143 -102
- package/src/providers/reasoning-metadata.ts +543 -0
- package/src/providers/registry.ts +80 -49
- package/src/reasoning-effort.ts +26 -2
- package/src/remote/hub-usage.ts +32 -0
- package/src/remote-control/index.ts +192 -41
- package/src/remote-control/workspace-activation.ts +9 -0
- package/src/remote-control/workspace-agent-connection.ts +366 -0
- package/src/remote-control/workspace-claude-runtime.ts +243 -0
- package/src/remote-control/workspace-codex-runtime.ts +531 -0
- package/src/remote-control/workspace-codex-sandbox.ts +115 -0
- package/src/remote-control/workspace-command-runner.ts +748 -0
- package/src/remote-control/workspace-coordinator.ts +231 -0
- package/src/remote-control/workspace-device.ts +585 -0
- package/src/remote-control/workspace-executable.ts +43 -0
- package/src/remote-control/workspace-executor.ts +397 -0
- package/src/remote-control/workspace-hub.ts +519 -0
- package/src/remote-control/workspace-pi-runtime.ts +382 -0
- package/src/remote-control/workspace-process.ts +129 -0
- package/src/remote-control/workspace-rpc.ts +304 -0
- package/src/remote-control/workspace-runtime.ts +60 -0
- package/src/remote-control/workspace-secret-store.ts +39 -0
- package/src/remote-control/workspace-sessions.ts +799 -0
- package/src/remote-control/workspace-tool-bridge.ts +192 -0
- package/src/responses/code-mode-helper-compat.ts +22 -3
- package/src/responses/hosted-tool-policy.ts +0 -1
- package/src/responses/muse-tool-name-alias.ts +379 -0
- package/src/responses/plaintext-v2-agent-messages.ts +902 -0
- package/src/router.ts +7 -0
- package/src/routing/compatibility/behavior.ts +0 -1
- package/src/server/audio-client.ts +64 -0
- package/src/server/audio-dictation.ts +91 -0
- package/src/server/audio-live.ts +185 -0
- package/src/server/audio-transcriptions.ts +183 -0
- package/src/server/audio-upstream.ts +153 -0
- package/src/server/auth-cors.ts +61 -2
- package/src/server/chat-completions.ts +1 -1
- package/src/server/chat-native-sse.ts +92 -48
- package/src/server/chat-native.ts +37 -15
- package/src/server/hub-usage.ts +57 -0
- package/src/server/images.ts +4 -0
- package/src/server/index.ts +722 -57
- package/src/server/lifecycle.ts +5 -6
- package/src/server/live-call-bindings.ts +60 -0
- package/src/server/live.ts +12 -1
- package/src/server/management/agent-settings-routes.ts +25 -4
- package/src/server/management/api-access.ts +37 -0
- package/src/server/management/api-key-usage.ts +7 -2
- package/src/server/management/config-routes.ts +1 -18
- package/src/server/management/context.ts +15 -0
- package/src/server/management/logs-usage-routes.ts +2 -0
- package/src/server/management/oauth-account-routes.ts +39 -12
- package/src/server/management/provider-routes.ts +125 -2
- package/src/server/management/remote-workspace-routes.ts +140 -0
- package/src/server/management/route-registry.ts +15 -0
- package/src/server/management/usage-aggregate-cache.ts +14 -15
- package/src/server/management/usage-summary-cache.ts +2 -0
- package/src/server/management-api.ts +23 -0
- package/src/server/ports.ts +17 -0
- package/src/server/relay-eager.ts +4 -1
- package/src/server/relay.ts +70 -10
- package/src/server/request-decompress.ts +6 -3
- package/src/server/responses/agent-task-recovery.ts +25 -32
- package/src/server/responses/codex-auth-error.ts +11 -0
- package/src/server/responses/codex-ws-exchange.ts +52 -3
- package/src/server/responses/codex-ws-wire.ts +55 -0
- package/src/server/responses/compact.ts +9 -1
- package/src/server/responses/core.ts +337 -73
- package/src/server/responses/encrypted-payload.ts +45 -2
- package/src/server/responses/ws-upstream.ts +4 -1
- package/src/server/responses-self-named-namespace-scrub.ts +1 -3
- package/src/server/responses-undeclared-tool-guard.ts +1 -1
- package/src/server/search.ts +3 -0
- package/src/server/sse-payload-rewrite.ts +136 -51
- package/src/server/ws-bridge.ts +35 -1
- package/src/service/cli.ts +372 -0
- package/src/service/diagnostics.ts +340 -0
- package/src/service/guards.ts +303 -0
- package/src/service/health.ts +222 -0
- package/src/service/launchd.ts +853 -0
- package/src/service/orchestration.ts +617 -0
- package/src/service/repair.ts +334 -0
- package/src/service/state.ts +363 -0
- package/src/service/systemd.ts +229 -0
- package/src/service/windows-ops.ts +690 -0
- package/src/service/windows-scheduler.ts +769 -0
- package/src/service/windows-taskxml.ts +613 -0
- package/src/service.ts +22 -5550
- package/src/storage/cleanup/db.ts +258 -0
- package/src/storage/cleanup/execute.ts +358 -0
- package/src/storage/cleanup/paths.ts +189 -0
- package/src/storage/cleanup/pending.ts +140 -0
- package/src/storage/cleanup/preview.ts +292 -0
- package/src/storage/cleanup/reconcile.ts +347 -0
- package/src/storage/cleanup/restore.ts +932 -0
- package/src/storage/cleanup/satellite.ts +474 -0
- package/src/storage/cleanup/staging.ts +129 -0
- package/src/storage/cleanup/types.ts +98 -0
- package/src/storage/cleanup.ts +49 -3127
- package/src/types/accounts.ts +2 -0
- package/src/types/config.ts +13 -12
- package/src/types/provider.ts +37 -0
- package/src/types/request.ts +2 -0
- package/src/types/tools.ts +17 -5
- package/src/types.ts +1 -0
- package/src/usage/expected-prices.ts +127 -0
- package/src/usage/log.ts +58 -1
- package/src/vision/eligibility.ts +13 -2
- package/src/web-search/loop.ts +56 -3
- package/gui/dist/assets/index-D_t6sCWs.js +0 -115
- package/gui/dist/assets/index-EdoPnm9_.css +0 -1
- package/src/adapters/devin-cli/acp.ts +0 -204
- package/src/adapters/devin-cli/adapter.ts +0 -345
- package/src/adapters/devin-cli/binary.ts +0 -69
- package/src/adapters/devin-cli/models.ts +0 -57
- package/src/oauth/devin-cli.ts +0 -149
- package/src/server/responses-reasoning-summary-rewrite.ts +0 -178
package/gui/dist/index.html
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
} catch (e) {}
|
|
17
17
|
})();
|
|
18
18
|
</script>
|
|
19
|
-
<script type="module" crossorigin src="/assets/index-
|
|
20
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
19
|
+
<script type="module" crossorigin src="/assets/index-D7ynYo2K.js"></script>
|
|
20
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BBOZWGB6.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
|
23
23
|
<div id="root"></div>
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "base64"
|
|
7
|
+
version = "0.22.1"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "itoa"
|
|
13
|
+
version = "1.0.18"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|
16
|
+
|
|
17
|
+
[[package]]
|
|
18
|
+
name = "memchr"
|
|
19
|
+
version = "2.8.3"
|
|
20
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
21
|
+
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
|
22
|
+
|
|
23
|
+
[[package]]
|
|
24
|
+
name = "opencodex-remote-workspace-helper"
|
|
25
|
+
version = "0.1.0"
|
|
26
|
+
dependencies = [
|
|
27
|
+
"base64",
|
|
28
|
+
"serde",
|
|
29
|
+
"serde_json",
|
|
30
|
+
"windows-sys",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[[package]]
|
|
34
|
+
name = "proc-macro2"
|
|
35
|
+
version = "1.0.107"
|
|
36
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
37
|
+
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
|
38
|
+
dependencies = [
|
|
39
|
+
"unicode-ident",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[[package]]
|
|
43
|
+
name = "quote"
|
|
44
|
+
version = "1.0.47"
|
|
45
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
46
|
+
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
|
47
|
+
dependencies = [
|
|
48
|
+
"proc-macro2",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[[package]]
|
|
52
|
+
name = "serde"
|
|
53
|
+
version = "1.0.229"
|
|
54
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
55
|
+
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
|
56
|
+
dependencies = [
|
|
57
|
+
"serde_core",
|
|
58
|
+
"serde_derive",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[[package]]
|
|
62
|
+
name = "serde_core"
|
|
63
|
+
version = "1.0.229"
|
|
64
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
65
|
+
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
|
66
|
+
dependencies = [
|
|
67
|
+
"serde_derive",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
[[package]]
|
|
71
|
+
name = "serde_derive"
|
|
72
|
+
version = "1.0.229"
|
|
73
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
74
|
+
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
|
75
|
+
dependencies = [
|
|
76
|
+
"proc-macro2",
|
|
77
|
+
"quote",
|
|
78
|
+
"syn",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
[[package]]
|
|
82
|
+
name = "serde_json"
|
|
83
|
+
version = "1.0.151"
|
|
84
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
85
|
+
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
|
86
|
+
dependencies = [
|
|
87
|
+
"itoa",
|
|
88
|
+
"memchr",
|
|
89
|
+
"serde",
|
|
90
|
+
"serde_core",
|
|
91
|
+
"zmij",
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
[[package]]
|
|
95
|
+
name = "syn"
|
|
96
|
+
version = "3.0.4"
|
|
97
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
98
|
+
checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f"
|
|
99
|
+
dependencies = [
|
|
100
|
+
"proc-macro2",
|
|
101
|
+
"quote",
|
|
102
|
+
"unicode-ident",
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
[[package]]
|
|
106
|
+
name = "unicode-ident"
|
|
107
|
+
version = "1.0.24"
|
|
108
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
109
|
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
110
|
+
|
|
111
|
+
[[package]]
|
|
112
|
+
name = "windows-link"
|
|
113
|
+
version = "0.2.1"
|
|
114
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
115
|
+
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
|
116
|
+
|
|
117
|
+
[[package]]
|
|
118
|
+
name = "windows-sys"
|
|
119
|
+
version = "0.61.2"
|
|
120
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
121
|
+
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
|
122
|
+
dependencies = [
|
|
123
|
+
"windows-link",
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
[[package]]
|
|
127
|
+
name = "zmij"
|
|
128
|
+
version = "1.0.23"
|
|
129
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
130
|
+
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "opencodex-remote-workspace-helper"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2024"
|
|
5
|
+
license = "MIT"
|
|
6
|
+
publish = false
|
|
7
|
+
|
|
8
|
+
[dependencies]
|
|
9
|
+
base64 = "0.22"
|
|
10
|
+
serde = { version = "1", features = ["derive"] }
|
|
11
|
+
serde_json = "1"
|
|
12
|
+
|
|
13
|
+
[target.'cfg(windows)'.dependencies]
|
|
14
|
+
windows-sys = { version = "0.61", features = [
|
|
15
|
+
"Win32_Foundation",
|
|
16
|
+
"Win32_Security",
|
|
17
|
+
"Win32_Security_Authorization",
|
|
18
|
+
"Win32_Security_Isolation",
|
|
19
|
+
"Win32_Storage_FileSystem",
|
|
20
|
+
"Win32_System_JobObjects",
|
|
21
|
+
"Win32_System_Memory",
|
|
22
|
+
"Win32_System_Pipes",
|
|
23
|
+
"Win32_System_Threading",
|
|
24
|
+
] }
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#![deny(unsafe_op_in_unsafe_fn)]
|
|
2
|
+
|
|
3
|
+
mod protocol;
|
|
4
|
+
mod sandbox;
|
|
5
|
+
|
|
6
|
+
use std::io::{self, Read, Write};
|
|
7
|
+
|
|
8
|
+
use protocol::{HelperRequest, HelperResponse, MAX_REQUEST_BYTES, PROTOCOL_VERSION};
|
|
9
|
+
|
|
10
|
+
fn main() {
|
|
11
|
+
if std::env::args().nth(1).as_deref() == Some("__probe-child") {
|
|
12
|
+
std::process::exit(sandbox::run_probe_child());
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let response = match read_request().and_then(handle_request) {
|
|
16
|
+
Ok(response) => response,
|
|
17
|
+
Err(error) => HelperResponse::error(error),
|
|
18
|
+
};
|
|
19
|
+
let mut stdout = io::stdout().lock();
|
|
20
|
+
if serde_json::to_writer(&mut stdout, &response).is_err() || stdout.write_all(b"\n").is_err() {
|
|
21
|
+
std::process::exit(2);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
fn read_request() -> Result<HelperRequest, String> {
|
|
26
|
+
let mut body = Vec::new();
|
|
27
|
+
io::stdin()
|
|
28
|
+
.take((MAX_REQUEST_BYTES + 1) as u64)
|
|
29
|
+
.read_to_end(&mut body)
|
|
30
|
+
.map_err(|_| "could not read helper request".to_owned())?;
|
|
31
|
+
if body.len() > MAX_REQUEST_BYTES {
|
|
32
|
+
return Err("helper request exceeds its size limit".to_owned());
|
|
33
|
+
}
|
|
34
|
+
let request: HelperRequest =
|
|
35
|
+
serde_json::from_slice(&body).map_err(|_| "helper request is invalid".to_owned())?;
|
|
36
|
+
request.validate()?;
|
|
37
|
+
Ok(request)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
fn handle_request(request: HelperRequest) -> Result<HelperResponse, String> {
|
|
41
|
+
if request.version != PROTOCOL_VERSION {
|
|
42
|
+
return Err("unsupported helper protocol version".to_owned());
|
|
43
|
+
}
|
|
44
|
+
match request.operation.as_str() {
|
|
45
|
+
"probe" => sandbox::probe().map(|()| HelperResponse::probe_success()),
|
|
46
|
+
"run" => sandbox::run(&request).map(HelperResponse::command_success),
|
|
47
|
+
_ => Err("unsupported helper operation".to_owned()),
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
|
2
|
+
use serde::{Deserialize, Serialize};
|
|
3
|
+
use std::path::Path;
|
|
4
|
+
#[cfg(target_os = "windows")]
|
|
5
|
+
use std::path::PathBuf;
|
|
6
|
+
|
|
7
|
+
pub const PROTOCOL_VERSION: u8 = 1;
|
|
8
|
+
pub const MAX_REQUEST_BYTES: usize = 64 * 1024;
|
|
9
|
+
pub const MAX_OUTPUT_BYTES: usize = 256 * 1024;
|
|
10
|
+
const MAX_PATH_BYTES: usize = 4096;
|
|
11
|
+
const MAX_COMMAND_ARGUMENTS: usize = 64;
|
|
12
|
+
const MAX_COMMAND_ARGUMENT_BYTES: usize = 4096;
|
|
13
|
+
const MAX_COMMAND_BYTES: usize = 16 * 1024;
|
|
14
|
+
const MAX_TOOLCHAIN_ROOTS: usize = 16;
|
|
15
|
+
|
|
16
|
+
#[derive(Debug, Deserialize)]
|
|
17
|
+
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
|
18
|
+
pub struct HelperRequest {
|
|
19
|
+
pub version: u8,
|
|
20
|
+
pub operation: String,
|
|
21
|
+
#[serde(default)]
|
|
22
|
+
pub root: String,
|
|
23
|
+
#[serde(default)]
|
|
24
|
+
pub cwd: String,
|
|
25
|
+
#[serde(default)]
|
|
26
|
+
pub command: Vec<String>,
|
|
27
|
+
#[serde(default)]
|
|
28
|
+
pub toolchain_roots: Vec<String>,
|
|
29
|
+
#[serde(default)]
|
|
30
|
+
pub timeout_ms: u64,
|
|
31
|
+
#[serde(default)]
|
|
32
|
+
pub max_output_bytes: usize,
|
|
33
|
+
#[serde(default)]
|
|
34
|
+
pub network_access: bool,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
impl HelperRequest {
|
|
38
|
+
pub fn validate(&self) -> Result<(), String> {
|
|
39
|
+
if self.operation == "probe" {
|
|
40
|
+
if !self.root.is_empty()
|
|
41
|
+
|| !self.cwd.is_empty()
|
|
42
|
+
|| !self.command.is_empty()
|
|
43
|
+
|| !self.toolchain_roots.is_empty()
|
|
44
|
+
|| self.timeout_ms != 0
|
|
45
|
+
|| self.max_output_bytes != 0
|
|
46
|
+
|| self.network_access
|
|
47
|
+
{
|
|
48
|
+
return Err("probe request must not carry command authority".to_owned());
|
|
49
|
+
}
|
|
50
|
+
return Ok(());
|
|
51
|
+
}
|
|
52
|
+
if self.operation != "run" {
|
|
53
|
+
return Ok(());
|
|
54
|
+
}
|
|
55
|
+
validate_path(&self.root, "workspace root")?;
|
|
56
|
+
validate_path(&self.cwd, "command cwd")?;
|
|
57
|
+
if !Path::new(&self.root).is_absolute() || !Path::new(&self.cwd).is_absolute() {
|
|
58
|
+
return Err("workspace root and cwd must be absolute".to_owned());
|
|
59
|
+
}
|
|
60
|
+
if self.command.is_empty() || self.command.len() > MAX_COMMAND_ARGUMENTS {
|
|
61
|
+
return Err("invalid command vector".to_owned());
|
|
62
|
+
}
|
|
63
|
+
let mut command_bytes = 0usize;
|
|
64
|
+
for value in &self.command {
|
|
65
|
+
if value.is_empty() || value.len() > MAX_COMMAND_ARGUMENT_BYTES || value.contains('\0')
|
|
66
|
+
{
|
|
67
|
+
return Err("invalid command vector".to_owned());
|
|
68
|
+
}
|
|
69
|
+
command_bytes = command_bytes
|
|
70
|
+
.checked_add(value.len())
|
|
71
|
+
.ok_or_else(|| "command vector is too large".to_owned())?;
|
|
72
|
+
}
|
|
73
|
+
if command_bytes > MAX_COMMAND_BYTES {
|
|
74
|
+
return Err("command vector is too large".to_owned());
|
|
75
|
+
}
|
|
76
|
+
if self.toolchain_roots.len() > MAX_TOOLCHAIN_ROOTS {
|
|
77
|
+
return Err("too many toolchain roots".to_owned());
|
|
78
|
+
}
|
|
79
|
+
for path in &self.toolchain_roots {
|
|
80
|
+
validate_path(path, "toolchain root")?;
|
|
81
|
+
if !Path::new(path).is_absolute() {
|
|
82
|
+
return Err("toolchain roots must be absolute".to_owned());
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if !(1..=60_000).contains(&self.timeout_ms) {
|
|
86
|
+
return Err("command timeout is outside its limit".to_owned());
|
|
87
|
+
}
|
|
88
|
+
if !(1024..=MAX_OUTPUT_BYTES).contains(&self.max_output_bytes) {
|
|
89
|
+
return Err("command output limit is outside its limit".to_owned());
|
|
90
|
+
}
|
|
91
|
+
Ok(())
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
#[cfg(target_os = "windows")]
|
|
95
|
+
pub fn canonical_paths(&self) -> Result<CanonicalPaths, String> {
|
|
96
|
+
let root = canonical_directory(&self.root, "workspace root")?;
|
|
97
|
+
let cwd = canonical_directory(&self.cwd, "command cwd")?;
|
|
98
|
+
if !cwd.starts_with(&root) {
|
|
99
|
+
return Err("command cwd escaped its workspace root".to_owned());
|
|
100
|
+
}
|
|
101
|
+
let mut toolchain_roots = Vec::with_capacity(self.toolchain_roots.len());
|
|
102
|
+
for value in &self.toolchain_roots {
|
|
103
|
+
let canonical = canonical_directory(value, "toolchain root")?;
|
|
104
|
+
if !toolchain_roots.contains(&canonical) {
|
|
105
|
+
toolchain_roots.push(canonical);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
Ok(CanonicalPaths {
|
|
109
|
+
root,
|
|
110
|
+
cwd,
|
|
111
|
+
toolchain_roots,
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fn validate_path(value: &str, label: &str) -> Result<(), String> {
|
|
117
|
+
if value.is_empty() || value.len() > MAX_PATH_BYTES || value.contains('\0') {
|
|
118
|
+
return Err(format!("invalid {label}"));
|
|
119
|
+
}
|
|
120
|
+
Ok(())
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
#[cfg(target_os = "windows")]
|
|
124
|
+
fn canonical_directory(value: &str, label: &str) -> Result<PathBuf, String> {
|
|
125
|
+
let original = Path::new(value);
|
|
126
|
+
let metadata =
|
|
127
|
+
std::fs::symlink_metadata(original).map_err(|_| format!("{label} is unavailable"))?;
|
|
128
|
+
if metadata.file_type().is_symlink() || !metadata.is_dir() {
|
|
129
|
+
return Err(format!("{label} must remain a real directory"));
|
|
130
|
+
}
|
|
131
|
+
original
|
|
132
|
+
.canonicalize()
|
|
133
|
+
.map_err(|_| format!("{label} is unavailable"))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#[cfg(target_os = "windows")]
|
|
137
|
+
#[derive(Debug)]
|
|
138
|
+
pub struct CanonicalPaths {
|
|
139
|
+
pub root: PathBuf,
|
|
140
|
+
pub cwd: PathBuf,
|
|
141
|
+
pub toolchain_roots: Vec<PathBuf>,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#[derive(Debug)]
|
|
145
|
+
pub struct CommandOutcome {
|
|
146
|
+
pub exit_code: i32,
|
|
147
|
+
pub stdout: Vec<u8>,
|
|
148
|
+
pub stderr: Vec<u8>,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
#[derive(Debug, Serialize)]
|
|
152
|
+
#[serde(rename_all = "camelCase")]
|
|
153
|
+
pub struct HelperResponse {
|
|
154
|
+
version: u8,
|
|
155
|
+
ok: bool,
|
|
156
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
157
|
+
probe: Option<bool>,
|
|
158
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
159
|
+
exit_code: Option<i32>,
|
|
160
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
161
|
+
stdout_base64: Option<String>,
|
|
162
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
163
|
+
stderr_base64: Option<String>,
|
|
164
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
165
|
+
error: Option<String>,
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
impl HelperResponse {
|
|
169
|
+
pub fn error(error: String) -> Self {
|
|
170
|
+
Self {
|
|
171
|
+
version: PROTOCOL_VERSION,
|
|
172
|
+
ok: false,
|
|
173
|
+
probe: None,
|
|
174
|
+
exit_code: None,
|
|
175
|
+
stdout_base64: None,
|
|
176
|
+
stderr_base64: None,
|
|
177
|
+
error: Some(limit_error(error)),
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
pub fn probe_success() -> Self {
|
|
182
|
+
Self {
|
|
183
|
+
version: PROTOCOL_VERSION,
|
|
184
|
+
ok: true,
|
|
185
|
+
probe: Some(true),
|
|
186
|
+
exit_code: None,
|
|
187
|
+
stdout_base64: None,
|
|
188
|
+
stderr_base64: None,
|
|
189
|
+
error: None,
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
pub fn command_success(outcome: CommandOutcome) -> Self {
|
|
194
|
+
Self {
|
|
195
|
+
version: PROTOCOL_VERSION,
|
|
196
|
+
ok: true,
|
|
197
|
+
probe: None,
|
|
198
|
+
exit_code: Some(outcome.exit_code),
|
|
199
|
+
stdout_base64: Some(STANDARD.encode(outcome.stdout)),
|
|
200
|
+
stderr_base64: Some(STANDARD.encode(outcome.stderr)),
|
|
201
|
+
error: None,
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
fn limit_error(mut value: String) -> String {
|
|
207
|
+
const MAX_ERROR_CHARS: usize = 512;
|
|
208
|
+
if value.chars().count() <= MAX_ERROR_CHARS {
|
|
209
|
+
return value;
|
|
210
|
+
}
|
|
211
|
+
value = value.chars().take(MAX_ERROR_CHARS).collect();
|
|
212
|
+
value.push('…');
|
|
213
|
+
value
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
#[cfg(test)]
|
|
217
|
+
mod tests {
|
|
218
|
+
use super::*;
|
|
219
|
+
|
|
220
|
+
#[test]
|
|
221
|
+
fn rejects_authority_smuggled_into_probe() {
|
|
222
|
+
let request: HelperRequest =
|
|
223
|
+
serde_json::from_str(r#"{"version":1,"operation":"probe","command":["whoami"]}"#)
|
|
224
|
+
.expect("valid JSON fixture");
|
|
225
|
+
assert!(request.validate().is_err());
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
#[test]
|
|
229
|
+
fn rejects_unknown_wire_fields() {
|
|
230
|
+
assert!(
|
|
231
|
+
serde_json::from_str::<HelperRequest>(
|
|
232
|
+
r#"{"version":1,"operation":"probe","surprise":true}"#,
|
|
233
|
+
)
|
|
234
|
+
.is_err()
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
#[test]
|
|
239
|
+
fn bounds_command_shape_before_platform_code() {
|
|
240
|
+
let request: HelperRequest = serde_json::from_str(
|
|
241
|
+
r#"{"version":1,"operation":"run","root":"/tmp/a","cwd":"/tmp/a","command":["x"],"timeoutMs":0,"maxOutputBytes":262144}"#,
|
|
242
|
+
)
|
|
243
|
+
.expect("valid JSON fixture");
|
|
244
|
+
assert!(request.validate().is_err());
|
|
245
|
+
}
|
|
246
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
use crate::protocol::{CommandOutcome, HelperRequest};
|
|
2
|
+
|
|
3
|
+
const MACOS_CONFINEMENT_UNAVAILABLE: &str =
|
|
4
|
+
"macOS Remote Workspace command confinement is unavailable; file tools remain enabled";
|
|
5
|
+
|
|
6
|
+
/// macOS has no unprivileged Job Object or cgroup equivalent that can revoke every descendant's
|
|
7
|
+
/// workspace access. A Seatbelt profile can constrain a process, but allowing subprocesses lets a
|
|
8
|
+
/// descendant call `setsid()` and outlive cancellation. Importing broad system profiles merely to
|
|
9
|
+
/// make a single-process probe start would also widen unrelated host-service authority. Until a
|
|
10
|
+
/// native containment owner closes both boundaries, command execution must stay unavailable.
|
|
11
|
+
pub fn probe() -> Result<(), String> {
|
|
12
|
+
Err(MACOS_CONFINEMENT_UNAVAILABLE.to_owned())
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/// Keep the helper itself fail-closed even if a caller bypasses OCX capability negotiation and
|
|
16
|
+
/// submits a `run` request directly.
|
|
17
|
+
pub fn run(_request: &HelperRequest) -> Result<CommandOutcome, String> {
|
|
18
|
+
Err(MACOS_CONFINEMENT_UNAVAILABLE.to_owned())
|
|
19
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#[cfg(target_os = "macos")]
|
|
2
|
+
mod macos;
|
|
3
|
+
#[cfg(target_os = "windows")]
|
|
4
|
+
mod windows;
|
|
5
|
+
|
|
6
|
+
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
|
7
|
+
use crate::protocol::{CommandOutcome, HelperRequest};
|
|
8
|
+
use std::fs::{self, OpenOptions};
|
|
9
|
+
use std::io::Read;
|
|
10
|
+
use std::net::{SocketAddr, TcpStream};
|
|
11
|
+
use std::time::Duration;
|
|
12
|
+
|
|
13
|
+
#[cfg(target_os = "macos")]
|
|
14
|
+
pub use macos::{probe, run};
|
|
15
|
+
#[cfg(target_os = "windows")]
|
|
16
|
+
pub use windows::{probe, run};
|
|
17
|
+
|
|
18
|
+
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
|
19
|
+
pub fn probe() -> Result<(), String> {
|
|
20
|
+
Err("native helper is supported only on macOS and Windows".to_owned())
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
|
24
|
+
pub fn run(_request: &HelperRequest) -> Result<CommandOutcome, String> {
|
|
25
|
+
Err("native helper is supported only on macOS and Windows".to_owned())
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
pub fn run_probe_child() -> i32 {
|
|
29
|
+
let mut args = std::env::args().skip(2);
|
|
30
|
+
let Some(workspace) = args.next() else {
|
|
31
|
+
return 20;
|
|
32
|
+
};
|
|
33
|
+
let Some(outside_file) = args.next() else {
|
|
34
|
+
return 21;
|
|
35
|
+
};
|
|
36
|
+
let Some(outside_write) = args.next() else {
|
|
37
|
+
return 22;
|
|
38
|
+
};
|
|
39
|
+
let Some(listener_address) = args.next() else {
|
|
40
|
+
return 23;
|
|
41
|
+
};
|
|
42
|
+
let Some(existing_workspace_file) = args.next() else {
|
|
43
|
+
return 24;
|
|
44
|
+
};
|
|
45
|
+
if args.next().is_some() {
|
|
46
|
+
return 24;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let marker = std::path::Path::new(&workspace).join("probe-marker");
|
|
50
|
+
if fs::write(&marker, b"sandboxed").is_err() {
|
|
51
|
+
return 25;
|
|
52
|
+
}
|
|
53
|
+
if !matches!(fs::read(&existing_workspace_file), Ok(value) if value == b"existing")
|
|
54
|
+
|| fs::write(&existing_workspace_file, b"updated").is_err()
|
|
55
|
+
{
|
|
56
|
+
return 29;
|
|
57
|
+
}
|
|
58
|
+
let mut outside = Vec::new();
|
|
59
|
+
if OpenOptions::new()
|
|
60
|
+
.read(true)
|
|
61
|
+
.open(&outside_file)
|
|
62
|
+
.and_then(|mut file| file.read_to_end(&mut outside))
|
|
63
|
+
.is_ok()
|
|
64
|
+
{
|
|
65
|
+
return 26;
|
|
66
|
+
}
|
|
67
|
+
if fs::write(&outside_write, b"escaped").is_ok() {
|
|
68
|
+
return 27;
|
|
69
|
+
}
|
|
70
|
+
let Ok(listener_address) = listener_address.parse::<SocketAddr>() else {
|
|
71
|
+
return 23;
|
|
72
|
+
};
|
|
73
|
+
if TcpStream::connect_timeout(&listener_address, Duration::from_millis(500)).is_ok() {
|
|
74
|
+
return 28;
|
|
75
|
+
}
|
|
76
|
+
0
|
|
77
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
use crate::protocol::{CommandOutcome, HelperRequest};
|
|
2
|
+
|
|
3
|
+
const WINDOWS_CONFINEMENT_UNAVAILABLE: &str =
|
|
4
|
+
"Windows Remote Workspace command confinement is unavailable; command execution is disabled";
|
|
5
|
+
|
|
6
|
+
// A command-capable implementation must retain cleanup ownership through helper cancellation
|
|
7
|
+
// and establish Job membership atomically. Until that owner is implemented and verified,
|
|
8
|
+
// direct helper requests and capability probes refuse before allocating OS resources.
|
|
9
|
+
pub fn probe() -> Result<(), String> {
|
|
10
|
+
Err(WINDOWS_CONFINEMENT_UNAVAILABLE.to_owned())
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
pub fn run(_request: &HelperRequest) -> Result<CommandOutcome, String> {
|
|
14
|
+
Err(WINDOWS_CONFINEMENT_UNAVAILABLE.to_owned())
|
|
15
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitkyc08/opencodex",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.53.0-preview.20260913",
|
|
4
4
|
"description": "Universal provider proxy for OpenAI Codex & Claude Code — use any LLM with Codex CLI/App/SDK and Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./bin/package-main.mjs",
|
|
@@ -28,6 +28,9 @@
|
|
|
28
28
|
"README.md",
|
|
29
29
|
"SPONSORS.md",
|
|
30
30
|
"AGENTS_INSTALL.md",
|
|
31
|
+
"native/remote-workspace-helper/Cargo.toml",
|
|
32
|
+
"native/remote-workspace-helper/Cargo.lock",
|
|
33
|
+
"native/remote-workspace-helper/src",
|
|
31
34
|
"LICENSE"
|
|
32
35
|
],
|
|
33
36
|
"engines": {
|
|
@@ -52,6 +55,8 @@
|
|
|
52
55
|
"structure:check": "bun scripts/structure-ssot.ts",
|
|
53
56
|
"generate:model-metadata": "bun scripts/generate-model-metadata.ts",
|
|
54
57
|
"build:gui": "cd gui && bun install --frozen-lockfile && bun run build && cd .. && bun run prepare:package",
|
|
58
|
+
"build:remote-workspace-helper": "cargo build --release --locked --manifest-path native/remote-workspace-helper/Cargo.toml",
|
|
59
|
+
"test:remote-workspace-helper": "cargo test --locked --manifest-path native/remote-workspace-helper/Cargo.toml",
|
|
55
60
|
"prepare:package": "bun scripts/prepare-package.ts",
|
|
56
61
|
"prepack": "bun run prepare:package",
|
|
57
62
|
"prepublishOnly": "bun run audit:high && bun run typecheck && bun run build:gui",
|
|
@@ -68,6 +68,14 @@ export interface NormalizeTarget {
|
|
|
68
68
|
mediaType: string;
|
|
69
69
|
replace(data: string, mediaType: string): void;
|
|
70
70
|
drop(note: string): void;
|
|
71
|
+
/**
|
|
72
|
+
* True when `drop` leaves the original bytes on the wire instead of removing or
|
|
73
|
+
* textifying them (openai-chat, which has no downstream guard that could re-attach a
|
|
74
|
+
* dropped image). The core normally stops counting a dropped target, which is correct
|
|
75
|
+
* only when the bytes actually leave. Here they do not, so those bytes keep counting
|
|
76
|
+
* toward the budget and the demotion loop keeps shrinking the images it still can.
|
|
77
|
+
*/
|
|
78
|
+
retainsBytesOnDrop?: boolean;
|
|
71
79
|
}
|
|
72
80
|
|
|
73
81
|
export interface NormalizeTargetsOptions extends NormalizeOptions {
|
|
@@ -127,11 +135,17 @@ export async function normalizeImageTargets(targets: NormalizeTarget[], options:
|
|
|
127
135
|
if (newestFirstIndex >= processLimit) continue;
|
|
128
136
|
if (b64.length > MAX_INPUT_BASE64_LENGTH) {
|
|
129
137
|
target.drop(BOMB_TEXT);
|
|
138
|
+
if (target.retainsBytesOnDrop) {
|
|
139
|
+
entries[i] = { target, sourceB64: b64, sourceMedia: target.mediaType.toLowerCase(), pos: TERMINAL_POS, size: b64.length, done: true };
|
|
140
|
+
}
|
|
130
141
|
continue;
|
|
131
142
|
}
|
|
132
143
|
const dims = sniffImageDimensions(b64);
|
|
133
144
|
if (dims && dims.width * dims.height > MAX_INPUT_PIXELS) {
|
|
134
145
|
target.drop(BOMB_TEXT);
|
|
146
|
+
if (target.retainsBytesOnDrop) {
|
|
147
|
+
entries[i] = { target, sourceB64: b64, sourceMedia: target.mediaType.toLowerCase(), pos: TERMINAL_POS, size: b64.length, done: true };
|
|
148
|
+
}
|
|
135
149
|
continue;
|
|
136
150
|
}
|
|
137
151
|
const sourceMedia = target.mediaType.toLowerCase();
|
|
@@ -139,6 +153,9 @@ export async function normalizeImageTargets(targets: NormalizeTarget[], options:
|
|
|
139
153
|
const result = await processAt(b64, pos, sourceMedia, encode, validate);
|
|
140
154
|
if (result.kind === "failed") {
|
|
141
155
|
target.drop(UNDECODABLE_TEXT);
|
|
156
|
+
if (target.retainsBytesOnDrop) {
|
|
157
|
+
entries[i] = { target, sourceB64: b64, sourceMedia, pos: TERMINAL_POS, size: b64.length, done: true };
|
|
158
|
+
}
|
|
142
159
|
continue;
|
|
143
160
|
}
|
|
144
161
|
let size = b64.length;
|
|
@@ -178,8 +195,14 @@ export async function normalizeImageTargets(targets: NormalizeTarget[], options:
|
|
|
178
195
|
const result = await processAt(entry.sourceB64, entry.pos + 1, entry.sourceMedia, encode, validate);
|
|
179
196
|
if (result.kind === "failed") {
|
|
180
197
|
entry.target.drop(UNDECODABLE_TEXT);
|
|
181
|
-
|
|
182
|
-
|
|
198
|
+
if (entry.target.retainsBytesOnDrop) {
|
|
199
|
+
// Bytes stay on the wire, so they stay in the total; mark it terminal so the
|
|
200
|
+
// loop moves on to a target it can still shrink instead of retrying this one.
|
|
201
|
+
entry.done = true;
|
|
202
|
+
} else {
|
|
203
|
+
sum -= entry.size;
|
|
204
|
+
entries[entries.indexOf(entry)] = null;
|
|
205
|
+
}
|
|
183
206
|
continue;
|
|
184
207
|
}
|
|
185
208
|
let newSize = entry.size;
|
|
@@ -202,6 +225,11 @@ export async function normalizeImageTargets(targets: NormalizeTarget[], options:
|
|
|
202
225
|
const e = entries[i];
|
|
203
226
|
if (!e) continue;
|
|
204
227
|
e.target.drop(OVERFLOW_DROP_TEXT);
|
|
228
|
+
if (e.target.retainsBytesOnDrop) {
|
|
229
|
+
// The drop left the bytes in place, so they still count and dropping another
|
|
230
|
+
// copy of this target would not help. Move on to one that can actually leave.
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
205
233
|
sum -= e.size;
|
|
206
234
|
entries[i] = null;
|
|
207
235
|
}
|
|
@@ -1320,7 +1320,7 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
|
|
|
1320
1320
|
}];
|
|
1321
1321
|
}
|
|
1322
1322
|
const json = parsed;
|
|
1323
|
-
const responseBytes =
|
|
1323
|
+
const responseBytes = Buffer.byteLength(JSON.stringify(json), "utf8");
|
|
1324
1324
|
budget.chargeRetained(responseBytes, { kind: "retained_collectors" });
|
|
1325
1325
|
try {
|
|
1326
1326
|
const events: AdapterEvent[] = [];
|