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,449 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Cross-platform localized diagnostics for codex-flow."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from localization import configured_language, resolve_language, tr
|
|
14
|
+
|
|
15
|
+
CODEX_HOME = Path(os.environ.get("CODEX_HOME", Path.home() / ".codex"))
|
|
16
|
+
CONFIG = CODEX_HOME / "config.toml"
|
|
17
|
+
POLICY = CODEX_HOME / "codex-flow.toml"
|
|
18
|
+
HOOKS = CODEX_HOME / "hooks.json"
|
|
19
|
+
STATE_DIR = CODEX_HOME / "codex-flow"
|
|
20
|
+
INSTRUCTION_HELPER = STATE_DIR / "manage-instructions.py"
|
|
21
|
+
INSTRUCTION_TEMPLATE = STATE_DIR / "flow-pilot-instructions.md"
|
|
22
|
+
LANG = resolve_language(POLICY)
|
|
23
|
+
FAILED = False
|
|
24
|
+
CODEX_AVAILABLE = True
|
|
25
|
+
HOOKS_ACTION_REQUIRED = False
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def T(en: str, zh: str) -> str:
|
|
29
|
+
return tr(en, zh, lang=LANG)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def policy_value(section: str, key: str, default: str = "") -> str:
|
|
33
|
+
try:
|
|
34
|
+
text = POLICY.read_text(encoding="utf-8-sig")
|
|
35
|
+
except OSError:
|
|
36
|
+
return default
|
|
37
|
+
match = re.search(rf"(?ms)^\[{re.escape(section)}\]\s*\n(.*?)(?=^\[[^\n]+\]\s*$|\Z)", text)
|
|
38
|
+
if not match:
|
|
39
|
+
return default
|
|
40
|
+
key_match = re.search(rf"(?m)^\s*{re.escape(key)}\s*=\s*(.*?)\s*$", match.group(1))
|
|
41
|
+
if not key_match:
|
|
42
|
+
return default
|
|
43
|
+
value = re.sub(r"\s+#.*$", "", key_match.group(1)).strip()
|
|
44
|
+
if len(value) >= 2 and value[0] == value[-1] == '"':
|
|
45
|
+
value = value[1:-1]
|
|
46
|
+
return value or default
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def root_value(key: str, default: str = "") -> str:
|
|
50
|
+
try:
|
|
51
|
+
text = POLICY.read_text(encoding="utf-8-sig")
|
|
52
|
+
except OSError:
|
|
53
|
+
return default
|
|
54
|
+
root = text.split("[", 1)[0]
|
|
55
|
+
m = re.search(rf"(?m)^\s*{re.escape(key)}\s*=\s*(.*?)\s*$", root)
|
|
56
|
+
return re.sub(r"\s+#.*$", "", m.group(1)).strip().strip('"') if m else default
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def ok(msg: str) -> None:
|
|
60
|
+
print(f" ✔ {msg}")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def warn(msg: str) -> None:
|
|
64
|
+
print(f" ▲ {msg}")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def fail(msg: str) -> None:
|
|
68
|
+
global FAILED
|
|
69
|
+
FAILED = True
|
|
70
|
+
print(f" ✖ {msg}")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def section(en: str, zh: str) -> None:
|
|
74
|
+
print(f"\n {T(en, zh)}")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def codex_thread_usage_capable(version: str) -> bool | None:
|
|
78
|
+
match = re.search(r"(\d+)\.(\d+)", version)
|
|
79
|
+
if not match:
|
|
80
|
+
return None
|
|
81
|
+
major, minor = int(match.group(1)), int(match.group(2))
|
|
82
|
+
return major > 0 or (major == 0 and minor >= 151)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def run_capture(args: list[str]) -> tuple[int, str]:
|
|
86
|
+
try:
|
|
87
|
+
command = args
|
|
88
|
+
if os.name == "nt" and args and Path(args[0]).suffix.lower() in {".cmd", ".bat"}:
|
|
89
|
+
command = [os.environ.get("COMSPEC", "cmd.exe"), "/d", "/c", *args]
|
|
90
|
+
proc = subprocess.run(command, capture_output=True, text=True, timeout=8, check=False)
|
|
91
|
+
text = (proc.stdout or proc.stderr or "").strip()
|
|
92
|
+
return proc.returncode, text
|
|
93
|
+
except (OSError, subprocess.SubprocessError):
|
|
94
|
+
return 127, ""
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _check_effort(label_en: str, label_zh: str, value: str) -> None:
|
|
98
|
+
if value in {"high", "xhigh", "max"}:
|
|
99
|
+
ok(T(f"{label_en}: {value}", f"{label_zh}:{value}"))
|
|
100
|
+
else:
|
|
101
|
+
fail(T(f"invalid {label_en}: {value or 'missing'}", f"无效{label_zh}:{value or 'missing'}"))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _hook_action_hint() -> str:
|
|
105
|
+
if CODEX_AVAILABLE:
|
|
106
|
+
return T(
|
|
107
|
+
"Open the Codex CLI using the same CODEX_HOME, run /hooks, then review and trust/enable the FlowPilot hooks.",
|
|
108
|
+
"请使用同一 CODEX_HOME 打开 Codex CLI,运行 /hooks,然后 review 并信任/启用 FlowPilot hooks。",
|
|
109
|
+
)
|
|
110
|
+
return T(
|
|
111
|
+
"Hook review is required, but Codex CLI is not in PATH. Install/open Codex CLI once with the same CODEX_HOME, run /hooks, approve FlowPilot, then you may continue using ChatGPT Desktop without keeping the CLI running.",
|
|
112
|
+
"Hooks 需要 review,但当前 PATH 中没有 Codex CLI。请使用同一 CODEX_HOME 安装/打开一次 Codex CLI,运行 /hooks 批准 FlowPilot;完成授权后无需让 CLI 常驻,可继续使用 ChatGPT Desktop。",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _instruction_entry_status() -> dict:
|
|
117
|
+
if not INSTRUCTION_HELPER.is_file():
|
|
118
|
+
return {"status": "missing", "error": "instruction helper is missing"}
|
|
119
|
+
if not INSTRUCTION_TEMPLATE.is_file():
|
|
120
|
+
return {"status": "missing", "error": "instruction template is missing"}
|
|
121
|
+
code, output = run_capture([
|
|
122
|
+
sys.executable,
|
|
123
|
+
str(INSTRUCTION_HELPER),
|
|
124
|
+
"--codex-home",
|
|
125
|
+
str(CODEX_HOME),
|
|
126
|
+
"--template",
|
|
127
|
+
str(INSTRUCTION_TEMPLATE),
|
|
128
|
+
"--json",
|
|
129
|
+
"check",
|
|
130
|
+
])
|
|
131
|
+
try:
|
|
132
|
+
report = json.loads(output)
|
|
133
|
+
except (json.JSONDecodeError, TypeError):
|
|
134
|
+
report = {"status": "error", "error": output or "instruction status unavailable"}
|
|
135
|
+
report["returncode"] = code
|
|
136
|
+
return report
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def main() -> int:
|
|
140
|
+
global CODEX_AVAILABLE, HOOKS_ACTION_REQUIRED
|
|
141
|
+
print(f"\n🩺 {T('codex-flow doctor', 'codex-flow 系统诊断')}")
|
|
142
|
+
|
|
143
|
+
section("Environment & Tools", "环境与工具")
|
|
144
|
+
codex = shutil.which("codex")
|
|
145
|
+
if codex:
|
|
146
|
+
_, version = run_capture([codex, "--version"])
|
|
147
|
+
ok(T(f"Codex CLI found{': ' + version if version else ''}", f"已找到 Codex CLI{(':' + version) if version else ''}"))
|
|
148
|
+
capable = codex_thread_usage_capable(version)
|
|
149
|
+
if capable is False:
|
|
150
|
+
warn(T(
|
|
151
|
+
f"Codex CLI {version} is below the known 0.151 support baseline; thread-attributed telemetry may be unavailable; upgrade to 0.151+ recommended",
|
|
152
|
+
f"Codex CLI {version} 低于已知的 0.151 支持基线;按线程归属的遥测可能不可用,建议升级到 0.151+",
|
|
153
|
+
))
|
|
154
|
+
else:
|
|
155
|
+
CODEX_AVAILABLE = False
|
|
156
|
+
warn(T(
|
|
157
|
+
"Codex CLI not found in PATH; core routing remains usable, telemetry quota reads and real local benchmark execution are unavailable",
|
|
158
|
+
"PATH 中未找到 Codex CLI;核心路由仍可使用,但遥测额度读取和真实本地 Benchmark 不可用",
|
|
159
|
+
))
|
|
160
|
+
print(" " + T("(Install via: npm install -g @openai/codex or brew install codex)", "(可通过 npm install -g @openai/codex 或 brew install codex 安装)"))
|
|
161
|
+
|
|
162
|
+
ok(T("config.toml found", "已找到 config.toml")) if CONFIG.is_file() else fail(T(f"missing {CONFIG}", f"缺少 {CONFIG}"))
|
|
163
|
+
ok(T("codex-flow policy found", "已找到 codex-flow 策略文件")) if POLICY.is_file() else fail(T(f"missing {POLICY}", f"缺少 {POLICY}"))
|
|
164
|
+
|
|
165
|
+
section("Strategy Runtime & Skills", "策略运行时与 Skills")
|
|
166
|
+
checks = [
|
|
167
|
+
(CODEX_HOME / "agents/worker-explorer.toml", "worker-explorer"),
|
|
168
|
+
(CODEX_HOME / "agents/worker-implementer.toml", "worker-implementer"),
|
|
169
|
+
(CODEX_HOME / "agents/worker-reviewer.toml", "worker-reviewer"),
|
|
170
|
+
(CODEX_HOME / "skills/flow-pilot/SKILL.md", "FlowPilot skill"),
|
|
171
|
+
(INSTRUCTION_HELPER, "FlowPilot entry helper"),
|
|
172
|
+
(INSTRUCTION_TEMPLATE, "FlowPilot entry template"),
|
|
173
|
+
(STATE_DIR / "strategy_runtime.py", "strategy runtime helper"),
|
|
174
|
+
(STATE_DIR / "strategies/__init__.py", "built-in strategy registry"),
|
|
175
|
+
(STATE_DIR / "strategies/task_budget_runtime.py", "task budget runtime helper"),
|
|
176
|
+
(STATE_DIR / "telemetry_core/latency.py", "latency telemetry helper"),
|
|
177
|
+
(STATE_DIR / "defaults.toml", "release policy defaults"),
|
|
178
|
+
(STATE_DIR / "updater.py", "OTA updater"),
|
|
179
|
+
(STATE_DIR / "update_runtime_config.py", "runtime config reconciler"),
|
|
180
|
+
]
|
|
181
|
+
for path, label in checks:
|
|
182
|
+
ok(T(f"{label} installed", f"{label} 已安装")) if path.is_file() else fail(T(f"{label} missing", f"缺少 {label}"))
|
|
183
|
+
|
|
184
|
+
section("Prompt Entry", "提示入口")
|
|
185
|
+
entry = _instruction_entry_status()
|
|
186
|
+
entry_status = str(entry.get("status") or "unknown")
|
|
187
|
+
entry_target = str(entry.get("target") or "AGENTS.md")
|
|
188
|
+
if entry_status == "installed":
|
|
189
|
+
ok(T(
|
|
190
|
+
f"FlowPilot entry instructions installed in {entry_target}",
|
|
191
|
+
f"FlowPilot 提示入口已安装到 {entry_target}",
|
|
192
|
+
))
|
|
193
|
+
elif entry_status == "missing":
|
|
194
|
+
fail(T(
|
|
195
|
+
"FlowPilot entry instructions missing from the effective AGENTS file",
|
|
196
|
+
"生效的 AGENTS 文件缺少 FlowPilot 提示入口",
|
|
197
|
+
))
|
|
198
|
+
elif entry_status == "stale":
|
|
199
|
+
fail(T(
|
|
200
|
+
f"FlowPilot entry instructions are stale in {entry_target}",
|
|
201
|
+
f"{entry_target} 中的 FlowPilot 提示入口已过期",
|
|
202
|
+
))
|
|
203
|
+
elif entry_status == "shadowed":
|
|
204
|
+
fail(T(
|
|
205
|
+
"FlowPilot entry instructions are shadowed by a non-empty AGENTS.override.md",
|
|
206
|
+
"FlowPilot 提示入口被非空 AGENTS.override.md 遮蔽",
|
|
207
|
+
))
|
|
208
|
+
else:
|
|
209
|
+
detail = str(entry.get("error") or entry_status)
|
|
210
|
+
fail(T(
|
|
211
|
+
f"FlowPilot entry instructions could not be checked: {detail}",
|
|
212
|
+
f"无法检查 FlowPilot 提示入口:{detail}",
|
|
213
|
+
))
|
|
214
|
+
|
|
215
|
+
if POLICY.is_file():
|
|
216
|
+
schema = root_value("schema_version")
|
|
217
|
+
if schema == "4":
|
|
218
|
+
ok(T("policy schema v4", "策略 schema v4"))
|
|
219
|
+
elif schema == "3":
|
|
220
|
+
warn(T("policy schema v3 is backward compatible; reinstall/update recommended to enable persistent multi-strategy configuration", "策略 schema v3 可向后兼容;建议重新安装/更新以启用持久化多策略配置"))
|
|
221
|
+
else:
|
|
222
|
+
warn(T(f"policy schema is {schema or 'unknown'}; reinstall recommended", f"策略 schema 为 {schema or 'unknown'};建议重新安装"))
|
|
223
|
+
|
|
224
|
+
strategy_enabled = policy_value("strategy", "enabled", "true")
|
|
225
|
+
strategy = policy_value("strategy", "profile", "efficient")
|
|
226
|
+
routing = policy_value("routing", "mode", "adaptive")
|
|
227
|
+
review = policy_value("modifiers", "review", "auto")
|
|
228
|
+
fanout = policy_value("modifiers", "fanout", "auto")
|
|
229
|
+
rollout_mode = policy_value("reasoning.rollout", "mode", "shadow")
|
|
230
|
+
rollout_floors = {
|
|
231
|
+
"minimum": policy_value("reasoning.rollout", "minimum", "high"),
|
|
232
|
+
"routine": policy_value("reasoning.rollout", "routine", "high"),
|
|
233
|
+
"complex": policy_value("reasoning.rollout", "complex", "xhigh"),
|
|
234
|
+
"critical": policy_value("reasoning.rollout", "critical", "max"),
|
|
235
|
+
}
|
|
236
|
+
if strategy_enabled == "true":
|
|
237
|
+
ok(T("strategy dispatch: enabled", "策略分发:已启用"))
|
|
238
|
+
elif strategy_enabled == "false":
|
|
239
|
+
ok(T("strategy dispatch: disabled by policy", "策略分发:已由策略禁用"))
|
|
240
|
+
else:
|
|
241
|
+
fail(T(f"invalid strategy dispatch switch: {strategy_enabled or 'missing'}", f"无效策略分发开关:{strategy_enabled or 'missing'}"))
|
|
242
|
+
if strategy in {"efficient", "balanced", "quality", "speed"}:
|
|
243
|
+
ok(T(f"strategy profile: {strategy}", f"执行策略:{strategy}"))
|
|
244
|
+
else:
|
|
245
|
+
fail(T(f"invalid strategy profile: {strategy}", f"无效执行策略:{strategy}"))
|
|
246
|
+
if routing in {"adaptive", "direct", "delegate"}:
|
|
247
|
+
ok(T(f"routing mode: {routing}", f"路由模式:{routing}"))
|
|
248
|
+
else:
|
|
249
|
+
fail(T(f"invalid routing mode: {routing}", f"无效路由模式:{routing}"))
|
|
250
|
+
if review in {"auto", "standard", "strict"}:
|
|
251
|
+
ok(T(f"review modifier: {review}", f"Review 修饰策略:{review}"))
|
|
252
|
+
else:
|
|
253
|
+
fail(T(f"invalid review modifier: {review or 'missing'}", f"无效 Review 修饰策略:{review or 'missing'}"))
|
|
254
|
+
if fanout in {"auto", "conservative", "aggressive"}:
|
|
255
|
+
ok(T(f"fanout modifier: {fanout}", f"Fan-out 修饰策略:{fanout}"))
|
|
256
|
+
else:
|
|
257
|
+
fail(T(f"invalid fanout modifier: {fanout or 'missing'}", f"无效 Fan-out 修饰策略:{fanout or 'missing'}"))
|
|
258
|
+
if rollout_mode in {"legacy", "shadow", "adaptive"}:
|
|
259
|
+
ok(T(
|
|
260
|
+
"efficient reasoning rollout: " + rollout_mode + " (minimum=" + rollout_floors["minimum"]
|
|
261
|
+
+ ", routine=" + rollout_floors["routine"] + ", complex=" + rollout_floors["complex"]
|
|
262
|
+
+ ", critical=" + rollout_floors["critical"] + ")",
|
|
263
|
+
"efficient 推理 rollout:" + rollout_mode + "(最低=" + rollout_floors["minimum"]
|
|
264
|
+
+ ",routine=" + rollout_floors["routine"] + ",complex=" + rollout_floors["complex"]
|
|
265
|
+
+ ",critical=" + rollout_floors["critical"] + ")",
|
|
266
|
+
))
|
|
267
|
+
else:
|
|
268
|
+
fail(T(
|
|
269
|
+
f"invalid efficient reasoning rollout mode: {rollout_mode or 'missing'}",
|
|
270
|
+
f"无效 efficient 推理 rollout 模式:{rollout_mode or 'missing'}",
|
|
271
|
+
))
|
|
272
|
+
for key, label in (("minimum", "rollout minimum"), ("routine", "rollout routine"), ("complex", "rollout complex"), ("critical", "rollout critical")):
|
|
273
|
+
_check_effort(
|
|
274
|
+
f"efficient reasoning {label}",
|
|
275
|
+
f"efficient 推理 {label}",
|
|
276
|
+
rollout_floors[key],
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
parent_effort = policy_value("parent", "min_reasoning_effort")
|
|
280
|
+
worker_effort = policy_value("worker", "min_reasoning_effort")
|
|
281
|
+
_check_effort("parent minimum reasoning", "父 Agent 最低推理强度", parent_effort)
|
|
282
|
+
_check_effort("worker minimum reasoning", "子 Agent 最低推理强度", worker_effort)
|
|
283
|
+
for role, role_zh in (("parent", "父 Agent"), ("worker", "子 Agent")):
|
|
284
|
+
for key, label in (
|
|
285
|
+
("routine_effort", "routine reasoning"),
|
|
286
|
+
("complex_effort", "complex reasoning"),
|
|
287
|
+
("critical_effort", "critical reasoning"),
|
|
288
|
+
):
|
|
289
|
+
_check_effort(f"{role} {label}", f"{role_zh} {label}", policy_value(role, key))
|
|
290
|
+
|
|
291
|
+
parent_reasoning = policy_value("parent", "reasoning_policy")
|
|
292
|
+
worker_reasoning = policy_value("worker", "reasoning_policy")
|
|
293
|
+
if parent_reasoning == "adaptive":
|
|
294
|
+
ok(T("parent reasoning policy: adaptive", "父 Agent 推理策略:adaptive"))
|
|
295
|
+
else:
|
|
296
|
+
warn(T(f"parent reasoning policy: {parent_reasoning or 'unknown'}", f"父 Agent 推理策略:{parent_reasoning or 'unknown'}"))
|
|
297
|
+
if worker_reasoning == "adaptive":
|
|
298
|
+
ok(T("worker reasoning policy: adaptive", "子 Agent 推理策略:adaptive"))
|
|
299
|
+
else:
|
|
300
|
+
warn(T(f"worker reasoning policy: {worker_reasoning or 'unknown'}", f"子 Agent 推理策略:{worker_reasoning or 'unknown'}"))
|
|
301
|
+
|
|
302
|
+
max_threads = policy_value("runtime", "max_concurrent_threads")
|
|
303
|
+
max_repairs = policy_value("runtime", "max_repair_cycles")
|
|
304
|
+
if re.fullmatch(r"[1-9]\d*", max_threads or ""):
|
|
305
|
+
ok(T(f"runtime thread ceiling: {max_threads}", f"运行时线程上限:{max_threads}"))
|
|
306
|
+
else:
|
|
307
|
+
fail(T(f"invalid runtime thread ceiling: {max_threads or 'missing'}", f"无效运行时线程上限:{max_threads or 'missing'}"))
|
|
308
|
+
if re.fullmatch(r"\d+", max_repairs or ""):
|
|
309
|
+
ok(T(f"runtime repair ceiling: {max_repairs}", f"运行时修复轮次上限:{max_repairs}"))
|
|
310
|
+
else:
|
|
311
|
+
fail(T(f"invalid runtime repair ceiling: {max_repairs or 'missing'}", f"无效运行时修复轮次上限:{max_repairs or 'missing'}"))
|
|
312
|
+
|
|
313
|
+
parent_policy = policy_value("parent", "model_policy", "unknown")
|
|
314
|
+
parent_min_model = policy_value("parent", "min_model", "unknown")
|
|
315
|
+
worker_policy = policy_value("worker", "model_policy", "unknown")
|
|
316
|
+
worker_requested = policy_value("worker", "model", "unknown")
|
|
317
|
+
worker_model = policy_value("worker", "resolved_model", "unknown")
|
|
318
|
+
ok(T(
|
|
319
|
+
f"parent model policy: {parent_policy} (floor: {parent_min_model})",
|
|
320
|
+
f"父 Agent 模型策略:{parent_policy}(下限:{parent_min_model})",
|
|
321
|
+
))
|
|
322
|
+
ok(T(
|
|
323
|
+
f"worker model policy: {worker_policy} (requested: {worker_requested}, resolved: {worker_model})",
|
|
324
|
+
f"子 Agent 模型策略:{worker_policy}(请求:{worker_requested},解析:{worker_model})",
|
|
325
|
+
))
|
|
326
|
+
|
|
327
|
+
configured = configured_language(POLICY)
|
|
328
|
+
effective = resolve_language(POLICY)
|
|
329
|
+
if configured in {"auto", "zh", "en"}:
|
|
330
|
+
ok(T(f"UI language: {configured} (effective: {effective})", f"界面语言:{configured}(当前生效:{effective})"))
|
|
331
|
+
else:
|
|
332
|
+
fail(T(f"invalid UI language: {configured}", f"无效界面语言:{configured}"))
|
|
333
|
+
|
|
334
|
+
notifications = policy_value("telemetry", "notifications", "false")
|
|
335
|
+
retention = policy_value("telemetry", "retention_days", "30")
|
|
336
|
+
if notifications == "true":
|
|
337
|
+
ok(T("system notifications: enabled (macOS)", "系统通知:已启用(macOS)"))
|
|
338
|
+
elif notifications == "false":
|
|
339
|
+
ok(T("system notifications: disabled by policy", "系统通知:已由策略禁用"))
|
|
340
|
+
else:
|
|
341
|
+
fail(T(f"invalid system notifications policy: {notifications or 'missing'}", f"无效系统通知策略:{notifications or 'missing'}"))
|
|
342
|
+
if re.fullmatch(r"[1-9]\d*", retention or ""):
|
|
343
|
+
ok(T(f"per-run telemetry retention: {retention} days", f"单次任务遥测保留:{retention} 天"))
|
|
344
|
+
else:
|
|
345
|
+
fail(T(f"invalid per-run telemetry retention: {retention or 'missing'}", f"无效单次任务遥测保留配置:{retention or 'missing'}"))
|
|
346
|
+
|
|
347
|
+
section("Hooks & Telemetry", "Hooks 与遥测")
|
|
348
|
+
enabled = policy_value("telemetry", "enabled", "true")
|
|
349
|
+
if enabled == "true":
|
|
350
|
+
collector = STATE_DIR / "telemetry.py"
|
|
351
|
+
manager = STATE_DIR / "manage-hooks.py"
|
|
352
|
+
ok(T("FlowPilot telemetry collector installed", "FlowPilot 遥测采集器已安装")) if collector.is_file() else fail(T("FlowPilot telemetry collector missing", "缺少 FlowPilot 遥测采集器"))
|
|
353
|
+
ok(T("FlowPilot hook manager installed", "FlowPilot hook 管理器已安装")) if manager.is_file() else fail(T("FlowPilot hook manager missing", "缺少 FlowPilot hook 管理器"))
|
|
354
|
+
if manager.is_file():
|
|
355
|
+
code, _ = run_capture([sys.executable, str(manager), "check", "--hooks", str(HOOKS)])
|
|
356
|
+
if code == 0:
|
|
357
|
+
ok(T("FlowPilot lifecycle hooks installed", "FlowPilot 生命周期 hooks 已安装"))
|
|
358
|
+
_, trust_output = run_capture([
|
|
359
|
+
sys.executable,
|
|
360
|
+
str(manager),
|
|
361
|
+
"status",
|
|
362
|
+
"--hooks",
|
|
363
|
+
str(HOOKS),
|
|
364
|
+
"--config",
|
|
365
|
+
str(CONFIG),
|
|
366
|
+
"--json",
|
|
367
|
+
])
|
|
368
|
+
try:
|
|
369
|
+
trust = json.loads(trust_output)
|
|
370
|
+
except (json.JSONDecodeError, TypeError):
|
|
371
|
+
trust = {"status": "unknown", "error": trust_output or "no trust report"}
|
|
372
|
+
status = str(trust.get("status") or "unknown")
|
|
373
|
+
total = int(trust.get("total") or 0)
|
|
374
|
+
trusted = int(trust.get("trusted") or 0)
|
|
375
|
+
if status == "trusted":
|
|
376
|
+
ok(T(
|
|
377
|
+
f"FlowPilot hook authorization: trusted ({trusted}/{total})",
|
|
378
|
+
f"FlowPilot hooks 授权:已信任({trusted}/{total})",
|
|
379
|
+
))
|
|
380
|
+
elif status == "modified":
|
|
381
|
+
HOOKS_ACTION_REQUIRED = True
|
|
382
|
+
warn(T(
|
|
383
|
+
"FlowPilot hook authorization: definitions changed since approval; re-authorization required",
|
|
384
|
+
"FlowPilot hooks 授权:hook 定义在上次批准后已变化,需要重新授权",
|
|
385
|
+
))
|
|
386
|
+
print(" " + _hook_action_hint())
|
|
387
|
+
elif status == "untrusted":
|
|
388
|
+
HOOKS_ACTION_REQUIRED = True
|
|
389
|
+
warn(T(
|
|
390
|
+
"FlowPilot hook authorization: approval required before hook-backed features can run",
|
|
391
|
+
"FlowPilot hooks 授权:需要批准后,依赖 hooks 的功能才能运行",
|
|
392
|
+
))
|
|
393
|
+
print(" " + _hook_action_hint())
|
|
394
|
+
elif status == "disabled":
|
|
395
|
+
HOOKS_ACTION_REQUIRED = True
|
|
396
|
+
warn(T(
|
|
397
|
+
"FlowPilot hook authorization: one or more lifecycle hooks are disabled",
|
|
398
|
+
"FlowPilot hooks 授权:一个或多个生命周期 hook 已被禁用",
|
|
399
|
+
))
|
|
400
|
+
print(" " + _hook_action_hint())
|
|
401
|
+
else:
|
|
402
|
+
HOOKS_ACTION_REQUIRED = True
|
|
403
|
+
detail = str(trust.get("error") or "trust state unavailable")
|
|
404
|
+
warn(T(
|
|
405
|
+
f"FlowPilot hook authorization could not be verified: {detail}",
|
|
406
|
+
f"无法验证 FlowPilot hooks 授权状态:{detail}",
|
|
407
|
+
))
|
|
408
|
+
print(" " + _hook_action_hint())
|
|
409
|
+
else:
|
|
410
|
+
fail(T(f"FlowPilot lifecycle hooks missing from {HOOKS}", f"{HOOKS} 中缺少 FlowPilot 生命周期 hooks"))
|
|
411
|
+
elif enabled == "false":
|
|
412
|
+
ok(T("FlowPilot telemetry disabled by policy", "FlowPilot 遥测已由策略禁用"))
|
|
413
|
+
else:
|
|
414
|
+
fail(T(f"invalid telemetry enabled policy: {enabled or 'missing'}", f"无效遥测启用配置:{enabled or 'missing'}"))
|
|
415
|
+
|
|
416
|
+
if CONFIG.is_file() and worker_model and worker_model != "unknown":
|
|
417
|
+
try:
|
|
418
|
+
cfg = CONFIG.read_text(encoding="utf-8-sig")
|
|
419
|
+
except OSError:
|
|
420
|
+
cfg = ""
|
|
421
|
+
if f'default_subagent_model = "{worker_model}"' in cfg:
|
|
422
|
+
ok(T("Codex worker baseline model matches resolved policy", "Codex 子 Agent 基线模型与解析后的策略一致"))
|
|
423
|
+
else:
|
|
424
|
+
fail(T("Codex default_subagent_model does not match resolved policy", "Codex default_subagent_model 与解析后的策略不一致"))
|
|
425
|
+
if f'default_subagent_reasoning_effort = "{worker_effort}"' in cfg:
|
|
426
|
+
ok(T("Codex worker baseline effort matches policy floor", "Codex 子 Agent 基线推理强度与策略下限一致"))
|
|
427
|
+
else:
|
|
428
|
+
fail(T("Codex default_subagent_reasoning_effort does not match worker floor", "Codex default_subagent_reasoning_effort 与子 Agent 下限不一致"))
|
|
429
|
+
|
|
430
|
+
print("\n " + "─" * 69)
|
|
431
|
+
if not FAILED:
|
|
432
|
+
if HOOKS_ACTION_REQUIRED:
|
|
433
|
+
print(" ⚠ " + T(
|
|
434
|
+
"Installed with action required: FlowPilot hook-backed features are inactive until hook review is completed.",
|
|
435
|
+
"安装正常,但仍需操作:在完成 hook review 前,FlowPilot 依赖 hooks 的功能不会生效。",
|
|
436
|
+
))
|
|
437
|
+
elif CODEX_AVAILABLE:
|
|
438
|
+
print(" ✨ " + T("Ready. FlowPilot strategy runtime and deterministic telemetry are installed.", "就绪。FlowPilot 策略运行时与确定性遥测均已安装。"))
|
|
439
|
+
else:
|
|
440
|
+
print(" ✨ " + T("Ready. Core FlowPilot strategy runtime is installed and healthy; Codex CLI-dependent telemetry quota reads and benchmark execution are unavailable in this shell.", "就绪。FlowPilot 核心策略运行时安装正常;当前 shell 中依赖 Codex CLI 的遥测额度读取和 Benchmark 执行不可用。"))
|
|
441
|
+
print(" " + T("Note: per-spawn model/effort overrides depend on the active Codex build; the installed baseline remains valid when overrides are unavailable.", "注意:每次 spawn 的模型/推理强度覆盖取决于当前 Codex 版本;即使覆盖不可用,已安装的基线配置仍然有效。") + "\n")
|
|
442
|
+
return 0
|
|
443
|
+
|
|
444
|
+
print(" ✖ " + T("One or more required core checks failed. Re-run the installer, then restart Codex.", "一个或多个核心检查失败。请重新运行安装器,然后重启 Codex。") + "\n")
|
|
445
|
+
return 1
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
if __name__ == "__main__":
|
|
449
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Generate codex-flow-update.json from packaged release artifacts."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
from datetime import datetime, timezone
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
13
|
+
REPO = "ParsifalC/codex-flow"
|
|
14
|
+
MINIMUM_UPDATER_VERSION = "1.0.0"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main() -> int:
|
|
18
|
+
parser = argparse.ArgumentParser()
|
|
19
|
+
parser.add_argument("--dist", type=Path, default=Path("dist"))
|
|
20
|
+
parser.add_argument("--tag")
|
|
21
|
+
parser.add_argument("--channel", default="stable", choices=("stable", "beta", "nightly"))
|
|
22
|
+
parser.add_argument("--release-notes-file", type=Path)
|
|
23
|
+
parser.add_argument("--output", type=Path, default=Path("dist/codex-flow-update.json"))
|
|
24
|
+
parser.add_argument("--no-restart-required", action="store_true", help="declare that Codex restart is not required")
|
|
25
|
+
args = parser.parse_args()
|
|
26
|
+
|
|
27
|
+
version = (ROOT / "VERSION").read_text(encoding="utf-8-sig").strip().lstrip("v")
|
|
28
|
+
tag = args.tag or f"v{version}"
|
|
29
|
+
notes = ""
|
|
30
|
+
if args.release_notes_file and args.release_notes_file.exists():
|
|
31
|
+
notes = args.release_notes_file.read_text(encoding="utf-8-sig").strip()
|
|
32
|
+
|
|
33
|
+
artifacts: dict[str, dict[str, str]] = {}
|
|
34
|
+
pattern = re.compile(rf"^codex-flow-{re.escape(version)}-(.+?)\.(tar\.gz|zip)$")
|
|
35
|
+
for archive in sorted(args.dist.iterdir() if args.dist.exists() else []):
|
|
36
|
+
match = pattern.match(archive.name)
|
|
37
|
+
if not match:
|
|
38
|
+
continue
|
|
39
|
+
checksum_path = archive.with_suffix(archive.suffix + ".sha256")
|
|
40
|
+
if not checksum_path.exists():
|
|
41
|
+
raise SystemExit(f"missing checksum for {archive.name}")
|
|
42
|
+
checksum = checksum_path.read_text(encoding="utf-8").split()[0].strip().lower()
|
|
43
|
+
if not re.fullmatch(r"[0-9a-f]{64}", checksum):
|
|
44
|
+
raise SystemExit(f"invalid checksum for {archive.name}")
|
|
45
|
+
platform_name = match.group(1)
|
|
46
|
+
artifacts[platform_name] = {
|
|
47
|
+
"url": f"https://github.com/{REPO}/releases/download/{tag}/{archive.name}",
|
|
48
|
+
"sha256": checksum,
|
|
49
|
+
"format": "zip" if archive.suffix == ".zip" else "tar.gz",
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if not artifacts:
|
|
53
|
+
raise SystemExit("no release archives found")
|
|
54
|
+
|
|
55
|
+
payload = {
|
|
56
|
+
"schema": 1,
|
|
57
|
+
"version": version,
|
|
58
|
+
"channel": args.channel,
|
|
59
|
+
"published_at": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
|
|
60
|
+
"minimum_updater_version": MINIMUM_UPDATER_VERSION,
|
|
61
|
+
"restart_required": not args.no_restart_required,
|
|
62
|
+
"mandatory": False,
|
|
63
|
+
"release_url": f"https://github.com/{REPO}/releases/tag/{tag}",
|
|
64
|
+
"release_notes": notes,
|
|
65
|
+
"artifacts": artifacts,
|
|
66
|
+
}
|
|
67
|
+
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
68
|
+
args.output.write_text(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
69
|
+
print(args.output)
|
|
70
|
+
return 0
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if __name__ == "__main__":
|
|
74
|
+
raise SystemExit(main())
|