Scripted Tools Know Which Computer They Run On¶
Summary¶
A scripted tool can read ctx.host.os ("macos", "windows" or "linux") and ctx.host.arch
("arm64" or "x64"). Both are null when the tool runs on an Android device. Separately,
exec now finds Windows commands by their bare name, so one argv works on every host. Tools get
no environment-variable reader on ctx, on purpose.
Why we needed it¶
Trailblaze now runs on Windows hosts, and a tool written on a Mac broke there in two ways:
- Commands.
exec(["sdkmanager", "--list"])failed, because Java’s process launcher finds only.exefiles.sdkmanagerissdkmanager.bat. Java also looks in the working directory beforePATH. - Everything else that differs by host, such as path separators and install locations. A tool had no reliable way to ask which OS it was on.
What we built¶
ctx.host.osandctx.host.arch. Both script runtimes (in-process QuickJS and subprocess) get them from the JVM that runs the tool. The Android build reportsnull, since there the JVM is the device itself (os.namereadsLinux).execresolves bare names the way a Windows command prompt does.adbbecomesadb.exeandsdkmanagerbecomessdkmanager.bat. Directories are tried inPATHorder and extensions inPATHEXTorder. Lookup uses the environment the child process will get, so a caller that passes its ownPathpicks the program. Names with a path or an extension pass through unchanged. A bare name thatPATHlacks fails with “not found on PATH”, rather than letting Windows look in the working directory first.
Decisions¶
- On
ctx.host, not a new top-level field. We first builtctx.hostPlatform. Butctx.hostis already where a tool reaches the host (ctx.host.<name>(args)calls a Kotlin host function).ctx.host.os === "windows"reads better and keeps it all in one place.osandarchare now reserved: a host function with either name is skipped at discovery with a warning. nullon a device rather than a guess. On a device there is no host computer, and the device is already described byctx.device.- Two fields, each a closed set.
aarch64andarm64both readarm64;amd64,x86_64andx64all readx64. Any other architecture readsnull, so that the TypeScript types can list every value and a typo fails type-checking. Trailblaze only ships for those two architectures. An x64 JVM on Windows on Arm reportsx64, which is what the programs it starts must be. osis the OS family, not the distro. Ubuntu and Red Hat both readlinux. We added no version numbers or distro names, because nothing needed them. If a tool ever does, the distro belongs in a separate field, sooskeeps meaning the same thing.- No environment variables on
ctx. Three reasons: - Most tools run inside the long-running daemon, so they would see the environment the daemon
started with, not the shell that ran
trailblaze run. A variable exported afterwards stays invisible until the daemon restarts. - A trail that quietly reads machine state passes on one laptop and fails in CI, with nothing in the trail saying why.
- Environments hold secrets, and a one-line reader makes it easy to copy a token into a tool result or the session log.
Each real need already has a path:
- Values a trail needs: --memory / --secret, read through ctx.memory.
- Machine configuration: a host function, so the read lives in one reviewed place.
- Subprocess tools: process.env, as before.
- Last resort: shelling out with an argv chosen by ctx.host.os (printenv NAME, or the
cmd.exe equivalent on Windows).
exec already passes the environment to the commands it runs, so a CLI that reads its own
configuration needs none of these. If a tool ever needs to read the environment directly, the
shape to build is variables declared in the tool’s spec, returned only if declared and treated
as sensitive in logs.
- Refuse batch calls cmd.exe would reinterpret, rather than escape them. Windows starts a .bat
or .cmd file through cmd.exe, which re-reads its path and arguments: %NAME% and !NAME!
expand, and &, |, <, > and ^ act as operators. Escaping arguments reliably for cmd.exe is a well-known
failure area, so exec refuses a batch call with an argument containing any of those
characters, a double quote, or a line break. The error says to write
["cmd", "/c", ...] when cmd.exe’s reading is intended. Plain arguments, including ones with
spaces, run normally.
Known gaps¶
- On a timeout, the host kills the process and every process it started, because a batch file’s real work runs in a child that cmd.exe starts. On Windows the child would otherwise keep the output pipe open, and the timeout would never return. On an Android device only the process itself is killed, because Android has no API that lists a process’s children.
- No CI job runs these tests on Windows. The Windows lookup and the batch-call refusal are unit
tested with explicit
windows = trueinputs on macOS and Linux.