token-runtime 0.1.0a2__py3-none-any.whl
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.
- token_runtime/__init__.py +1 -0
- token_runtime/adapters.py +181 -0
- token_runtime/agent_integrations.py +114 -0
- token_runtime/anthropic_adapter.py +301 -0
- token_runtime/anthropic_conformance.py +336 -0
- token_runtime/anthropic_preserved_thinking_cert.py +213 -0
- token_runtime/benchmark.py +106 -0
- token_runtime/capabilities.py +111 -0
- token_runtime/cli.py +284 -0
- token_runtime/codex_recertification.py +115 -0
- token_runtime/compat_cert.py +462 -0
- token_runtime/compatibility.py +81 -0
- token_runtime/config.py +72 -0
- token_runtime/conformance.py +36 -0
- token_runtime/contracts.py +70 -0
- token_runtime/engine.py +136 -0
- token_runtime/feature_flags.py +78 -0
- token_runtime/gateway.py +151 -0
- token_runtime/gemini_adapter.py +260 -0
- token_runtime/gemini_conformance.py +351 -0
- token_runtime/integrations.py +272 -0
- token_runtime/metrics.py +79 -0
- token_runtime/model.py +39 -0
- token_runtime/openai_certification.py +310 -0
- token_runtime/planner.py +96 -0
- token_runtime/reducers.py +179 -0
- token_runtime/store.py +36 -0
- token_runtime/strategies.py +50 -0
- token_runtime/terminal_ui.py +96 -0
- token_runtime-0.1.0a2.dist-info/METADATA +238 -0
- token_runtime-0.1.0a2.dist-info/RECORD +35 -0
- token_runtime-0.1.0a2.dist-info/WHEEL +5 -0
- token_runtime-0.1.0a2.dist-info/entry_points.txt +2 -0
- token_runtime-0.1.0a2.dist-info/licenses/LICENSE +202 -0
- token_runtime-0.1.0a2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable, Mapping
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
from dataclasses import dataclass, replace
|
|
6
|
+
|
|
7
|
+
from .capabilities import (
|
|
8
|
+
CapabilityDetector,
|
|
9
|
+
CapabilityKey,
|
|
10
|
+
CapabilityProfile,
|
|
11
|
+
CapabilityRegistry,
|
|
12
|
+
)
|
|
13
|
+
from .compatibility import (
|
|
14
|
+
CompatibilityRecord,
|
|
15
|
+
CompatibilityRegistry,
|
|
16
|
+
CompatibilityState,
|
|
17
|
+
)
|
|
18
|
+
from .conformance import ConformanceResult, require_conformance, run_conformance
|
|
19
|
+
from .gemini_adapter import GeminiGenerateContentAdapter
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
GEMINI_GENERATE_CONTENT_KEY = CapabilityKey(
|
|
23
|
+
"generic-google",
|
|
24
|
+
"gemini_generate_content",
|
|
25
|
+
"google",
|
|
26
|
+
)
|
|
27
|
+
EVIDENCE_ID = "token-gemini-adapter-1:offline-request-boundary-v1"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True, slots=True)
|
|
31
|
+
class GeminiConformanceBundle:
|
|
32
|
+
profile: CapabilityProfile
|
|
33
|
+
record: CompatibilityRecord
|
|
34
|
+
detector: CapabilityDetector
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def build_gemini_conformance() -> GeminiConformanceBundle:
|
|
38
|
+
profile = CapabilityProfile(
|
|
39
|
+
key=GEMINI_GENERATE_CONTENT_KEY,
|
|
40
|
+
server_cache="cached-content-reference-preserved",
|
|
41
|
+
tool_calls=True,
|
|
42
|
+
reasoning_state="exact-replay-required",
|
|
43
|
+
exact_byte_preservation=True,
|
|
44
|
+
evidence_id=EVIDENCE_ID,
|
|
45
|
+
)
|
|
46
|
+
record = CompatibilityRecord(
|
|
47
|
+
key=GEMINI_GENERATE_CONTENT_KEY,
|
|
48
|
+
state=CompatibilityState.PASSTHROUGH_ONLY,
|
|
49
|
+
evidence_id=EVIDENCE_ID,
|
|
50
|
+
reason="offline_conformance_only",
|
|
51
|
+
)
|
|
52
|
+
capabilities = CapabilityRegistry()
|
|
53
|
+
capabilities.register(profile)
|
|
54
|
+
compatibility = CompatibilityRegistry()
|
|
55
|
+
compatibility.register(record)
|
|
56
|
+
return GeminiConformanceBundle(
|
|
57
|
+
profile=profile,
|
|
58
|
+
record=record,
|
|
59
|
+
detector=CapabilityDetector(capabilities, compatibility),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _supported_fixture() -> dict[str, object]:
|
|
64
|
+
return {
|
|
65
|
+
"contents": [
|
|
66
|
+
{"role": "user", "parts": [{"text": "current task"}]},
|
|
67
|
+
{
|
|
68
|
+
"role": "model",
|
|
69
|
+
"parts": [
|
|
70
|
+
{
|
|
71
|
+
"functionCall": {
|
|
72
|
+
"id": "call_1",
|
|
73
|
+
"name": "lookup",
|
|
74
|
+
"args": {"q": "status"},
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
"systemInstruction": {"parts": [{"text": "stable policy"}]},
|
|
81
|
+
"tools": [
|
|
82
|
+
{
|
|
83
|
+
"functionDeclarations": [
|
|
84
|
+
{
|
|
85
|
+
"name": "lookup",
|
|
86
|
+
"description": "fixture",
|
|
87
|
+
"parameters": {"type": "OBJECT"},
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
],
|
|
92
|
+
"toolConfig": {"functionCallingConfig": {"mode": "AUTO"}},
|
|
93
|
+
"cachedContent": "cachedContents/cache-1",
|
|
94
|
+
"generationConfig": {"temperature": 0.2, "thinkingLevel": "LOW"},
|
|
95
|
+
"futureField": {"preserve": True},
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _supported_roundtrip() -> bool:
|
|
100
|
+
payload = _supported_fixture()
|
|
101
|
+
adapter = GeminiGenerateContentAdapter()
|
|
102
|
+
envelope = adapter.parse(payload)
|
|
103
|
+
return envelope.wire_safe and adapter.serialize(envelope) == payload
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _roleless_text_roundtrip() -> bool:
|
|
107
|
+
payload = {"contents": [{"parts": [{"text": "hello"}]}]}
|
|
108
|
+
adapter = GeminiGenerateContentAdapter()
|
|
109
|
+
envelope = adapter.parse(payload)
|
|
110
|
+
user = next((block for block in envelope.blocks if block.text == "hello"), None)
|
|
111
|
+
return (
|
|
112
|
+
envelope.wire_safe
|
|
113
|
+
and user is not None
|
|
114
|
+
and user.kind == "user"
|
|
115
|
+
and user.role == "user"
|
|
116
|
+
and user.metadata.get("path") == ("contents", 0, "parts", 0, "text")
|
|
117
|
+
and adapter.serialize(envelope) == payload
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _cached_content_reference_preserved() -> bool:
|
|
122
|
+
payload = _supported_fixture()
|
|
123
|
+
adapter = GeminiGenerateContentAdapter()
|
|
124
|
+
envelope = adapter.parse(payload)
|
|
125
|
+
serialized = adapter.serialize(envelope)
|
|
126
|
+
return (
|
|
127
|
+
envelope.wire_safe
|
|
128
|
+
and serialized == payload
|
|
129
|
+
and serialized["cachedContent"] == "cachedContents/cache-1"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _text_path_mutation_preserves_protocol() -> bool:
|
|
134
|
+
payload = _supported_fixture()
|
|
135
|
+
adapter = GeminiGenerateContentAdapter()
|
|
136
|
+
envelope = adapter.parse(payload)
|
|
137
|
+
target = next(block for block in envelope.blocks if block.text == "current task")
|
|
138
|
+
changed = replace(
|
|
139
|
+
envelope,
|
|
140
|
+
blocks=tuple(
|
|
141
|
+
replace(block, text="updated task") if block is target else block
|
|
142
|
+
for block in envelope.blocks
|
|
143
|
+
),
|
|
144
|
+
)
|
|
145
|
+
actual = adapter.serialize(changed)
|
|
146
|
+
expected = deepcopy(payload)
|
|
147
|
+
expected["contents"][0]["parts"][0]["text"] = "updated task"
|
|
148
|
+
return actual == expected
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _system_instruction_text_mutation() -> bool:
|
|
152
|
+
payload = _supported_fixture()
|
|
153
|
+
adapter = GeminiGenerateContentAdapter()
|
|
154
|
+
envelope = adapter.parse(payload)
|
|
155
|
+
target = next(block for block in envelope.blocks if block.kind == "system")
|
|
156
|
+
changed = replace(
|
|
157
|
+
envelope,
|
|
158
|
+
blocks=tuple(
|
|
159
|
+
replace(block, text="updated policy") if block is target else block
|
|
160
|
+
for block in envelope.blocks
|
|
161
|
+
),
|
|
162
|
+
)
|
|
163
|
+
actual = adapter.serialize(changed)
|
|
164
|
+
expected = deepcopy(payload)
|
|
165
|
+
expected["systemInstruction"]["parts"][0]["text"] = "updated policy"
|
|
166
|
+
return actual == expected
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _tool_classification() -> bool:
|
|
170
|
+
call_payload = _supported_fixture()
|
|
171
|
+
call_envelope = GeminiGenerateContentAdapter().parse(call_payload)
|
|
172
|
+
call_by_kind = {block.kind: block for block in call_envelope.blocks}
|
|
173
|
+
call_required = {"tool_schema", "tool_call"}
|
|
174
|
+
|
|
175
|
+
response_payload = {
|
|
176
|
+
"contents": [
|
|
177
|
+
{
|
|
178
|
+
"role": "user",
|
|
179
|
+
"parts": [
|
|
180
|
+
{
|
|
181
|
+
"functionResponse": {
|
|
182
|
+
"id": "call_1",
|
|
183
|
+
"name": "lookup",
|
|
184
|
+
"response": {"status": "green"},
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
],
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
response_envelope = GeminiGenerateContentAdapter().parse(response_payload)
|
|
192
|
+
output = next(
|
|
193
|
+
block for block in response_envelope.blocks if block.kind == "tool_output"
|
|
194
|
+
)
|
|
195
|
+
return (
|
|
196
|
+
call_envelope.wire_safe
|
|
197
|
+
and call_required <= set(call_by_kind)
|
|
198
|
+
and all("path" not in call_by_kind[kind].metadata for kind in call_required)
|
|
199
|
+
and not response_envelope.wire_safe
|
|
200
|
+
and "path" not in output.metadata
|
|
201
|
+
and GeminiGenerateContentAdapter().serialize(response_envelope) == response_payload
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _function_response_exact_passthrough() -> bool:
|
|
206
|
+
payload = {
|
|
207
|
+
"contents": [
|
|
208
|
+
{
|
|
209
|
+
"role": "user",
|
|
210
|
+
"parts": [
|
|
211
|
+
{
|
|
212
|
+
"functionResponse": {
|
|
213
|
+
"name": "lookup",
|
|
214
|
+
"response": {"status": "green"},
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
],
|
|
218
|
+
}
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
adapter = GeminiGenerateContentAdapter()
|
|
222
|
+
envelope = adapter.parse(payload)
|
|
223
|
+
return (
|
|
224
|
+
not envelope.wire_safe
|
|
225
|
+
and any(block.kind == "tool_output" for block in envelope.blocks)
|
|
226
|
+
and adapter.serialize(envelope) == payload
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _thought_signature_exact_passthrough() -> bool:
|
|
231
|
+
payload = {
|
|
232
|
+
"contents": [
|
|
233
|
+
{
|
|
234
|
+
"role": "model",
|
|
235
|
+
"parts": [
|
|
236
|
+
{
|
|
237
|
+
"functionCall": {"name": "lookup", "args": {"q": "x"}},
|
|
238
|
+
"thoughtSignature": "opaque-signature",
|
|
239
|
+
}
|
|
240
|
+
],
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
adapter = GeminiGenerateContentAdapter()
|
|
245
|
+
envelope = adapter.parse(payload)
|
|
246
|
+
return (
|
|
247
|
+
not envelope.wire_safe
|
|
248
|
+
and adapter.serialize(envelope) == payload
|
|
249
|
+
and any(block.kind == "protocol_state" for block in envelope.blocks)
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _unknown_multimodal_passthrough() -> bool:
|
|
254
|
+
payload = {
|
|
255
|
+
"contents": [
|
|
256
|
+
{
|
|
257
|
+
"role": "user",
|
|
258
|
+
"parts": [
|
|
259
|
+
{"text": "describe"},
|
|
260
|
+
{"inlineData": {"mimeType": "image/png", "data": "opaque"}},
|
|
261
|
+
],
|
|
262
|
+
}
|
|
263
|
+
]
|
|
264
|
+
}
|
|
265
|
+
adapter = GeminiGenerateContentAdapter()
|
|
266
|
+
envelope = adapter.parse(payload)
|
|
267
|
+
return not envelope.wire_safe and adapter.serialize(envelope) == payload
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _unsupported_tool_passthrough() -> bool:
|
|
271
|
+
payload = {
|
|
272
|
+
"contents": [{"role": "user", "parts": [{"text": "search"}]}],
|
|
273
|
+
"tools": [{"googleSearch": {}}],
|
|
274
|
+
}
|
|
275
|
+
adapter = GeminiGenerateContentAdapter()
|
|
276
|
+
envelope = adapter.parse(payload)
|
|
277
|
+
return not envelope.wire_safe and adapter.serialize(envelope) == payload
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _malformed_function_passthrough() -> bool:
|
|
281
|
+
payload = {
|
|
282
|
+
"contents": [
|
|
283
|
+
{"role": "model", "parts": [{"functionCall": {"args": {"q": "x"}}}]}
|
|
284
|
+
]
|
|
285
|
+
}
|
|
286
|
+
adapter = GeminiGenerateContentAdapter()
|
|
287
|
+
envelope = adapter.parse(payload)
|
|
288
|
+
return not envelope.wire_safe and adapter.serialize(envelope) == payload
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _streaming_unclaimed() -> bool:
|
|
292
|
+
bundle = build_gemini_conformance()
|
|
293
|
+
return (
|
|
294
|
+
bundle.profile.streaming == "unknown"
|
|
295
|
+
and GeminiGenerateContentAdapter.protocol_id == "gemini_generate_content"
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _certification_state_passthrough_only() -> bool:
|
|
300
|
+
bundle = build_gemini_conformance()
|
|
301
|
+
return (
|
|
302
|
+
bundle.record.state is CompatibilityState.PASSTHROUGH_ONLY
|
|
303
|
+
and bundle.record.reason == "offline_conformance_only"
|
|
304
|
+
and bundle.profile.evidence_id == bundle.record.evidence_id
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _near_match_non_inheritance() -> bool:
|
|
309
|
+
bundle = build_gemini_conformance()
|
|
310
|
+
keys = (
|
|
311
|
+
CapabilityKey(
|
|
312
|
+
"generic-google",
|
|
313
|
+
"gemini_generate_content",
|
|
314
|
+
"google",
|
|
315
|
+
model_family="gemini-future",
|
|
316
|
+
),
|
|
317
|
+
CapabilityKey("generic-google", "gemini_generate_content", "google-v2"),
|
|
318
|
+
CapabilityKey("gemini-cli", "gemini_generate_content", "google"),
|
|
319
|
+
)
|
|
320
|
+
return all(
|
|
321
|
+
bundle.detector.detect(key).profile is None
|
|
322
|
+
and bundle.detector.detect(key).compatibility.state
|
|
323
|
+
is CompatibilityState.PASSTHROUGH_ONLY
|
|
324
|
+
and bundle.detector.detect(key).compatibility.reason == "unknown_capability"
|
|
325
|
+
for key in keys
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def gemini_conformance_cases() -> Mapping[str, Callable[[], bool]]:
|
|
330
|
+
return {
|
|
331
|
+
"cached_content_reference_preserved": _cached_content_reference_preserved,
|
|
332
|
+
"certification_state_passthrough_only": _certification_state_passthrough_only,
|
|
333
|
+
"function_response_exact_passthrough": _function_response_exact_passthrough,
|
|
334
|
+
"malformed_function_passthrough": _malformed_function_passthrough,
|
|
335
|
+
"near_match_non_inheritance": _near_match_non_inheritance,
|
|
336
|
+
"roleless_text_roundtrip": _roleless_text_roundtrip,
|
|
337
|
+
"streaming_unclaimed": _streaming_unclaimed,
|
|
338
|
+
"supported_roundtrip": _supported_roundtrip,
|
|
339
|
+
"system_instruction_text_mutation": _system_instruction_text_mutation,
|
|
340
|
+
"text_path_mutation_preserves_protocol": _text_path_mutation_preserves_protocol,
|
|
341
|
+
"thought_signature_exact_passthrough": _thought_signature_exact_passthrough,
|
|
342
|
+
"tool_classification": _tool_classification,
|
|
343
|
+
"unknown_multimodal_passthrough": _unknown_multimodal_passthrough,
|
|
344
|
+
"unsupported_tool_passthrough": _unsupported_tool_passthrough,
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def run_gemini_conformance() -> tuple[ConformanceResult, ...]:
|
|
349
|
+
results = run_conformance(gemini_conformance_cases())
|
|
350
|
+
require_conformance(results)
|
|
351
|
+
return results
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
import hashlib
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import re
|
|
9
|
+
from typing import Literal
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
_BEGIN = b"# >>> TOKEN MANAGED >>>"
|
|
13
|
+
_END = b"# <<< TOKEN MANAGED <<<"
|
|
14
|
+
_BASE_RE = re.compile(br"(?m)^OPENAI_BASE_URL\s*=.*$")
|
|
15
|
+
_MANAGED_RE = re.compile(
|
|
16
|
+
re.escape(_BEGIN) + br"\r?\n.*?\r?\n" + re.escape(_END) + br"(?:\r?\n)?",
|
|
17
|
+
re.DOTALL,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True, slots=True)
|
|
22
|
+
class Integration:
|
|
23
|
+
kind: Literal["codex", "openai_env"]
|
|
24
|
+
path: Path
|
|
25
|
+
root: Path
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True, slots=True)
|
|
29
|
+
class InstallPlan:
|
|
30
|
+
integration: Integration
|
|
31
|
+
mode: Literal["sidecar", "managed_block", "managed_toml"]
|
|
32
|
+
target_path: Path
|
|
33
|
+
gateway_url: str
|
|
34
|
+
original_bytes: bytes | None
|
|
35
|
+
desired_bytes: bytes
|
|
36
|
+
safe_to_apply: bool = True
|
|
37
|
+
reason: str = "ready"
|
|
38
|
+
metadata_path: Path | None = None
|
|
39
|
+
metadata_bytes: bytes | None = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass(frozen=True, slots=True)
|
|
43
|
+
class ApplyResult:
|
|
44
|
+
changed: bool
|
|
45
|
+
reason: str
|
|
46
|
+
target_path: Path
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def detect_integrations(root: str | Path) -> list[Integration]:
|
|
50
|
+
base = Path(root)
|
|
51
|
+
found: list[Integration] = []
|
|
52
|
+
codex = base / ".codex" / "config.toml"
|
|
53
|
+
if codex.is_file():
|
|
54
|
+
found.append(Integration("codex", codex, base))
|
|
55
|
+
env = base / ".env"
|
|
56
|
+
if env.is_file():
|
|
57
|
+
content = env.read_bytes()
|
|
58
|
+
if b"OPENAI_" in content or _BEGIN in content:
|
|
59
|
+
found.append(Integration("openai_env", env, base))
|
|
60
|
+
return found
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _managed_block(gateway_url: str) -> bytes:
|
|
64
|
+
return (
|
|
65
|
+
_BEGIN + b"\n" +
|
|
66
|
+
f"OPENAI_BASE_URL={gateway_url}\n".encode("utf-8") +
|
|
67
|
+
_END + b"\n"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _replace_or_append_block(original: bytes, gateway_url: str) -> bytes:
|
|
72
|
+
block = _managed_block(gateway_url)
|
|
73
|
+
if _MANAGED_RE.search(original):
|
|
74
|
+
return _MANAGED_RE.sub(block, original, count=1)
|
|
75
|
+
separator = b"" if not original or original.endswith((b"\n", b"\r")) else b"\n"
|
|
76
|
+
return original + separator + block
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _codex_provider_sections(content: bytes):
|
|
80
|
+
return list(re.finditer(br"(?m)^\[model_providers\.([A-Za-z0-9_-]+)\][ \t]*(?:\r?\n|$)", content))
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _replace_codex_base_url(content: bytes, provider_id: str, new_url: str):
|
|
84
|
+
sections = _codex_provider_sections(content)
|
|
85
|
+
match = next((m for m in sections if m.group(1).decode() == provider_id), None)
|
|
86
|
+
if match is None:
|
|
87
|
+
return None
|
|
88
|
+
next_headers = [m.start() for m in re.finditer(br"(?m)^\[[^\r\n]+\][ \t]*(?:\r?\n|$)", content) if m.start() > match.start()]
|
|
89
|
+
end = min(next_headers) if next_headers else len(content)
|
|
90
|
+
section = content[match.end():end]
|
|
91
|
+
base = re.search(br'(?m)^[ \t]*base_url[ \t]*=[ \t]*"([^"\r\n]+)"', section)
|
|
92
|
+
if base is None:
|
|
93
|
+
return None
|
|
94
|
+
start = match.end() + base.start(1)
|
|
95
|
+
stop = match.end() + base.end(1)
|
|
96
|
+
old = content[start:stop].decode("utf-8")
|
|
97
|
+
desired = content[:start] + new_url.encode("utf-8") + content[stop:]
|
|
98
|
+
return old, desired
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _plan_codex(integration: Integration, gateway_url: str) -> InstallPlan:
|
|
102
|
+
original = integration.path.read_bytes()
|
|
103
|
+
target = integration.path
|
|
104
|
+
metadata_path = integration.root / ".token" / "integrations" / "codex.json"
|
|
105
|
+
if metadata_path.exists():
|
|
106
|
+
try:
|
|
107
|
+
metadata = json.loads(metadata_path.read_text(encoding="utf-8"))
|
|
108
|
+
except (OSError, ValueError, TypeError):
|
|
109
|
+
return InstallPlan(integration, "managed_toml", target, gateway_url, original, original, False, "invalid_codex_metadata", metadata_path)
|
|
110
|
+
current_hash = hashlib.sha256(original).hexdigest()
|
|
111
|
+
if metadata.get("strategy") == "managed_provider_base_url" and current_hash == metadata.get("desired_sha256"):
|
|
112
|
+
return InstallPlan(integration, "managed_toml", target, gateway_url, original, original, True, "already_managed", metadata_path, metadata_path.read_bytes())
|
|
113
|
+
return InstallPlan(integration, "managed_toml", target, gateway_url, original, original, False, "target_drift", metadata_path)
|
|
114
|
+
sections = _codex_provider_sections(original)
|
|
115
|
+
if len(sections) != 1:
|
|
116
|
+
reason = "ambiguous_codex_provider" if len(sections) > 1 else "missing_codex_provider"
|
|
117
|
+
return InstallPlan(integration, "managed_toml", target, gateway_url, original, original, False, reason, metadata_path)
|
|
118
|
+
provider_id = sections[0].group(1).decode("utf-8")
|
|
119
|
+
replaced = _replace_codex_base_url(original, provider_id, gateway_url)
|
|
120
|
+
if replaced is None:
|
|
121
|
+
return InstallPlan(integration, "managed_toml", target, gateway_url, original, original, False, "missing_codex_base_url", metadata_path)
|
|
122
|
+
old_url, desired = replaced
|
|
123
|
+
metadata = {
|
|
124
|
+
"kind": "codex", "strategy": "managed_provider_base_url",
|
|
125
|
+
"provider_id": provider_id, "gateway_url": gateway_url,
|
|
126
|
+
"original_base_url": old_url,
|
|
127
|
+
"original_sha256": hashlib.sha256(original).hexdigest(),
|
|
128
|
+
"desired_sha256": hashlib.sha256(desired).hexdigest(),
|
|
129
|
+
}
|
|
130
|
+
metadata_bytes = (json.dumps(metadata, indent=2, sort_keys=True) + "\n").encode("utf-8")
|
|
131
|
+
return InstallPlan(integration, "managed_toml", target, gateway_url, original, desired, True, "ready", metadata_path, metadata_bytes)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def plan_install(integration: Integration, *, gateway_url: str) -> InstallPlan:
|
|
135
|
+
if integration.kind == "codex":
|
|
136
|
+
return _plan_codex(integration, gateway_url)
|
|
137
|
+
|
|
138
|
+
original = integration.path.read_bytes()
|
|
139
|
+
unmanaged = _BASE_RE.search(_MANAGED_RE.sub(b"", original))
|
|
140
|
+
safe = unmanaged is None
|
|
141
|
+
desired = _replace_or_append_block(original, gateway_url) if safe else original
|
|
142
|
+
metadata_path = integration.root / ".token" / "integrations" / "openai_env.json"
|
|
143
|
+
if metadata_path.exists():
|
|
144
|
+
metadata_bytes = metadata_path.read_bytes()
|
|
145
|
+
else:
|
|
146
|
+
separator_added = bool(original and not original.endswith((b"\n", b"\r")))
|
|
147
|
+
metadata_bytes = (json.dumps({
|
|
148
|
+
"kind": "openai_env",
|
|
149
|
+
"original_sha256": hashlib.sha256(original).hexdigest(),
|
|
150
|
+
"separator_added": separator_added,
|
|
151
|
+
}, indent=2, sort_keys=True) + "\n").encode("utf-8")
|
|
152
|
+
return InstallPlan(
|
|
153
|
+
integration, "managed_block", integration.path, gateway_url,
|
|
154
|
+
original, desired, safe_to_apply=safe,
|
|
155
|
+
reason="ready" if safe else "existing_openai_base_url_unmanaged",
|
|
156
|
+
metadata_path=metadata_path, metadata_bytes=metadata_bytes,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _atomic_write(path: Path, content: bytes) -> None:
|
|
161
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
162
|
+
tmp = path.with_name(path.name + ".token-tmp")
|
|
163
|
+
tmp.write_bytes(content)
|
|
164
|
+
os.chmod(tmp, 0o600)
|
|
165
|
+
os.replace(tmp, path)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def apply_plan(plan: InstallPlan, *, apply: bool = False) -> ApplyResult:
|
|
169
|
+
current = plan.target_path.read_bytes() if plan.target_path.exists() else None
|
|
170
|
+
changed = current != plan.desired_bytes
|
|
171
|
+
if not plan.safe_to_apply:
|
|
172
|
+
return ApplyResult(False, plan.reason, plan.target_path)
|
|
173
|
+
if not apply or not changed:
|
|
174
|
+
return ApplyResult(changed, "dry_run" if not apply else "unchanged", plan.target_path)
|
|
175
|
+
if current not in (plan.original_bytes, plan.desired_bytes):
|
|
176
|
+
return ApplyResult(False, "target_drift", plan.target_path)
|
|
177
|
+
try:
|
|
178
|
+
_atomic_write(plan.target_path, plan.desired_bytes)
|
|
179
|
+
if plan.metadata_path is not None and plan.metadata_bytes is not None:
|
|
180
|
+
_atomic_write(plan.metadata_path, plan.metadata_bytes)
|
|
181
|
+
except Exception:
|
|
182
|
+
if plan.original_bytes is None:
|
|
183
|
+
plan.target_path.unlink(missing_ok=True)
|
|
184
|
+
else:
|
|
185
|
+
_atomic_write(plan.target_path, plan.original_bytes)
|
|
186
|
+
if plan.metadata_path is not None:
|
|
187
|
+
plan.metadata_path.unlink(missing_ok=True)
|
|
188
|
+
raise
|
|
189
|
+
return ApplyResult(True, "applied", plan.target_path)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def restore_plan(plan: InstallPlan, *, apply: bool = False) -> ApplyResult:
|
|
193
|
+
current = plan.target_path.read_bytes() if plan.target_path.exists() else None
|
|
194
|
+
desired = plan.original_bytes
|
|
195
|
+
metadata_exists = bool(plan.metadata_path and plan.metadata_path.exists())
|
|
196
|
+
changed = current != desired or metadata_exists
|
|
197
|
+
if not apply or not changed:
|
|
198
|
+
return ApplyResult(changed, "dry_run" if not apply else "unchanged", plan.target_path)
|
|
199
|
+
if current not in (plan.original_bytes, plan.desired_bytes):
|
|
200
|
+
return ApplyResult(False, "target_drift", plan.target_path)
|
|
201
|
+
if current != desired:
|
|
202
|
+
if desired is None:
|
|
203
|
+
plan.target_path.unlink(missing_ok=True)
|
|
204
|
+
else:
|
|
205
|
+
_atomic_write(plan.target_path, desired)
|
|
206
|
+
if plan.metadata_path is not None:
|
|
207
|
+
plan.metadata_path.unlink(missing_ok=True)
|
|
208
|
+
return ApplyResult(True, "restored", plan.target_path)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def uninstall_integration(integration: Integration, *, apply: bool = False) -> ApplyResult:
|
|
212
|
+
if integration.kind == "codex":
|
|
213
|
+
metadata_path = integration.root / ".token" / "integrations" / "codex.json"
|
|
214
|
+
if not metadata_path.exists():
|
|
215
|
+
return ApplyResult(False, "unchanged" if apply else "dry_run", integration.path)
|
|
216
|
+
if not apply:
|
|
217
|
+
return ApplyResult(True, "dry_run", integration.path)
|
|
218
|
+
try:
|
|
219
|
+
metadata = json.loads(metadata_path.read_text(encoding="utf-8"))
|
|
220
|
+
except (OSError, ValueError, TypeError):
|
|
221
|
+
return ApplyResult(False, "invalid_codex_metadata", integration.path)
|
|
222
|
+
if metadata.get("strategy") == "sidecar":
|
|
223
|
+
metadata_path.unlink()
|
|
224
|
+
return ApplyResult(True, "uninstalled", integration.path)
|
|
225
|
+
if metadata.get("strategy") != "managed_provider_base_url":
|
|
226
|
+
return ApplyResult(False, "invalid_codex_metadata", integration.path)
|
|
227
|
+
current = integration.path.read_bytes()
|
|
228
|
+
current_hash = hashlib.sha256(current).hexdigest()
|
|
229
|
+
if current_hash == metadata.get("original_sha256"):
|
|
230
|
+
metadata_path.unlink()
|
|
231
|
+
return ApplyResult(True, "metadata_removed", integration.path)
|
|
232
|
+
if current_hash != metadata.get("desired_sha256"):
|
|
233
|
+
return ApplyResult(False, "target_drift", integration.path)
|
|
234
|
+
restored = _replace_codex_base_url(current, str(metadata.get("provider_id")), str(metadata.get("original_base_url")))
|
|
235
|
+
if restored is None:
|
|
236
|
+
return ApplyResult(False, "restore_failed", integration.path)
|
|
237
|
+
_, desired = restored
|
|
238
|
+
if hashlib.sha256(desired).hexdigest() != metadata.get("original_sha256"):
|
|
239
|
+
return ApplyResult(False, "restore_hash_mismatch", integration.path)
|
|
240
|
+
_atomic_write(integration.path, desired)
|
|
241
|
+
metadata_path.unlink()
|
|
242
|
+
return ApplyResult(True, "uninstalled", integration.path)
|
|
243
|
+
|
|
244
|
+
target = integration.path
|
|
245
|
+
current = target.read_bytes()
|
|
246
|
+
match = _MANAGED_RE.search(current)
|
|
247
|
+
metadata_path = integration.root / ".token" / "integrations" / "openai_env.json"
|
|
248
|
+
metadata_exists = metadata_path.exists()
|
|
249
|
+
if match is None:
|
|
250
|
+
if apply and metadata_exists:
|
|
251
|
+
metadata_path.unlink()
|
|
252
|
+
changed = metadata_exists
|
|
253
|
+
return ApplyResult(changed, "metadata_removed" if apply and changed else ("dry_run" if not apply else "unchanged"), target)
|
|
254
|
+
|
|
255
|
+
separator_added = False
|
|
256
|
+
if metadata_exists:
|
|
257
|
+
try:
|
|
258
|
+
metadata = json.loads(metadata_path.read_text(encoding="utf-8"))
|
|
259
|
+
separator_added = bool(metadata.get("separator_added", False))
|
|
260
|
+
except (OSError, ValueError, TypeError):
|
|
261
|
+
separator_added = False
|
|
262
|
+
start = match.start()
|
|
263
|
+
end = match.end()
|
|
264
|
+
desired = current[:start] + current[end:]
|
|
265
|
+
if separator_added and start > 0 and current[start - 1:start] == b"\n":
|
|
266
|
+
desired = current[:start - 1] + current[end:]
|
|
267
|
+
|
|
268
|
+
if not apply:
|
|
269
|
+
return ApplyResult(True, "dry_run", target)
|
|
270
|
+
_atomic_write(target, desired)
|
|
271
|
+
metadata_path.unlink(missing_ok=True)
|
|
272
|
+
return ApplyResult(True, "uninstalled", target)
|