Releases: EstebanForge/construct-cli
Releases · EstebanForge/construct-cli
Release list
The Construct CLI 1.11.0
[1.11.0] - 2026-07-10
Added
- Seccomp Relaxation (
disable_seccomp): New[sandbox]config option that emitssecurity_opt: [seccomp:unconfined]in the generateddocker-compose.override.yml, enabling headless browser automation inside the container. Docker's default seccomp filter blocks syscalls Chrome/Chromium's multi-process CDP backend needs (clone3with namespace flags,seccomp(2)BPF install,ptrace), causing every persistent browser launch (agent-browser, Playwright, CDP extensions) to die withTrace/breakpoint trap. Default-off; opt in with[sandbox] disable_seccomp = true. The toggle is hash-tracked so flipping it regenerates the override, andconstruct sys doctorsurfaces when it is enabled (with a security note). Requiresconstruct build+ container restart to take effect. Seedocs/SECURITY.mdfor the tradeoff (removes a kernel-level syscall-restriction layer).
The Construct CLI 1.10.0
[1.10.0] - 2026-07-03
Added
- Host Exec Bridge (
host_binaries): New[sandbox]config option that lets the agent invoke selected host-only binaries from inside the container, where they actually execute on the host machine (not in the sandbox). The agent sees them on PATH and calls them normally; a shim transparently proxies each call to a host-side bridge that runs the real binary as your host user and streams stdout/stderr back with exit codes preserved.- Config:
[sandbox] host_binaries = ["wicket"]. Off when empty (no bridge starts, zero attack surface). - Security model: each listed binary runs on the host with full container-controlled argv. The bridge is token-gated (per-session 32-byte bearer,
X-Construct-Exec-Token), allowlist-pinned (unknownargv[0]fails closed with 403), and resolves each binary to an absolute host path at startup (no per-requestexec.LookPath, no PATH-poisoning). Only list binaries you trust with container-controlled argv — declaring e.g.dockergrants effective host root to the agent. A startup banner (⚠ host exec enabled: …) confirms when active; seedocs/HOST-EXEC.mdfor the full threat model. - No interactive TTY by design: there is no controlling terminal; pipe stdin (one-shot) works, interactive prompts do not. Pass
--no-interactive/--json/--yesflags where the listed binary offers them. Future TTY support, if ever needed, is an HTTP-upgrade transport on the same bridge (not a socat rebuild). - Daemon-safe: symlinks are reconciled host-side in
Prepare()(nodocker exec), so toggling the list takes effect on the nextconstructinvocation without a daemon restart. Requiresconstruct buildafter first enabling (the shim is baked into the image). - Per-call timeout: 30-minute hard cap (override via
CONSTRUCT_HOST_EXEC_TIMEOUT); bridge-initiated kills surface as exit code124(thetimeout(1)convention). - Audit log: every invocation is recorded at
~/.config/construct-cli/logs/host_exec.log(timestamp, argv, resolved path, exit code, duration).construct sys doctorwarns on misconfig (listed binary missing on host PATH, stale shim manifest, shim not yet baked into the image).
- Config:
The Construct CLI 1.9.4
[1.9.4] - 2026-06-20
Added
- SSH Identity Pinning (
ssh_pin_identities): New[sandbox]config option that pins one SSH identity per host to avoidToo many authentication failureswhen the forwarded agent holds many keys. Two entry forms:"host=keyname"(simple) and"alias=hostname=keyname"(multi-account on same service). Pinned identities are serialized intoCONSTRUCT_SSH_PIN_IDENTITIESand consumed byensure_ssh_config()in the container entrypoint, which emitsIdentitiesOnly yes+ the named key for each configured host. - Per-Session SSH Proxy Sockets: Each
constructprocess now uses its own socat socket (/home/construct/.ssh/agent.<pid>.sock) rather than a shared/home/construct/.ssh/agent.sock. Concurrent sessions sharing one daemon no longer overwrite each other's agent proxy.Teardown()cleans up only the calling session's socket. - SSH Bridge Regression Tests: Table-driven tests for
sshPinIdentitiesEnv(10 cases including malformed-entry guards),sshProxySockForPID(distinct PIDs, stable same PID, exact path), bridge no-agent guard, full proxy integration, and Bitwarden vault lock/unlock recycle case. - Entrypoint
ensure_ssh_configBash Tests: Newinternal/templates/entrypoint_ssh_config_test.goextracts and executes the realensure_ssh_configshell function in an isolatedHOME. Seven cases: no hardcodedIdentityAgent, physical-keys-only, agent pin with.pub, alias three-field form, physical-key fallback, pin skipped when key missing, opt-out (# construct-managed: false) respected.
Fixed
- SSH Agent Forwarding in Container:
ensure_ssh_config()no longer emits phantomIdentityFilepaths for keys that do not exist on disk, eliminating false "no SSH key" reports from agents. Hardcoded~/.ssh/defaultand~/.ssh/personalare only emitted when the files are actually present. IdentityAgentOverride Removed: TheHost *block no longer writesIdentityAgent ~/.ssh/agent.sock, which was overriding the per-sessionSSH_AUTH_SOCKenv var and routing all agent requests to whichever session last wrote that socket. SSH now usesSSH_AUTH_SOCKdirectly, injected per-session by the engine.- Error Context on SSH Proxy Helpers (Go Mistakes #49): Both
ensureDaemonSSHProxyandwaitForDaemonSSHProxynow wrap errors with%w, including container name, socket path, and port, enablingerrors.Is/errors.Asunwrapping and actionable messages in multi-session scenarios.
The Construct CLI 1.9.3
[1.9.3] - 2026-06-18
Fixed
- Pi Update No Longer Updates Extensions: Pi changed the bare
pi updateto update only pi (self), withpi update --allrequired to update pi and its extensions together. Construct's system update (ct sys update) was still calling barepi update, so pi extensions were silently no longer updated on each run. All three invocation sites now usepi update --all: the generated topgrade config (packages.go), the statictopgrade.tomltemplate, and the manualupdate-all.shfallback.
The Construct CLI 1.9.2
[1.9.2] - 2026-06-18
Removed
- Worktrunk: Removed
worktrunkfrom the default Cargo install list (packages.toml). Upstreamworktrunk 0.59.0fails to build from a registry tarball (cargo install) because itsvergen-gitclbuild script cannot computeVERGEN_GIT_DESCRIBEoutside a git worktree, andsrc/cli/mod.rsuses a hard compile-timeenv!("VERGEN_GIT_DESCRIBE"). The Topgrade/Cargo update step aborted on every update run. The historical 0.11.1 addition entry is retained for accuracy.
The Construct CLI 1.9.1
[1.9.1] - 2026-06-15
Added
- SSH Agent Reachability Check in
sys doctor: The existing "SSH Agent" check only verifiedSSH_AUTH_SOCKwas set, which a stale/recycled socket (e.g. Bitwarden/1Password after lock) passes while every in-containerssh/gitop fails silently. The check now probes the agent directly viassh-add -land reports reachable (with key count), reachable-but-no-keys, not-reachable, or unknown (ssh-add missing). Extracted into a unit-testedcheckSSHAgenthelper.
Fixed
- Concurrent Setup Deadlock: Two
docker compose runsetups running at once (e.g. a user retrying because setup looked stuck) share the same home bind-mount and both write into npm's shared globalnode_modules+ cache, deadlocking on npm's cache/lock and hanging indefinitely. Setup now takes a non-blocking exclusiveflockon~/.config/construct-cli/setup.lockbefore spawning the compose run; a second instance refuses to start and tells the user to wait. The lock auto-releases on process exit, so no manual cleanup is needed after a crash. - Redundant npm Reinstalls During Setup: Every
npm install -gin the generated setup script used--force, re-fetching and re-linking all global packages on every setup run. This made setup slow enough to look stuck (the trigger for the retry that caused the deadlock above).--forceis dropped from the setup path so npm skips packages already at the target version.update-all.shstill uses--forcefor explicit@latestupgrades (intentional). - SSH Agent Bridge Silently Failed on Stale Port: The in-container
socatSSH-agent proxy (started byentrypoint.shat container creation) baked the host bridge port into its argv. The host bridge (StartSSHBridge) bound a random ephemeral port that changed across CLI invocations, so the stalesocatkept pointing at a dead host port and everyssh/gitop failed with "communication with agent failed". The v1.8.15 "restartsocaton each exec" fix did not actually work:ensureDaemonSSHProxybackgroundssocat(returns near-instantly, almost never errors) and both exec sites discarded the result ofwaitForDaemonSSHProxy(gated onif err == nil). The liveness probe wastest -S(socket file exists), which a leftover socket or stalesocatpasses. Now both exec sites check and log both errors, and the probe is a realUNIX-CONNECT(socket actually accepting connections). - Topgrade Brew Step Crash on Linux: The
homebrew/casktap (auto-tapped underHOMEBREW_NO_INSTALL_FROM_APImode) breaksbrew upgradeon Linux because casks use arch-conditionalsha256 arm:/intel:that resolves to nil on non-macOS systems (e.g.Casks/0/0-ad), aborting the whole update run and stalling all formula upgrades. Bothupdate-all.shandentrypoint.shnow defensively untaphomebrew/caskon Linux (idempotent). Casks are macOS-only and non-functional on the Linux box.
Changed
- Deterministic SSH Bridge Port per Box:
StartSSHBridgenow derives a stable TCP port from the box identity (hash of the container name) instead of an ephemeral random port, with fallback to ephemeral on bind failure. The same box now maps to the same host port across invocations, so a stalesocatbaked at container creation keeps pointing at the right port instead of aging out. Band sits below the Linux ephemeral range (32768+) to reduce OS collisions. Correctness is still guaranteed by the per-execsocatrestart regardless of port strategy.
The Construct CLI 1.9.0
[1.9.0] - 2026-06-10
Added
- Non-interactive Container Exec: New
construct sys exec -- <command>command allows running a single command inside a running Construct container without attaching to an interactive shell. Designed for LLM agents operating in headless environments that need to execute commands inside the container and capture output. Streams stdout/stderr separately to the host, returns the container process exit code. Supports both daemon and CWD-scoped containers. Requires a running container (start withconstruct sys shellorconstruct sys daemon start). - Daemon Name Constant: Canonical
DaemonNameconstant ininternal/constants/constants.goreplaces scattered string literals across agent engine and sys packages. - Container Naming Export:
CwdContainerName()moved frominternal/agent(unexported) tointernal/runtime(exported) for cross-package reuse. - Non-interactive Exec Primitive:
ExecNonInteractiveStream()ininternal/runtime/runtime.goexecutes commands in running containers without TTY allocation, streaming stdout/stderr separately, returning real exit codes.
Changed
MapDaemonWorkdirandReadKeyringEnvexported from agent package for reuse bysys exec.- Smart Migration Rebuilds: Container image rebuilds are now gated by per-template hash tracking. Version bumps that only change Go code (no template changes) skip the rebuild entirely, reducing update time from ~2 minutes to ~2 seconds. Templates are classified into tiers: image-baked (Dockerfile, entrypoint.sh, etc.) trigger a full rebuild; runtime-only (docker-compose.yml, agent-patch.sh) trigger a deferred restart; no changes means no rebuild.
The Construct CLI 1.8.15
[1.8.15] - 2026-06-03
Changed
- Cross-Platform SSH Agent Bridge: Replaced macOS-only SSH agent forwarding with a TCP bridge that works on both macOS and Linux. On macOS the bridge binds
127.0.0.1(Docker Desktop routes it); on Linux it binds0.0.0.0so containers reach it viahost.docker.internal. Removed directSSH_AUTH_SOCKsocket mounts and permission-fixing logic from entrypoint and compose overrides. - Dynamic SSH Agent Socket Re-reading: The TCP bridge now re-reads
SSH_AUTH_SOCKper connection, with up to 3 retry attempts (100ms backoff). Handles agents like Bitwarden that recycle socket paths on vault lock/unlock. - Daemon SSH Proxy Restart: Running containers now get their socat proxy restarted with the current bridge port on each
exec, preventing stale-port failures across sessions.
Fixed
- NPM Package Setup Failures: Added
--forceflag to global npm package installs and upgrades during construct provisioning. Prevents setup crashes caused by pre-existing symlink conflicts (e.g.EEXISTconflicts during@kilocode/cliinstallation) and cascadingtar TAR_ENTRY_ERROR ENOENTextraction errors (which blocked the installation of thepipackage).
The Construct CLI 1.8.14
[1.8.14] - 2026-06-02
Added
- Global Gitignore Mount: User's global gitignore file is now automatically mounted (read-only) into the container at
/home/construct/.config/git/ignore. Detects the file from four locations in priority order:git config core.excludesFile,$XDG_CONFIG_HOME/git/ignore,~/.gitignore, and~/.gitignore_global. Tilde paths and spaces in paths are handled correctly. Mount is included in the override hash for proper cache invalidation.
The Construct CLI 1.8.13
[1.8.13] - 2026-06-01
Added
- Global Gitignore Mount: User's global gitignore file is now automatically mounted (read-only) into the container at
/home/construct/.config/git/ignore. Detects the file from four locations in priority order:git config core.excludesFile,$XDG_CONFIG_HOME/git/ignore,~/.gitignore, and~/.gitignore_global. Tilde paths and spaces in paths are handled correctly. Mount is included in the override hash for proper cache invalidation.