opentui(phase3): launcher integration — HERMES_TUI_ENGINE dual-engine
hermes --tui launches the native OpenTUI engine (Bun) when HERMES_TUI_ENGINE=opentui (env) or display.tui_engine=opentui (config); Ink stays the default and the shipping path is untouched. - _resolve_tui_engine() (env > config > ink); refuses opentui on Windows/Termux (no Bun) -> falls back to ink with a notice. - _make_opentui_argv() -> [bun, src/entry.real.tsx] (no build step). - _bun_bin() with HERMES_BUN override. - Branch at top of _make_tui_argv BEFORE _ensure_tui_node (Bun-only host must not bootstrap Node). - Gate _launch_tui NODE_OPTIONS/--max-old-space-size on engine==ink (Bun is JSC; the V8 flag errors/ignores). Verified end-to-end via tmux: real hermes --tui -> Bun -> OpenTUI -> real Python gateway streamed a real reply. No-flag default still ink.
This commit is contained in:
@@ -272,9 +272,8 @@ def main():
|
||||
# (well above current catalog size) lets the full catalog land in the
|
||||
# index instead of being truncated at an arbitrary build-time limit.
|
||||
SOURCE_LIMITS = {
|
||||
# 0 = unbounded catalog walk (max_items=0 in ClawHubSource). A positive
|
||||
# limit bounds the walk and also enables the interactive 12s budget.
|
||||
"clawhub": 0,
|
||||
# ClawHub had 49,698+ skills as of May 2026; 200k leaves headroom.
|
||||
"clawhub": 200_000,
|
||||
"lobehub": 100_000,
|
||||
"browse-sh": 5_000,
|
||||
"claude-marketplace": 5_000,
|
||||
@@ -298,21 +297,6 @@ def main():
|
||||
# Batch resolve GitHub paths for skills.sh entries
|
||||
all_skills = batch_resolve_paths(all_skills, auth)
|
||||
|
||||
# Collect which sources hit a GitHub API rate limit during the crawl.
|
||||
# github / claude-marketplace / well-known all read api.github.com, so a
|
||||
# rate-limited token zeroes all three at once — surfaced below so the
|
||||
# failure message names the real cause instead of "source returned 0".
|
||||
rate_limited_sources = {
|
||||
name for name, source in sources.items()
|
||||
if getattr(source, "is_rate_limited", False)
|
||||
}
|
||||
if rate_limited_sources:
|
||||
print(
|
||||
" WARNING: GitHub API rate limit hit for: "
|
||||
+ ", ".join(sorted(rate_limited_sources)),
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
# Deduplicate by identifier
|
||||
seen: dict[str, dict] = {}
|
||||
for skill in all_skills:
|
||||
@@ -327,9 +311,25 @@ def main():
|
||||
"browse-sh": 5, "claude-marketplace": 6, "lobehub": 7}
|
||||
deduped.sort(key=lambda s: (source_order.get(s["source"], 99), s["name"]))
|
||||
|
||||
# Build index
|
||||
index = {
|
||||
"version": INDEX_VERSION,
|
||||
"generated_at": datetime.now(timezone.utc).isoformat(),
|
||||
"skill_count": len(deduped),
|
||||
"skills": deduped,
|
||||
}
|
||||
|
||||
os.makedirs(os.path.dirname(OUTPUT_PATH), exist_ok=True)
|
||||
with open(OUTPUT_PATH, "w", encoding="utf-8") as f:
|
||||
json.dump(index, f, separators=(",", ":"), ensure_ascii=False)
|
||||
|
||||
elapsed = time.time() - overall_start
|
||||
file_size = os.path.getsize(OUTPUT_PATH)
|
||||
print(f"\nDone! {len(deduped)} skills indexed in {elapsed:.0f}s")
|
||||
print(f"Output: {OUTPUT_PATH} ({file_size / 1024:.0f} KB)")
|
||||
|
||||
from collections import Counter
|
||||
by_source = Counter(s["source"] for s in deduped)
|
||||
print(f"\nCrawled {len(deduped)} skills in {time.time() - overall_start:.0f}s")
|
||||
for src, count in sorted(by_source.items(), key=lambda x: -x[1]):
|
||||
resolved = sum(1 for s in deduped
|
||||
if s["source"] == src and s.get("resolved_github_id"))
|
||||
@@ -380,46 +380,14 @@ def main():
|
||||
)
|
||||
for line in health_errors:
|
||||
print(line, file=sys.stderr)
|
||||
if rate_limited_sources:
|
||||
print(
|
||||
"\nGitHub API rate limit was hit during this crawl for: "
|
||||
+ ", ".join(sorted(rate_limited_sources))
|
||||
+ ". This is the usual cause of an all-GitHub-tap collapse "
|
||||
"(github / claude-marketplace / well-known dropping to zero "
|
||||
"together). Re-run with a higher-quota GITHUB_TOKEN.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
print(
|
||||
"\nIf the drop is expected (e.g. a hub is genuinely shutting "
|
||||
"down), lower the floor in scripts/build_skills_index.py "
|
||||
"EXPECTED_FLOORS in the same PR.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
# IMPORTANT: do NOT write OUTPUT_PATH on failure. The index file is
|
||||
# gitignored, so a fresh deploy checkout has no copy on disk — leaving
|
||||
# it absent lets website/scripts/extract-skills.py fall back to the
|
||||
# legacy snapshot cache (or skip the unified index) instead of reading
|
||||
# a degenerate file. Writing-then-exiting-2 was the bug that shipped an
|
||||
# index with every GitHub-API source dropped to zero: deploy-site.yml
|
||||
# swallows the exit code with `|| echo non-fatal`, and the partial file
|
||||
# was already on disk for extract-skills to pick up.
|
||||
sys.exit(2)
|
||||
|
||||
# Healthy — only now write the index out for the docs build to consume.
|
||||
index = {
|
||||
"version": INDEX_VERSION,
|
||||
"generated_at": datetime.now(timezone.utc).isoformat(),
|
||||
"skill_count": len(deduped),
|
||||
"skills": deduped,
|
||||
}
|
||||
os.makedirs(os.path.dirname(OUTPUT_PATH), exist_ok=True)
|
||||
with open(OUTPUT_PATH, "w", encoding="utf-8") as f:
|
||||
json.dump(index, f, separators=(",", ":"), ensure_ascii=False)
|
||||
file_size = os.path.getsize(OUTPUT_PATH)
|
||||
print(f"\nDone! {len(deduped)} skills indexed in "
|
||||
f"{time.time() - overall_start:.0f}s")
|
||||
print(f"Output: {OUTPUT_PATH} ({file_size / 1024:.0f} KB)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user