launch
tit.launch ¶
Launch the TI-Toolbox container and open its browser UI.
Uses the same compose specification and container labels as Electron. Installed wheels use BUILTIN_SPEC; tests verify it matches docker-compose.yml.
LaunchError ¶
Bases: RuntimeError
A failure the user can act on; the message is the remedy, not a stack trace.
StackSpec
dataclass
¶
StackSpec(image: str, working_dir: str, init: bool, volumes: tuple[str, ...], environment: tuple[tuple[str, str], ...], ports: tuple[str, ...], healthcheck: tuple[str, ...] = ())
The parts of the compose service this launcher realises.
Values still carry ${VAR} / ${VAR:-default} references; they are
interpolated against :func:build_env when the argv is built, exactly as
shared/composeFile.ts does for the app.
LaunchOptions
dataclass
¶
LaunchOptions(project: str, port: int = DEFAULT_PORT, image: str | None = None, open_browser: bool = True, timeout: float = 180.0, echo: object = print, repo_dir: str = '', server_reload: bool = False, static_dir: str = '', existing: str | None = None, container: str | None = None, session_container: str = '', session_project: str = '')
One launch invocation.
The last three are the dev overrides — the three variables the compose file exposes
as ${TIT_REPO_DIR:-}, ${TIT_SERVER_RELOAD:-} and ${TIT_STATIC_DIR:-}, and
the only difference between a user run and a developer run (see
--dev). They default to off, which is the only correct
answer for a user: repo_dir bind-mounts a host directory over /ti-toolbox, which
is where the image installed its own tit from, so a non-empty value in a packaged or
pip-installed run replaces the toolbox with whatever is at that path.
compose_path ¶
compose_path() -> Path | None
Resolve an explicit compose file, the checkout copy, or the built-in fallback.
Standalone loaders pass their adjacent YAML through TIT_COMPOSE_FILE.
An invalid explicit path fails instead of silently starting a different spec.
Source code in tit/launch.py
load_spec ¶
Parse the compose file into a :class:StackSpec, or return :data:BUILTIN_SPEC.
path defaults to :func:compose_path. Passing a path that does not
exist is an error (the caller asked for that file); omitting it and having
no checkout is not.
Source code in tit/launch.py
parse_compose ¶
The services.tit block of a v3 compose file, as a :class:StackSpec.
Source code in tit/launch.py
interpolate ¶
${VAR} / ${VAR:-default}, from env only (never os.environ).
Reading the ambient environment here is exactly the hazard
shared/compose.ts documents for TIT_REPO_DIR: a stray variable in
the user's shell must not bind-mount a host directory over the image's own
/ti-toolbox.
Source code in tit/launch.py
hash8 ¶
The Electron app's 32-bit project hash (shared/compose.ts#hash8), reproduced.
Not a security boundary — it exists so both launchers derive the same
container name and tit.project label for the same directory, which is
what lets tit launch --status see a container the app started and the
app attach to one this launcher started.
Source code in tit/launch.py
project_name ¶
container_name ¶
default_image ¶
default_image() -> str
Use the compose image default, or an explicit TIT_IMAGE_TAG override.
Runtime package versions can be prereleases with no matching image. The compose spec (and its wheel fallback) owns the image independently of that version.
Source code in tit/launch.py
user_config_dir ¶
user_config_dir() -> Path
~/.config/ti-toolbox, created if absent — mounted at /root/.config/ti-toolbox.
Source code in tit/launch.py
build_env ¶
build_env(*, host_project_dir: str, port: int, token: str, user_config: str, image_tag: str, repo_dir: str = '', static_dir: str = '', server_reload: bool = False) -> dict[str, str]
The map the compose file's ${VAR} references are interpolated from.
Mirrors shared/compose.ts#buildStackEnv. TIT_REPO_DIR and
TIT_SERVER_RELOAD are always present and empty by default for the reason
that file gives: an omitted key would let the user's own shell supply one.
Source code in tit/launch.py
build_run_argv ¶
build_run_argv(spec: StackSpec, env: dict[str, str], *, host_project_dir: str, image: str) -> list[str]
The full docker run argv for one stack — pure, so it can be asserted in a test.
Every volume whose source interpolates to empty is dropped rather than
handed to Docker as an empty bind source (composeFile.ts does the same);
that is how the optional dev-repo mount switches itself off.
Source code in tit/launch.py
session_url ¶
The one URL a browser needs: it trades the token for the session cookie.
tit/server/auth.py mints a session id, sets it as an
HttpOnly; SameSite=Strict cookie and redirects to /, so the token
never has to be typed again and never appears in the address bar afterwards.
Source code in tit/launch.py
find_free_port ¶
First free TCP port at or above preferred on the loopback interface.
Source code in tit/launch.py
probe_container_gpu ¶
Exercise CUDA in the selected image without any project mounts.
Source code in tit/launch.py
require_docker ¶
Fail with the remedy, not a traceback, when the daemon is unreachable.
Source code in tit/launch.py
find_container ¶
The container this project owns, by label — the app's own attach rule.
Both labels are checked: tit.project is a 32-bit hash, so matching it
alone could attach one project's UI to another project's container.
Source code in tit/launch.py
find_running_containers ¶
Discover current and legacy toolbox sessions, including other projects.
Source code in tit/launch.py
choose_existing ¶
Require a deliberate session and action; never infer consent from matching config.
Source code in tit/launch.py
container_credentials ¶
(origin, token) read out of a running container's own environment.
The token is never written to the host filesystem — on attach it comes back from the container that holds it, which is why there is no state file here.
Source code in tit/launch.py
ensure_image ¶
Refresh the mutable release image; retain cached images for offline/dev use.
Source code in tit/launch.py
wait_for_health ¶
Poll /api/health until it answers {"status": "ok"}.
The generous default matches the Electron app's: a cold amd64 start under emulation is slow, and declaring a live stack dead costs the user the whole session.
Source code in tit/launch.py
is_wsl ¶
is_wsl() -> bool
True inside a WSL2 distribution: the Linux side of a Windows host.
WSL puts WSL_DISTRO_NAME (and, with interop on, WSL_INTEROP) in every
process, so those two variables are the whole test. The kernel string is deliberately
not consulted: a Docker Desktop container runs on that same "microsoft" kernel and must
never be mistaken for the host. Being environment-only also lets a plain Linux run stand
in for WSL in tests, and a WSL host stand in for plain Linux (tests/conftest.py
clears the variables). loader.sh's is_wsl applies the identical rule.
Source code in tit/launch.py
translate_project_path ¶
On WSL2, C:\Users\me\project is /mnt/c/Users/me/project; elsewhere unchanged.
Docker Desktop is handed the Linux spelling, and /mnt/c/... already is one, so it
passes straight through. loader.sh applies the identical rule before it validates
the directory, which keeps --print-config byte-identical on WSL too.
Source code in tit/launch.py
resolve_project ¶
The absolute, real path of the project directory, or a message saying what is wrong.
Source code in tit/launch.py
start ¶
start(options: LaunchOptions) -> tuple[str, str]
Ask before reusing or replacing any running toolbox container; return (origin, token).
Source code in tit/launch.py
840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 | |
stop ¶
Stop and remove this project's container. Already gone is success, not failure.
Source code in tit/launch.py
status ¶
{name, state, image, origin, health} for this project's container, or None.
Source code in tit/launch.py
logs ¶
Stream the container's logs to this terminal; returns docker's exit code.
Source code in tit/launch.py
windows_openers ¶
Commands that open url in the Windows default browser from inside WSL, in order.
wslview (wslu) is WSL-aware and honours the user's own setup; PowerShell and
cmd.exe ship with Windows and reach WSL through interop. Only URL characters
reach those two, because each one is a shell of sorts; the session URL is made of
nothing else.
Source code in tit/launch.py
open_in_browser ¶
Open the session URL, saying so either way — a headless host has no browser to open.
On WSL the browser lives on the Windows side: webbrowser would hand the URL to
xdg-open and report success whether or not anything showed it, so the Windows
openers are tried instead and judged by their exit status.