codex-flow 2.1.13__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.
- codex_flow/__init__.py +28 -0
- codex_flow/__main__.py +9 -0
- codex_flow/cli.py +242 -0
- codex_flow/data/LICENSE +21 -0
- codex_flow/data/README.en.md +303 -0
- codex_flow/data/README.md +305 -0
- codex_flow/data/VERSION +1 -0
- codex_flow/data/apps/chatgpt-mcp/README.md +86 -0
- codex_flow/data/apps/chatgpt-mcp/__init__.py +1 -0
- codex_flow/data/apps/chatgpt-mcp/adapter.py +458 -0
- codex_flow/data/apps/chatgpt-mcp/server.py +358 -0
- codex_flow/data/apps/chatgpt-mcp/widget.html +927 -0
- codex_flow/data/apps/macos-overlay/README.en.md +121 -0
- codex_flow/data/apps/macos-overlay/README.md +123 -0
- codex_flow/data/apps/macos-overlay/Sources/Controllers/OverlayRuntimeState.swift +126 -0
- codex_flow/data/apps/macos-overlay/Sources/Controllers/OverlayScreenGeometry.swift +82 -0
- codex_flow/data/apps/macos-overlay/Sources/Controllers/OverlayWindowController.swift +1052 -0
- codex_flow/data/apps/macos-overlay/Sources/Localization.swift +197 -0
- codex_flow/data/apps/macos-overlay/Sources/Models/TelemetryData.swift +1557 -0
- codex_flow/data/apps/macos-overlay/Sources/Services/AccountSnapshotService.swift +1101 -0
- codex_flow/data/apps/macos-overlay/Sources/Services/FlowPilotInstanceLock.swift +153 -0
- codex_flow/data/apps/macos-overlay/Sources/Services/IPCServer.swift +298 -0
- codex_flow/data/apps/macos-overlay/Sources/Services/TelemetryQueryEngine.swift +800 -0
- codex_flow/data/apps/macos-overlay/Sources/Services/TelemetryWatcher.swift +135 -0
- codex_flow/data/apps/macos-overlay/Sources/Services/UpdateService.swift +610 -0
- codex_flow/data/apps/macos-overlay/Sources/Views/AccountView.swift +610 -0
- codex_flow/data/apps/macos-overlay/Sources/Views/AnalyticsView.swift +566 -0
- codex_flow/data/apps/macos-overlay/Sources/Views/AutostartView.swift +293 -0
- codex_flow/data/apps/macos-overlay/Sources/Views/BubbleView.swift +317 -0
- codex_flow/data/apps/macos-overlay/Sources/Views/HistoryView.swift +1124 -0
- codex_flow/data/apps/macos-overlay/Sources/Views/HoverRevealText.swift +165 -0
- codex_flow/data/apps/macos-overlay/Sources/Views/InspectorSkillsToolsView.swift +121 -0
- codex_flow/data/apps/macos-overlay/Sources/Views/LogoView.swift +182 -0
- codex_flow/data/apps/macos-overlay/Sources/Views/SleekSwitch.swift +117 -0
- codex_flow/data/apps/macos-overlay/Sources/Views/StrategyModeView.swift +561 -0
- codex_flow/data/apps/macos-overlay/Sources/Views/SummaryView.swift +1273 -0
- codex_flow/data/apps/macos-overlay/Sources/Views/UpdateView.swift +352 -0
- codex_flow/data/apps/macos-overlay/Sources/main.swift +340 -0
- codex_flow/data/apps/macos-overlay/Tests/OverlayScreenGeometryTests.swift +163 -0
- codex_flow/data/apps/macos-overlay/Tests/TelemetryPhase1ContractTests.swift +357 -0
- codex_flow/data/apps/macos-overlay/Tests/TelemetryQueryEngineConcurrencyTests.swift +221 -0
- codex_flow/data/apps/macos-overlay/Tests/TelemetryQuotaSelectionTests.swift +158 -0
- codex_flow/data/apps/macos-overlay/Tests/TelemetryWorkerTokenTests.swift +122 -0
- codex_flow/data/apps/macos-overlay/build.sh +75 -0
- codex_flow/data/benchmark/corpus.json +103 -0
- codex_flow/data/benchmark/manifest.example.json +41 -0
- codex_flow/data/benchmark/manifest.schema.json +137 -0
- codex_flow/data/benchmark/prices/gpt-5.6-2026-08-30.json +5 -0
- codex_flow/data/benchmark/profiles.json +90 -0
- codex_flow/data/benchmark/schema.json +77 -0
- codex_flow/data/benchmark/tasks.json +50 -0
- codex_flow/data/completions/codex-flow.bash +34 -0
- codex_flow/data/completions/codex-flow.zsh +52 -0
- codex_flow/data/glama.json +6 -0
- codex_flow/data/install-release.ps1 +126 -0
- codex_flow/data/install-release.sh +155 -0
- codex_flow/data/install.ps1 +349 -0
- codex_flow/data/install.sh +362 -0
- codex_flow/data/policy/benchmark.toml +49 -0
- codex_flow/data/policy/defaults.toml +70 -0
- codex_flow/data/scripts/analyze-benchmark.py +510 -0
- codex_flow/data/scripts/benchmark-local.py +171 -0
- codex_flow/data/scripts/check-recommendation.py +277 -0
- codex_flow/data/scripts/doctor.py +449 -0
- codex_flow/data/scripts/generate-release-manifest.py +74 -0
- codex_flow/data/scripts/localization.py +192 -0
- codex_flow/data/scripts/manage-hooks.py +448 -0
- codex_flow/data/scripts/manage-instructions.py +389 -0
- codex_flow/data/scripts/manage-shell.py +151 -0
- codex_flow/data/scripts/materialize-corpus.py +193 -0
- codex_flow/data/scripts/menu.py +646 -0
- codex_flow/data/scripts/migrations/0001_update_settings.py +80 -0
- codex_flow/data/scripts/package-release.py +132 -0
- codex_flow/data/scripts/render-benchmark-report.py +292 -0
- codex_flow/data/scripts/run-benchmark.py +829 -0
- codex_flow/data/scripts/strategies/__init__.py +28 -0
- codex_flow/data/scripts/strategies/balanced.py +115 -0
- codex_flow/data/scripts/strategies/base.py +363 -0
- codex_flow/data/scripts/strategies/efficient.py +158 -0
- codex_flow/data/scripts/strategies/lifecycle_runtime.py +590 -0
- codex_flow/data/scripts/strategies/quality.py +209 -0
- codex_flow/data/scripts/strategies/speed.py +108 -0
- codex_flow/data/scripts/strategies/task_budget_runtime.py +644 -0
- codex_flow/data/scripts/strategies/task_phase_runtime.py +341 -0
- codex_flow/data/scripts/strategies/work_unit_runtime.py +421 -0
- codex_flow/data/scripts/strategy_runtime.py +1091 -0
- codex_flow/data/scripts/telemetry.py +400 -0
- codex_flow/data/scripts/telemetry_core/__init__.py +192 -0
- codex_flow/data/scripts/telemetry_core/app_server.py +1192 -0
- codex_flow/data/scripts/telemetry_core/collector.py +1247 -0
- codex_flow/data/scripts/telemetry_core/common.py +421 -0
- codex_flow/data/scripts/telemetry_core/latency.py +593 -0
- codex_flow/data/scripts/telemetry_core/query.py +427 -0
- codex_flow/data/scripts/telemetry_core/quota_ledger.py +598 -0
- codex_flow/data/scripts/telemetry_core/render.py +460 -0
- codex_flow/data/scripts/telemetry_core/repair.py +223 -0
- codex_flow/data/scripts/ui.py +266 -0
- codex_flow/data/scripts/update-homebrew-formula.py +146 -0
- codex_flow/data/scripts/update_runtime_config.py +134 -0
- codex_flow/data/scripts/updater.py +1718 -0
- codex_flow/data/smithery.yaml +18 -0
- codex_flow/data/templates/agents/worker-explorer.toml +24 -0
- codex_flow/data/templates/agents/worker-implementer.toml +49 -0
- codex_flow/data/templates/agents/worker-reviewer.toml +25 -0
- codex_flow/data/templates/flow-pilot-instructions.md +35 -0
- codex_flow/data/templates/skills/flow-pilot/SKILL.md +577 -0
- codex_flow/mcp.py +35 -0
- codex_flow-2.1.13.dist-info/METADATA +342 -0
- codex_flow-2.1.13.dist-info/RECORD +113 -0
- codex_flow-2.1.13.dist-info/WHEEL +5 -0
- codex_flow-2.1.13.dist-info/entry_points.txt +3 -0
- codex_flow-2.1.13.dist-info/licenses/LICENSE +21 -0
- codex_flow-2.1.13.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Check OpenAI official model docs and update codex-flow recommendations.
|
|
3
|
+
|
|
4
|
+
Fail-closed by design: if the official pages cannot be parsed or required
|
|
5
|
+
capabilities cannot be verified, no policy file is modified.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import html
|
|
11
|
+
import json
|
|
12
|
+
import re
|
|
13
|
+
import ssl
|
|
14
|
+
import sys
|
|
15
|
+
import urllib.error
|
|
16
|
+
import urllib.request
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
MODELS_URL = "https://developers.openai.com/api/docs/models"
|
|
21
|
+
MODEL_URL = "https://developers.openai.com/api/docs/models/{model}"
|
|
22
|
+
MODEL_RE = re.compile(r"gpt-(\d+)\.(\d+)-(sol|terra|luna)", re.I)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class ModelInfo:
|
|
27
|
+
model: str
|
|
28
|
+
major: int
|
|
29
|
+
minor: int
|
|
30
|
+
tier: str
|
|
31
|
+
input_price: float
|
|
32
|
+
cached_input_price: float
|
|
33
|
+
output_price: float
|
|
34
|
+
supports_high: bool
|
|
35
|
+
supports_xhigh: bool
|
|
36
|
+
supports_max: bool
|
|
37
|
+
supports_agent_tools: bool
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def family(self) -> tuple[int, int]:
|
|
41
|
+
return self.major, self.minor
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def worker_cost_score(self) -> float:
|
|
45
|
+
# Implementation loops typically combine large input context with output.
|
|
46
|
+
# Keep the score simple and transparent; this is a ranking heuristic, not
|
|
47
|
+
# an estimate of a user's bill.
|
|
48
|
+
return 0.7 * self.input_price + 0.3 * self.output_price
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _ssl_context() -> ssl.SSLContext:
|
|
52
|
+
try:
|
|
53
|
+
context = ssl.create_default_context()
|
|
54
|
+
except Exception:
|
|
55
|
+
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
|
56
|
+
context.check_hostname = True
|
|
57
|
+
context.verify_mode = ssl.CERT_REQUIRED
|
|
58
|
+
|
|
59
|
+
paths = ssl.get_default_verify_paths()
|
|
60
|
+
ca_missing = bool(paths.openssl_cafile and not os.path.exists(paths.openssl_cafile))
|
|
61
|
+
no_ca_configured = not bool(paths.cafile or paths.capath)
|
|
62
|
+
|
|
63
|
+
if ca_missing or no_ca_configured:
|
|
64
|
+
loaded = False
|
|
65
|
+
try:
|
|
66
|
+
import certifi
|
|
67
|
+
|
|
68
|
+
ca = certifi.where()
|
|
69
|
+
if os.path.exists(ca):
|
|
70
|
+
context.load_verify_locations(cafile=ca)
|
|
71
|
+
loaded = True
|
|
72
|
+
except Exception:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
if not loaded:
|
|
76
|
+
common_bundle_paths = (
|
|
77
|
+
"/etc/ssl/cert.pem",
|
|
78
|
+
"/etc/pki/tls/certs/ca-bundle.crt",
|
|
79
|
+
"/etc/ssl/certs/ca-certificates.crt",
|
|
80
|
+
"/etc/ssl/ca-bundle.pem",
|
|
81
|
+
"/usr/local/share/certs/ca-root-nss.crt",
|
|
82
|
+
)
|
|
83
|
+
for path in common_bundle_paths:
|
|
84
|
+
if os.path.exists(path):
|
|
85
|
+
try:
|
|
86
|
+
context.load_verify_locations(cafile=path)
|
|
87
|
+
break
|
|
88
|
+
except Exception:
|
|
89
|
+
pass
|
|
90
|
+
return context
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def fetch(url: str) -> str:
|
|
94
|
+
req = urllib.request.Request(
|
|
95
|
+
url,
|
|
96
|
+
headers={"User-Agent": "codex-flow-recommendation-bot/0.3"},
|
|
97
|
+
)
|
|
98
|
+
with urllib.request.urlopen(req, timeout=30, context=_ssl_context()) as response:
|
|
99
|
+
charset = response.headers.get_content_charset() or "utf-8"
|
|
100
|
+
return response.read().decode(charset, errors="replace")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def visible_text(raw: str) -> str:
|
|
104
|
+
raw = re.sub(r"(?is)<(script|style)\b.*?</\1>", " ", raw)
|
|
105
|
+
raw = re.sub(r"(?s)<[^>]+>", " ", raw)
|
|
106
|
+
return re.sub(r"\s+", " ", html.unescape(raw)).strip()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def discover_models(index_raw: str) -> list[str]:
|
|
110
|
+
found: dict[str, tuple[int, int, str]] = {}
|
|
111
|
+
for match in MODEL_RE.finditer(index_raw):
|
|
112
|
+
major, minor, tier = int(match.group(1)), int(match.group(2)), match.group(3).lower()
|
|
113
|
+
model = f"gpt-{major}.{minor}-{tier}"
|
|
114
|
+
found[model] = (major, minor, tier)
|
|
115
|
+
return sorted(found)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def parse_model(model: str, raw: str) -> ModelInfo:
|
|
119
|
+
m = MODEL_RE.fullmatch(model)
|
|
120
|
+
if not m:
|
|
121
|
+
raise ValueError(f"unsupported model id: {model}")
|
|
122
|
+
major, minor, tier = int(m.group(1)), int(m.group(2)), m.group(3).lower()
|
|
123
|
+
text = visible_text(raw)
|
|
124
|
+
|
|
125
|
+
effort_match = re.search(
|
|
126
|
+
r"Reasoning(?:\.effort)?\s+supports:?\s*(.{0,180})",
|
|
127
|
+
text,
|
|
128
|
+
re.I,
|
|
129
|
+
)
|
|
130
|
+
effort_tokens = set(
|
|
131
|
+
re.findall(r"\b(?:none|low|medium|high|xhigh|max)\b", effort_match.group(1).lower())
|
|
132
|
+
if effort_match
|
|
133
|
+
else []
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
price_match = re.search(
|
|
137
|
+
r"Text tokens.*?Input\s*\$([0-9]+(?:\.[0-9]+)?)"
|
|
138
|
+
r".*?Cached input\s*\$([0-9]+(?:\.[0-9]+)?)"
|
|
139
|
+
r".*?Output\s*\$([0-9]+(?:\.[0-9]+)?)",
|
|
140
|
+
text,
|
|
141
|
+
re.I,
|
|
142
|
+
)
|
|
143
|
+
if not price_match:
|
|
144
|
+
raise ValueError(f"could not parse token pricing for {model}")
|
|
145
|
+
|
|
146
|
+
supports_agent_tools = bool(
|
|
147
|
+
re.search(r"\b(?:Apply patch|Hosted shell|Skills)\b", text, re.I)
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
return ModelInfo(
|
|
151
|
+
model=model,
|
|
152
|
+
major=major,
|
|
153
|
+
minor=minor,
|
|
154
|
+
tier=tier,
|
|
155
|
+
input_price=float(price_match.group(1)),
|
|
156
|
+
cached_input_price=float(price_match.group(2)),
|
|
157
|
+
output_price=float(price_match.group(3)),
|
|
158
|
+
supports_high="high" in effort_tokens,
|
|
159
|
+
supports_xhigh="xhigh" in effort_tokens,
|
|
160
|
+
supports_max="max" in effort_tokens,
|
|
161
|
+
supports_agent_tools=supports_agent_tools,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def choose(models: list[ModelInfo]) -> tuple[ModelInfo, ModelInfo]:
|
|
166
|
+
qualifying = [
|
|
167
|
+
m
|
|
168
|
+
for m in models
|
|
169
|
+
if m.supports_high and m.supports_xhigh and m.supports_max and m.supports_agent_tools
|
|
170
|
+
]
|
|
171
|
+
if not qualifying:
|
|
172
|
+
raise ValueError("no model satisfies the reasoning/tool capability floor")
|
|
173
|
+
|
|
174
|
+
families = sorted({m.family for m in qualifying}, reverse=True)
|
|
175
|
+
for family in families:
|
|
176
|
+
same_family = [m for m in qualifying if m.family == family]
|
|
177
|
+
parents = [m for m in same_family if m.tier == "sol"]
|
|
178
|
+
workers = [m for m in same_family if m.tier in {"luna", "terra"}]
|
|
179
|
+
if parents and workers:
|
|
180
|
+
parent = parents[0]
|
|
181
|
+
worker = min(workers, key=lambda m: (m.worker_cost_score, m.output_price, m.input_price))
|
|
182
|
+
return parent, worker
|
|
183
|
+
raise ValueError("latest qualifying family lacks both flagship and efficient worker tiers")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def replace_toml_value(text: str, section: str, key: str, value: str) -> str:
|
|
187
|
+
pattern = re.compile(
|
|
188
|
+
rf"(?ms)(^\[{re.escape(section)}\]\s*$)(.*?)(?=^\[[^\n]+\]\s*$|\Z)"
|
|
189
|
+
)
|
|
190
|
+
match = pattern.search(text)
|
|
191
|
+
if not match:
|
|
192
|
+
raise ValueError(f"missing [{section}] section")
|
|
193
|
+
body = match.group(2)
|
|
194
|
+
line_re = re.compile(rf"(?m)^\s*{re.escape(key)}\s*=.*$")
|
|
195
|
+
new_line = f'{key} = "{value}"'
|
|
196
|
+
if line_re.search(body):
|
|
197
|
+
body = line_re.sub(new_line, body)
|
|
198
|
+
else:
|
|
199
|
+
if body and not body.endswith("\n"):
|
|
200
|
+
body += "\n"
|
|
201
|
+
body += new_line + "\n"
|
|
202
|
+
return text[: match.start(2)] + body + text[match.end(2) :]
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def write_defaults(path: Path, parent: ModelInfo, worker: ModelInfo) -> bool:
|
|
206
|
+
original = path.read_text()
|
|
207
|
+
updated = replace_toml_value(original, "models", "parent_recommended_model", parent.model)
|
|
208
|
+
updated = replace_toml_value(updated, "models", "worker_model", worker.model)
|
|
209
|
+
if updated == original:
|
|
210
|
+
return False
|
|
211
|
+
path.write_text(updated)
|
|
212
|
+
return True
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def load_fixture(path: Path) -> tuple[str, dict[str, str]]:
|
|
216
|
+
payload = json.loads(path.read_text())
|
|
217
|
+
return payload["index"], payload["models"]
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def main() -> int:
|
|
221
|
+
parser = argparse.ArgumentParser()
|
|
222
|
+
parser.add_argument("--defaults", default="policy/defaults.toml")
|
|
223
|
+
parser.add_argument("--fixture", help="offline JSON fixture used by tests")
|
|
224
|
+
parser.add_argument("--write", action="store_true", help="update policy/defaults.toml when changed")
|
|
225
|
+
parser.add_argument("--json", action="store_true", help="emit machine-readable result")
|
|
226
|
+
args = parser.parse_args()
|
|
227
|
+
|
|
228
|
+
try:
|
|
229
|
+
if args.fixture:
|
|
230
|
+
index_raw, fixture_models = load_fixture(Path(args.fixture))
|
|
231
|
+
fetch_model = lambda model: fixture_models[model]
|
|
232
|
+
else:
|
|
233
|
+
index_raw = fetch(MODELS_URL)
|
|
234
|
+
fetch_model = lambda model: fetch(MODEL_URL.format(model=model))
|
|
235
|
+
|
|
236
|
+
discovered = discover_models(index_raw)
|
|
237
|
+
if not discovered:
|
|
238
|
+
raise ValueError("no Sol/Terra/Luna model ids discovered from official model index")
|
|
239
|
+
|
|
240
|
+
parsed: list[ModelInfo] = []
|
|
241
|
+
for model in discovered:
|
|
242
|
+
try:
|
|
243
|
+
parsed.append(parse_model(model, fetch_model(model)))
|
|
244
|
+
except (KeyError, ValueError, urllib.error.URLError) as exc:
|
|
245
|
+
print(f"warning: skipping {model}: {exc}", file=sys.stderr)
|
|
246
|
+
|
|
247
|
+
parent, worker = choose(parsed)
|
|
248
|
+
result = {
|
|
249
|
+
"parent_recommended_model": parent.model,
|
|
250
|
+
"worker_model": worker.model,
|
|
251
|
+
"worker_cost_score": round(worker.worker_cost_score, 6),
|
|
252
|
+
"worker_input_price": worker.input_price,
|
|
253
|
+
"worker_cached_input_price": worker.cached_input_price,
|
|
254
|
+
"worker_output_price": worker.output_price,
|
|
255
|
+
"source": MODELS_URL,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
changed = write_defaults(Path(args.defaults), parent, worker) if args.write else False
|
|
259
|
+
result["changed"] = changed
|
|
260
|
+
|
|
261
|
+
if args.json:
|
|
262
|
+
print(json.dumps(result, sort_keys=True))
|
|
263
|
+
else:
|
|
264
|
+
print(f"parent recommendation: {parent.model}")
|
|
265
|
+
print(
|
|
266
|
+
f"worker recommendation: {worker.model} "
|
|
267
|
+
f"(${worker.input_price:g} input / ${worker.output_price:g} output per 1M)"
|
|
268
|
+
)
|
|
269
|
+
print("defaults changed" if changed else "defaults already current")
|
|
270
|
+
return 0
|
|
271
|
+
except Exception as exc:
|
|
272
|
+
print(f"recommendation check failed closed: {exc}", file=sys.stderr)
|
|
273
|
+
return 1
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
if __name__ == "__main__":
|
|
277
|
+
raise SystemExit(main())
|