Prototype: operator-declared launchers in launchers.toml (one tap β throwaway Space) - #125
Draft
enieuwy wants to merge 1 commit into
Draft
Prototype: operator-declared launchers in launchers.toml (one tap β throwaway Space)#125enieuwy wants to merge 1 commit into
enieuwy wants to merge 1 commit into
Conversation
The gesture this gives a phone is the one a desktop gets from a herdr popup, and Collie can never show a popup: it has no pane id and is absent from herdr's pane API, so `pane.list`/`pane.read` β everything Collie renders β cannot see it. A pane is the only surface a phone can read, so the ephemerality has to be the pane's own lifecycle. Herdr already drops a tab whose last pane closes and a Space whose last tab closes, so a command that closes its own pane takes the whole Space with it: tap, look, quit, nothing left running. Rows live in `launchers.toml`, the third sibling of `commands.toml` and `keys.toml`, sharing their reader, their mtime-checked live reload and their hold-the-last-good-rows failure posture. A row is `command` plus an optional `label` (defaults to the command's first token) and `cwd` (defaults to home, `~` expanded). A control character in `command` drops the row rather than being stripped: the line is typed verbatim through `pane.send_text`, so a newline would submit a second line the operator never reviewed, and silently rewriting what runs is worse than refusing it. The configured rows are the allowlist. `POST /api/launch` takes a command STRING and runs it only on exact equality with a row, so the client names a row and never supplies a command line; the bridge interpolates nothing. That is why the route exists rather than the client calling /api/workspace and then typing into the new pane itself β doing it there would move the command line to the phone and leave no allowlist to match against. It carries the same write guard, the same session scoping and the same audit posture as the other structural creates, including the fail-closed redaction default: `command` is not added to METADATA_KEYS, and the line still answers who launched what, where, when. Enter is sent literally rather than COLLIE_SUBMIT_KEYS: that setting is the agent-dependent submit sequence for a TUI composer, and this is a bare shell prompt where Enter is the only key that means "run it". A send that fails rolls the Space back, so a launch that did not start cannot leave an empty shell. Two surfaces, because the rows answer two different questions. The dashboard carries a Launch section, folding on the same terms as Spaces and Recent β it is the one section whose height a config file decides, and `flex-wrap` fits two labels per row, so six launchers would put three rows of buttons between the herd you came to read and the navigator below it. Folded, its header still carries the count, which is the reason to unfold. The Space and pane headers carry a rocket that opens the same rows as a sheet. That is the case the dashboard strip cannot serve: you are reading an agent and want a glance at something else, which otherwise costs Home, tap, Back. A sheet row is a full screen width, so it shows the command under the label β the difference between trusting a button and wondering what it runs. Both surfaces render nothing at all when no rows are declared, so an operator who declares none keeps the dashboard and the headers they already had.
Owner
|
Thanks this is a cool addition!! I'm going to check the exact proposal and its shape in the upcoming days and get back at you. |
Contributor
Author
|
Happy to contribute to collie! You've built something great π |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is a prototype more than a proposal β I built it for my own phone, it has been running on my install for a day, and I am posting it mostly to ask whether the shape is right. Happy for it to be closed, or to be told the whole idea belongs in a fork.
What I wanted
On the desktop I have a herdr popup bound to a key that runs a small TUI, I glance at it, and I quit. From the phone I could not have that. As far as I can tell a popup is invisible to Collie by construction β it has no pane id and is not in herdr's pane API, so
pane.list/pane.readcannot see one. A pane is the only surface the phone can read, so the nearest equivalent I could think of was a pane that closes itself: herdr already drops a tab whose last pane closes and a Space whose last tab closes, so a command that closes its own pane leaves nothing behind.What this adds
launchers.toml, next tocommands.tomlandkeys.toml:I deliberately did not reach for a new env var β #109 β #112 settled where operator rows live, and this reuses that machinery rather than reopening it: same
operator-file.tsreader, same mtime-checked live reload, same hold-the-last-good-rows posture, same drop-the-row-never-the-file validation.A tap creates a Space labelled from the row, types the command, sends Enter, and navigates into the fresh pane with the existing
freshPaneflow.POST /api/launchtakes a command string and runs it only on exact equality with a configured row. My reasoning for a route rather than doing it client-side:/api/workspaceplus a reply into the new pane would put the command line on the phone and leave nothing to match against, whereas this way the client can only name a row. Same write guard, same session scoping, same audit posture as the other structural creates βcommandis not added toMETADATA_KEYS, so it redacts underCOLLIE_AUDIT_CONTENT=nonelike any other content-bearing detail.Enter is sent literally rather than
COLLIE_SUBMIT_KEYS, since that is the submit sequence for an agent's composer and this is a bare shell prompt. A failed send rolls the Space back.Screenshots
Rows are
rumen-peek,showy-quota-peek, and alazygitrow that exists only to showcwd.The pane header at 390 px β Find, the launcher, the status badge:
One tap later, and then the part I actually care about: while it runs it is an ordinary Space, and after quitting it is gone.
SPACES (7)while runningSPACES (6)after quitting(Images live on an orphan
assets/launcher-screenshotsbranch in my fork, so they are not in this diff. Say the word and I will re-upload them as attachments instead.)Where I am least sure
commands.toml? I chose separate because a command row addresses an existing pane and has ascope, while a launcher creates the pane, soscopewould be meaningless for it βkeys.tomlfelt like the precedent. Easy to fold in if you disagree./api/launchshould exist at all, per the reasoning above. This is the decision I would most like checked.confirm = truefor launcher rows, though command rows have it. Trivial to add if you want the symmetry.Checks
bun test ./bridge ./scriptsβ 666 pass. Web suite β 114 files / 2393 tests pass. Both typechecks clean. New tests cover the grammar (defaults,~expansion, each drop case, later-row-wins, a non-arraylaunchers, a non-table row among good ones), the route (unlisted command rejected without creating anything; a listed row creates a Space with that label and cwd and types the command plus Enter; a send failure closes the created pane), and the two UI surfaces.Per CLAUDE.md I have left the version files and
CHANGELOG.mdalone.