Skip to content

Harden launcher preflight and port handling#357

Open
namabeeru wants to merge 1 commit into
CraftOS-dev:devfrom
namabeeru:codex/launcher-preflight-followup
Open

Harden launcher preflight and port handling#357
namabeeru wants to merge 1 commit into
CraftOS-dev:devfrom
namabeeru:codex/launcher-preflight-followup

Conversation

@namabeeru

Copy link
Copy Markdown
Contributor

Summary

  • Reintroduce the runtime-preflight and launcher-hardening parts from Defer provider SDK imports and harden startup launchers #347 after the model-factory import fix landed separately in Defer OpenAI/Anthropic SDK imports in model factory #348.
  • Make dependency probing fail open when the probe is inconclusive, while still exiting early when missing runtime dependencies are genuinely confirmed.
  • Derive launcher and shortcut URLs from --frontend-port / --backend-port instead of hardcoding the backend port.
  • Centralize the startup ready marker so craftbot.py, run.py, and installer/api.py share the same value.

Response to review feedback

@korivi-CraftOS this addresses the two follow-up items from #347:

  • runtime_preflight.py: probe infrastructure failures now warn and continue. TimeoutExpired, subprocess exceptions, nonzero probe exits, missing sentinel output, malformed JSON, and non-list JSON are all treated as inconclusive. Startup only exits when the probe succeeds and returns a concrete list of missing imports.
  • 60s timeout rationale: I kept it as a conservative upper bound for slower conda/mamba startup plus import-heavy dependency checks. The important change is that a timeout is now non-fatal and only prevents the probe from hanging startup indefinitely.
  • craftbot.py: frontend/backend URLs now derive from the passed port flags, including both --backend-port 8123 and --backend-port=8123 forms. The macOS shortcut uses the derived backend URL for its health check.
  • Ready marker duplication: CRAFTBOT IS READY now lives in top-level startup_constants.py, which both the installer and agent-side code can import.

Validation

  • /usr/local/bin/python3.10 -m pytest tests/test_run_dependency_check.py tests/test_craftbot_service.py tests/test_startup_constants_usage.py tests/test_model_factory.py -q -> 15 passed
  • Focused PR suite looped 10/10 successfully before opening this PR.
  • /usr/local/bin/python3.10 -m py_compile craftbot.py run.py app/main.py app/runtime_preflight.py installer/api.py startup_constants.py tests/test_craftbot_service.py tests/test_run_dependency_check.py tests/test_startup_constants_usage.py -> passed
  • git diff --check origin/main...HEAD -> passed
  • Bad-runtime loop with Homebrew Python 3.14.5: run.py, python -m app.main, and craftbot.py start --cli all exited nonzero with clear missing-dependency messaging; no traceback, false CRAFTBOT STARTED, or stale PID.
  • Good-runtime preflight loop with /usr/local/bin/python3.10: 10/10 successful.
  • Real launcher smoke with /usr/local/bin/python3.10: 3/3 start/status/stop cycles successful.

Repo-wide checks still have unrelated existing failures outside this PR's changed files:

  • /usr/local/bin/python3.10 -m pytest tests -q -> 101 passed, 4 failed, 16 deselected; the failures are in tests/test_trigger_router_and_parking.py because FakeLLM.generate_response_async() does not accept prompt_name.
  • /usr/local/bin/python3.10 -m pytest -q -> collection errors in generated/template Living UI test directories (models import and tests.conftest import-path mismatch).

@namabeeru namabeeru changed the base branch from main to dev July 3, 2026 13:43
@namabeeru

Copy link
Copy Markdown
Contributor Author

Retargeted this PR to dev to match the contributing guide (dev -> staging -> main).

Comment thread craftbot.py
"#!/bin/sh\n"
f"cd {shlex.quote(BASE_DIR)} || exit 1\n"
f"if curl -fsS {shlex.quote(browser_url)} >/dev/null 2>&1 "
f"&& curl -fsS {shlex.quote(backend_url)} >/dev/null 2>&1; then\n"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Problem: The -f flag makes curl "fail" (exit non-zero) whenever the server replies with a 4xx status. But CraftBot's backend normally returns a 4xx on a plain GET /, even when it's perfectly healthy, run.py itself treats anything under 500 as "backend is up".

Result: The health check thinks the bot is down when it's actually running. So double-clicking the desktop shortcut restarts the whole service instead of just opening the browser.

Fix: Drop -f so any HTTP response counts as "up" (matching run.py), or point the check at a route that actually returns 200.

Comment thread craftbot.py
content = f"#!/bin/sh\nopen '{BROWSER_URL}'\n"
backend_url = _backend_url(extra_args)
restart_args = _port_args(extra_args)
start_cmd = shlex.join([_python_exe(), "craftbot.py", "start"] + restart_args)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Problem: This assumes craftbot.py sits next to Python. In a frozen/installed macOS build, there's no craftbot.py file it's baked into the binary so the restart command has nothing to run.

Why it matters: Because of Comment on Line 878, this broken restart branch is exactly the one that fires when the bot is already running. So the two bugs stack: wrong branch chosen, and that branch fails.

Fix: Only use craftbot.py start when running from source (not IS_FROZEN); in a frozen build, call the installed executable instead.

@ahmad-ajmal

Copy link
Copy Markdown
Collaborator

Could you also point the PR to V1.4.1? Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants