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:
alt-glitch 2026-06-08 11:11:54 +00:00
parent 24f74eb888
commit 2bd9c9b881
741 changed files with 17733 additions and 79889 deletions

View file

@ -1,50 +0,0 @@
#!/usr/bin/env python3
"""Generate the Automation Blueprints catalog JSON for the docs site.
Mirrors ``extract-skills.py``: imports the single-source-of-truth blueprint
definitions from ``cron/blueprint_catalog.py`` and emits a flat JSON array the
docs page renders into cards (description, schedule, copy-paste slash command,
and a ``hermes://`` "Send to App" deep-link).
Output: ``website/static/api/automation-blueprints-index.json`` (served at
``/docs/api/automation-blueprints-index.json``). Run automatically by
``website/scripts/prebuild.mjs`` before ``npm start`` / ``npm run build``.
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
# Repo root = two levels up from website/scripts/.
REPO_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(REPO_ROOT))
OUTPUT = REPO_ROOT / "website" / "static" / "api" / "automation-blueprints-index.json"
def build_index() -> list:
from cron.blueprint_catalog import CATALOG, blueprint_catalog_entry
return [blueprint_catalog_entry(r) for r in CATALOG]
def main() -> int:
try:
index = build_index()
except Exception as e: # pragma: no cover - import/build failure
# Match extract-skills.py's resilience: write an empty array so the
# docs build never hard-fails on a generator hiccup.
sys.stderr.write(f"extract-automation-blueprints: {e}; writing empty index\n")
index = []
OUTPUT.parent.mkdir(parents=True, exist_ok=True)
with open(OUTPUT, "w", encoding="utf-8") as f:
json.dump(index, f, separators=(",", ":"))
sys.stderr.write(f"extract-automation-blueprints: wrote {len(index)} blueprints -> {OUTPUT}\n")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -31,7 +31,7 @@ SKILL_SOURCES = [
# Pages the user had previously hand-written in user-guide/skills/.
# We leave these alone (they get first-class sidebar treatment separately).
HAND_WRITTEN = {"google-workspace.md"}
HAND_WRITTEN = {"godmode.md", "google-workspace.md"}
_FENCE_RE = re.compile(r"^(?P<indent>\s*)(?P<fence>```+|~~~+)", re.MULTILINE)
@ -583,7 +583,7 @@ def build_sidebar_items(entries: list[tuple[dict[str, Any], dict[str, Any]]]) ->
Structure:
Skills
(hand-written pages first: google-workspace)
(hand-written pages first: godmode, google-workspace)
Bundled
apple
apple-apple-notes

View file

@ -31,7 +31,6 @@ const scriptDir = dirname(fileURLToPath(import.meta.url));
const websiteDir = resolve(scriptDir, "..");
const extractScript = join(scriptDir, "extract-skills.py");
const llmsScript = join(scriptDir, "generate-llms-txt.py");
const cronBlueprintsScript = join(scriptDir, "extract-automation-blueprints.py");
const outputFile = join(websiteDir, "static", "api", "skills.json");
const unifiedIndexFile = join(websiteDir, "static", "api", "skills-index.json");
const UNIFIED_INDEX_URL =
@ -139,7 +138,3 @@ if (!existsSync(extractScript)) {
// 2) llms.txt + llms-full.txt — agent-friendly docs entrypoints. Non-fatal.
runPython(llmsScript, "generate-llms-txt.py");
// 3) automation-blueprints-index.json — Automation Blueprints catalog page. Non-fatal; the page
// renders an empty state if the generator can't run.
runPython(cronBlueprintsScript, "extract-automation-blueprints.py");