Skip to content

easl 0.2.3

Choose a tag to compare

@github-actions github-actions released this 07 Oct 22:58
· 16 commits to main since this release
7a1898c
  • easl updates itself. A minute after launch and then daily, easl reads easl.sh/latest.json; when a newer version is out, an Update button appears at the top right of every board window, and easl › Check for Updates… checks now (and says when easl is up to date). Update shows "Update to X? easl quits and reopens; your terminals keep running." with Release Notes beside it, then downloads the release into easl's support folder, checks its size and SHA-256 against latest.json, its signature, Team ID and notarization, and its version, quits easl like ⌘Q, swaps the app where it is and reopens it. Any failure leaves the running easl as it was and says why; if the swap itself fails, the old version is put back and the next launch says why. A Homebrew install updates the same way (brew outdated lists easl until brew upgrade --cask easl). Testing: EASL_UPDATE_URL reads another latest.json, and scripts/dev.sh passes it through; development instances never check by themselves.
  • Links in a question open with a click, and its text copies. A web address an agent writes in a question (http://twaldin-home:5199/home?studio, https://…) shows in link color, and a click opens it in a browser tile beside the question, as its context links do; the pointer turns to a hand over it, and VoiceOver lists it as a link. Drag over the question's text to select any part of it, from a link too, and copy it with ⌘C or the context menu's Copy. Number keys, Return, the note field and Esc work as before, with text selected and after. This holds for an answered or closed question's text as well, and at any board or content zoom.
  • Restarting an omp tile that hasn't replied yet works on omp 18.8. omp writes a session's file only once it has a reply, but easl's omp extension reported the session's path from the start, and agent.restart --mode resume (and a reboot's resume) relaunched with omp --resume=<that path>. omp 18.6 opened a fresh session there; omp 18.8 refuses a path with no file (Session "…" not found.) and exits, so a just-started tile, or one after /new, came back as a shell with no agent. The extension now has omp put the session on disk before it reports it, as omp's own ACP clients do, so the path always resumes, on omp 18.6 and 18.8 alike.
  • A restarted tile keeps its session when the relaunch doesn't start. agent.restart --mode resume records the relaunch on the tile (its session, model and thinking) once the old session is gone, but the killed omp could still be shutting down and its release, arriving after that, cleared the record. When the relaunch then failed to start (omp-update's restarts of three idle tiles hit a missing omp on the relaunch's PATH), two of the tiles were left with no session to resume. A release before the relaunched agent has reported is now taken as the killed agent's and ignored, so the tile keeps what it resumes and can be restarted again.
  • A terminal's command finds what its prompt finds, however easl was opened. easl agent spawn --command 'omp …', a resumed agent and agent.restart's relaunch run the command in a login shell (zsh -l -c). That shell reads .zprofile but not .zshrc (bash: a .bashrc that returns when not interactive), and installers such as bun's and nvm's put their directory on PATH there. Opened from a terminal, easl passed on that terminal's PATH. Opened from Finder, the Dock, open or the updater's relaunch, it had launchd's /usr/bin:/bin:/usr/sbin:/sbin, and the command failed with zsh:1: command not found: omp while omp worked at the tile's prompt. The command now runs with the PATH an interactive login shell of yours sets up in the tile's directory, easl's bin still first. easl asks once per directory and asks again when a shell startup file changes. The shell after the command is unchanged. A startup file that hangs or fails holds a start up for at most 5 s, and the command then runs as before. easld's terminals (owned and hosted ones, their resumes after a reboot and their relaunches) ask the same of the host's shell at each start. On a deckbox, node had resolved to /usr/bin/node instead of nvm's.
  • Agents open another Mac's board. easl board.open_remote --host twaldin-work --board brd_… opens that Mac's board in this easl as File › Open Remote… does: a tab of your board window, viewed live over ssh, with your current tab left in front unless --select true. The host is a name the picker shows (a tailnet Mac, a host opened before) or any ssh host; a board already open from that host is returned, never opened twice. Nothing about the board is written on this Mac; only the host joins the picker's recent hosts. An offline host fails unavailable, a board the host doesn't have open not_found.
  • easl metrics shows how long keys waited. A keys line counts the keys typed into terminals and says how long they waited for the board's main thread before reaching the terminal (mean and max over the last minute, max since launch) and how long handing them to the terminal took. A main-thread stall holds every key typed during it and releases them together, late; when keys in a second waited 50 ms or more, one log line says how many and the longest wait. The hand-over ends where Ghostty queues the write for its IO thread; the write itself isn't timed yet.
  • A plain shell on a Linux host finds easld's socket. The TS client, the easl CLI and the Python SDK defaulted to the Mac app's ~/Library/Application Support/Easl/easl.sock everywhere, so on a host that runs easld (a deckbox) easl needed EASL_SOCKET set by hand outside a terminal tile. Off macOS the default is now easld's own socket: $EASL_HOME/easl.sock, else $XDG_STATE_HOME/easl/easl.sock, else ~/.local/state/easl/easl.sock, still only when it exists and still after an explicit value and EASL_SOCKET. macOS is unchanged.
  • A remote board redraws its host-drawn tiles after a reconnect. When the link to the host dropped and came back, the board was read again, but a code, changes, HTML, browser, image or diagram tile whose object hadn't changed kept the drawing it had: a "Not drawn" badge from a render that failed while the link was down, or an image from before the drop. Once both links to the host are back and the board has been read, each of those tiles now asks the host for a fresh drawing, once; ↻ on a tile still asks again.
  • easld runs the terminals of its own boards. Started with --own-terminals (its Linux unit, easld@.service, now sets it), easld starts a terminal's session when it is created on one of its boards without props.host, and ends it, with zmx's log and the reports spooled for it, when the terminal is deleted. So easl agent spawn on a host that runs easld starts a real agent in easld's capped slice with no Mac attached, and easl tell reaches it. The session gets a hosted terminal's variables pointed at easld itself (EASL_SOCKET its socket, so the agent's reports, messages and spool go there; EASL_BOARD_ROOT the board's root there; no cmux variables, even ones easld inherited) and easld's home, the board and the tile as its owner: a session labelled with another home or board is neither taken over nor ended. A batch that fails starts nothing, and a session that can't start or end is logged in the board's history. Off by default, so an easld on a Mac leaves terminals to the app. To turn it on for a host, run scripts/offload-setup.sh <host>, then on the host sudo install -m 0644 ~/.local/share/easl/easld@.service /etc/systemd/system/easld@.service, sudo systemctl daemon-reload (without it the restart keeps the old command line) and sudo systemctl restart easld@<user>.service; the setup script prints these when the installed unit differs. Not yet: a Mac's tile attaching to them (attach by hand: docs/contracts.md "Owned terminals").
  • easld's agents come back after a reboot. An easld that owns its terminals now remembers the boards it opens (open-boards.json in its home; a board already open when you update joins the next time it's opened) and reopens them when it starts. After a reboot each terminal there gets its session again: an agent that reported its session resumes it, with the options of the tile's own command (omp --resume=<id>, claude --resume <id>, codex resume <id>, gemini --resume <id>, opencode --session <id>, as the Mac's tiles resume), and one whose agent exited runs its command. Restarting easld itself starts nothing: sessions that still run are left as they are. A session easld started whose terminal is gone (deleted while easld was down, or one zmx wouldn't end) is ended a minute or so later, and the board's history says so; other homes' and boards' sessions are never touched. The resumed command is checked by easld's and the app's tests against one shared list of cases.
  • easl agent.restart works on easld's own agents. On an easld that owns its terminals, agent.restart of one of them no longer needs a Mac: easld ends the session, waits until zmx no longer lists it, and starts the relaunch in a new one, resuming the agent's session (--mode resume) or starting it fresh (--mode fresh) with its model and thinking setting and your --args, as the Mac's tiles do. It refuses what a Mac's restart refuses (an agent working, a prompt or message it hasn't started, a draft, unless --force), messages queued for the killed agent come back to their senders, and the tile runs the relaunched command from then on, so a reboot resumes the new session; the killed agent's own exit report, arriving late, no longer wipes the session the relaunch resumes. If zmx stops answering, the restart gives up after 3 s instead of holding up every other terminal start and end. agent.list now says whether each of those terminals' sessions runs (live) and gives the agent's pid, or the session's foreground process when the agent reported none. The relaunched command is checked by easld's and the app's tests against one shared list of cases.

What's Changed

  • dev: EASL_DEV_FRAME opens a development instance's window on a given screen, off the user's Space by @twaldin in #57
  • perf-loop: load-tolerant rows (instructions per event, the Mac's load, stretches by main-thread CPU); rusage in seconds; docs by @twaldin in #58
  • perf-sanitize: the leak check reads the output's strings, not its JSON text by @twaldin in #59
  • Clients default to easld's socket off macOS by @twaldin in #61
  • Remote boards: tiles re-render after a reconnect by @twaldin in #62
  • In-app updater: easl › Check for Updates… and a titlebar Update button by @twaldin in #63
  • easld --own-terminals: easld starts and ends the sessions of its own boards' terminals by @twaldin in #65
  • Question tiles: links in the question open with a click, and its text selects and copies by @twaldin in #64
  • perf-loop: --birth-display names a yabai display id, not an index by @twaldin in #68
  • easld reopens the boards it owns and restores their sessions (lifecycle PR2) by @twaldin in #66
  • docs: phase 2 remeasured (#33) by @twaldin in #71
  • easl metrics: how long keys into terminals waited for the main thread by @twaldin in #69
  • omp 18.8: put the session on disk before reporting its path, so restart/resume of a tile with no reply works by @twaldin in #74
  • agent.restart: the killed agent's late release no longer wipes the relaunched tile's session by @twaldin in #75
  • board.open_remote: agents open another Mac's board as File › Open Remote… does by @twaldin in #72
  • Terminal commands run with the user's interactive PATH, however easl was opened by @twaldin in #70
  • easld restarts the agents of the terminals it owns, and lists their sessions (lifecycle PR3) by @twaldin in #73
  • Release 0.2.3 by @twaldin in #76

Full Changelog: v0.2.2...v0.2.3