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.
Files changed (113) hide show
  1. codex_flow/__init__.py +28 -0
  2. codex_flow/__main__.py +9 -0
  3. codex_flow/cli.py +242 -0
  4. codex_flow/data/LICENSE +21 -0
  5. codex_flow/data/README.en.md +303 -0
  6. codex_flow/data/README.md +305 -0
  7. codex_flow/data/VERSION +1 -0
  8. codex_flow/data/apps/chatgpt-mcp/README.md +86 -0
  9. codex_flow/data/apps/chatgpt-mcp/__init__.py +1 -0
  10. codex_flow/data/apps/chatgpt-mcp/adapter.py +458 -0
  11. codex_flow/data/apps/chatgpt-mcp/server.py +358 -0
  12. codex_flow/data/apps/chatgpt-mcp/widget.html +927 -0
  13. codex_flow/data/apps/macos-overlay/README.en.md +121 -0
  14. codex_flow/data/apps/macos-overlay/README.md +123 -0
  15. codex_flow/data/apps/macos-overlay/Sources/Controllers/OverlayRuntimeState.swift +126 -0
  16. codex_flow/data/apps/macos-overlay/Sources/Controllers/OverlayScreenGeometry.swift +82 -0
  17. codex_flow/data/apps/macos-overlay/Sources/Controllers/OverlayWindowController.swift +1052 -0
  18. codex_flow/data/apps/macos-overlay/Sources/Localization.swift +197 -0
  19. codex_flow/data/apps/macos-overlay/Sources/Models/TelemetryData.swift +1557 -0
  20. codex_flow/data/apps/macos-overlay/Sources/Services/AccountSnapshotService.swift +1101 -0
  21. codex_flow/data/apps/macos-overlay/Sources/Services/FlowPilotInstanceLock.swift +153 -0
  22. codex_flow/data/apps/macos-overlay/Sources/Services/IPCServer.swift +298 -0
  23. codex_flow/data/apps/macos-overlay/Sources/Services/TelemetryQueryEngine.swift +800 -0
  24. codex_flow/data/apps/macos-overlay/Sources/Services/TelemetryWatcher.swift +135 -0
  25. codex_flow/data/apps/macos-overlay/Sources/Services/UpdateService.swift +610 -0
  26. codex_flow/data/apps/macos-overlay/Sources/Views/AccountView.swift +610 -0
  27. codex_flow/data/apps/macos-overlay/Sources/Views/AnalyticsView.swift +566 -0
  28. codex_flow/data/apps/macos-overlay/Sources/Views/AutostartView.swift +293 -0
  29. codex_flow/data/apps/macos-overlay/Sources/Views/BubbleView.swift +317 -0
  30. codex_flow/data/apps/macos-overlay/Sources/Views/HistoryView.swift +1124 -0
  31. codex_flow/data/apps/macos-overlay/Sources/Views/HoverRevealText.swift +165 -0
  32. codex_flow/data/apps/macos-overlay/Sources/Views/InspectorSkillsToolsView.swift +121 -0
  33. codex_flow/data/apps/macos-overlay/Sources/Views/LogoView.swift +182 -0
  34. codex_flow/data/apps/macos-overlay/Sources/Views/SleekSwitch.swift +117 -0
  35. codex_flow/data/apps/macos-overlay/Sources/Views/StrategyModeView.swift +561 -0
  36. codex_flow/data/apps/macos-overlay/Sources/Views/SummaryView.swift +1273 -0
  37. codex_flow/data/apps/macos-overlay/Sources/Views/UpdateView.swift +352 -0
  38. codex_flow/data/apps/macos-overlay/Sources/main.swift +340 -0
  39. codex_flow/data/apps/macos-overlay/Tests/OverlayScreenGeometryTests.swift +163 -0
  40. codex_flow/data/apps/macos-overlay/Tests/TelemetryPhase1ContractTests.swift +357 -0
  41. codex_flow/data/apps/macos-overlay/Tests/TelemetryQueryEngineConcurrencyTests.swift +221 -0
  42. codex_flow/data/apps/macos-overlay/Tests/TelemetryQuotaSelectionTests.swift +158 -0
  43. codex_flow/data/apps/macos-overlay/Tests/TelemetryWorkerTokenTests.swift +122 -0
  44. codex_flow/data/apps/macos-overlay/build.sh +75 -0
  45. codex_flow/data/benchmark/corpus.json +103 -0
  46. codex_flow/data/benchmark/manifest.example.json +41 -0
  47. codex_flow/data/benchmark/manifest.schema.json +137 -0
  48. codex_flow/data/benchmark/prices/gpt-5.6-2026-08-30.json +5 -0
  49. codex_flow/data/benchmark/profiles.json +90 -0
  50. codex_flow/data/benchmark/schema.json +77 -0
  51. codex_flow/data/benchmark/tasks.json +50 -0
  52. codex_flow/data/completions/codex-flow.bash +34 -0
  53. codex_flow/data/completions/codex-flow.zsh +52 -0
  54. codex_flow/data/glama.json +6 -0
  55. codex_flow/data/install-release.ps1 +126 -0
  56. codex_flow/data/install-release.sh +155 -0
  57. codex_flow/data/install.ps1 +349 -0
  58. codex_flow/data/install.sh +362 -0
  59. codex_flow/data/policy/benchmark.toml +49 -0
  60. codex_flow/data/policy/defaults.toml +70 -0
  61. codex_flow/data/scripts/analyze-benchmark.py +510 -0
  62. codex_flow/data/scripts/benchmark-local.py +171 -0
  63. codex_flow/data/scripts/check-recommendation.py +277 -0
  64. codex_flow/data/scripts/doctor.py +449 -0
  65. codex_flow/data/scripts/generate-release-manifest.py +74 -0
  66. codex_flow/data/scripts/localization.py +192 -0
  67. codex_flow/data/scripts/manage-hooks.py +448 -0
  68. codex_flow/data/scripts/manage-instructions.py +389 -0
  69. codex_flow/data/scripts/manage-shell.py +151 -0
  70. codex_flow/data/scripts/materialize-corpus.py +193 -0
  71. codex_flow/data/scripts/menu.py +646 -0
  72. codex_flow/data/scripts/migrations/0001_update_settings.py +80 -0
  73. codex_flow/data/scripts/package-release.py +132 -0
  74. codex_flow/data/scripts/render-benchmark-report.py +292 -0
  75. codex_flow/data/scripts/run-benchmark.py +829 -0
  76. codex_flow/data/scripts/strategies/__init__.py +28 -0
  77. codex_flow/data/scripts/strategies/balanced.py +115 -0
  78. codex_flow/data/scripts/strategies/base.py +363 -0
  79. codex_flow/data/scripts/strategies/efficient.py +158 -0
  80. codex_flow/data/scripts/strategies/lifecycle_runtime.py +590 -0
  81. codex_flow/data/scripts/strategies/quality.py +209 -0
  82. codex_flow/data/scripts/strategies/speed.py +108 -0
  83. codex_flow/data/scripts/strategies/task_budget_runtime.py +644 -0
  84. codex_flow/data/scripts/strategies/task_phase_runtime.py +341 -0
  85. codex_flow/data/scripts/strategies/work_unit_runtime.py +421 -0
  86. codex_flow/data/scripts/strategy_runtime.py +1091 -0
  87. codex_flow/data/scripts/telemetry.py +400 -0
  88. codex_flow/data/scripts/telemetry_core/__init__.py +192 -0
  89. codex_flow/data/scripts/telemetry_core/app_server.py +1192 -0
  90. codex_flow/data/scripts/telemetry_core/collector.py +1247 -0
  91. codex_flow/data/scripts/telemetry_core/common.py +421 -0
  92. codex_flow/data/scripts/telemetry_core/latency.py +593 -0
  93. codex_flow/data/scripts/telemetry_core/query.py +427 -0
  94. codex_flow/data/scripts/telemetry_core/quota_ledger.py +598 -0
  95. codex_flow/data/scripts/telemetry_core/render.py +460 -0
  96. codex_flow/data/scripts/telemetry_core/repair.py +223 -0
  97. codex_flow/data/scripts/ui.py +266 -0
  98. codex_flow/data/scripts/update-homebrew-formula.py +146 -0
  99. codex_flow/data/scripts/update_runtime_config.py +134 -0
  100. codex_flow/data/scripts/updater.py +1718 -0
  101. codex_flow/data/smithery.yaml +18 -0
  102. codex_flow/data/templates/agents/worker-explorer.toml +24 -0
  103. codex_flow/data/templates/agents/worker-implementer.toml +49 -0
  104. codex_flow/data/templates/agents/worker-reviewer.toml +25 -0
  105. codex_flow/data/templates/flow-pilot-instructions.md +35 -0
  106. codex_flow/data/templates/skills/flow-pilot/SKILL.md +577 -0
  107. codex_flow/mcp.py +35 -0
  108. codex_flow-2.1.13.dist-info/METADATA +342 -0
  109. codex_flow-2.1.13.dist-info/RECORD +113 -0
  110. codex_flow-2.1.13.dist-info/WHEEL +5 -0
  111. codex_flow-2.1.13.dist-info/entry_points.txt +3 -0
  112. codex_flow-2.1.13.dist-info/licenses/LICENSE +21 -0
  113. 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())