feat(app): self-contained installer app — frozen CLI + one-click onboard - #75
Open
mikehasa wants to merge 2 commits into
Open
feat(app): self-contained installer app — frozen CLI + one-click onboard#75mikehasa wants to merge 2 commits into
mikehasa wants to merge 2 commits into
Conversation
…ck onboard A non-developer can now install agentacct without Python, pipx, or a terminal. The app embeds a standalone CLI and drives the normal `agentacct onboard` from a one-click screen. Packaging pipeline (packaging/): - freeze-cli.sh freezes the Python CLI into a PyInstaller onedir binary (~0.5s warm start). It is a drop-in for a pip/pipx agentacct: the CLI, the uvicorn/fastapi daemon, the MCP stdio server, and onboard all run frozen. The interpreter-emulation entry (pyinstaller_entry.py) also handles the two Python forms the Claude Code hooks invoke it with (`<cli> -m module`, `<cli> script.py`), so one binary is CLI + hook runner with no change to onboard or existing installs. - build-dmg.sh: freeze -> build app (embeds the CLI) -> sign -> DMG -> notarize. Signing and notarization are env-gated (DEVELOPER_ID / NOTARY_PROFILE), so the same script makes an unsigned DMG today and a signed+notarized one once a Developer ID exists — no code change. - entitlements.plist carries the hardened-runtime entitlements a frozen Python binary needs to run notarized. App: - build-app.sh embeds packaging/dist/agentacct into Contents/Resources/cli when present (dev builds skip it and stay fast). - SetupModel installs the embedded CLI to ~/.local/share/agentacct/cli, writes a ~/.local/bin/agentacct wrapper on PATH, and runs onboard from the installed binary so every hook/MCP config points at the stable installed path. SetupSheet is the one-click screen; a packaged build whose recorder isn't installed offers it once on first launch. Verified end-to-end: the frozen binary runs mcp serve (MCP initialize + tools), the daemon, onboard (writes all client configs), and both hook forms; the install flow writes stable paths; the DMG (29MB) mounts and its embedded CLI runs. Author: mikehasa
…ning script - ProcessRunner: the EOF (empty-read) branch now drains the final unterminated tail once and nils its own handler, instead of leaving the handler installed to re-fire on every empty read (busy-spin + duplicate tail line). terminationHandler still only finishes — buffer stays owned by the readability queue, no cross-queue data race. - build-dmg.sh: the embedded-CLI codesign pass no longer swallows stderr (a lib that won't sign must surface, not fail notarization silently); dropped the ineffective post-DMG app staple (it stapled a stage dir not in the shipped DMG). Author: mikehasa
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.
Ships a self-contained installer app so a non-developer can install agentacct without Python, pipx, or a terminal. The app embeds a frozen standalone CLI and drives the normal
agentacct onboardfrom a one-click screen. No core data/counting paths change.Why
The app is a GUI over the daemon; it cannot replace the CLI. The MCP servers the coding agents connect to (
agentacct mcp serve), the daemon (agentacct serve), and the recording hooks are all the Python CLI. So a machine with no Python needs the CLI as a standalone binary. This freezes it and ships it in the app.Packaging (
packaging/)freeze-cli.sh— PyInstaller onedir freeze (~0.5s warm start; onefile re-extracts 24MB per launch, unusable for a CLI launched by every hook/MCP connection). The frozen binary is a drop-in for a pip/pipxagentacct.pyinstaller_entry.py— the frozen entry. Besides the CLI it emulates the two Python-interpreter forms the Claude Code hooks invoke it with (<cli> -m agentacct.statusline_hook,<cli> <hook>.py), so one binary is CLI + hook runner with zero change to onboard or to existing pip/pipx installs.build-dmg.sh— freeze → build app (embeds CLI) → sign → DMG → notarize. Signing/notarization are env-gated (DEVELOPER_ID/NOTARY_PROFILE): the same script makes an unsigned DMG today and a signed+notarized one once a Developer ID exists — no code change.entitlements.plist— hardened-runtime entitlements a frozen Python binary needs to run notarized.apps/agentacct/Scripts/build-app.shembeds the frozen CLI intoContents/Resources/cli/when present; dev builds skip it and stay fast.App setup flow
SetupModel/SetupSheet:~/.local/share/agentacct/cli/(stable; an app upgrade replaces it in place so every registration stays valid).~/.local/bin/agentacctwrapper on PATH.agentacct onboard --agent auto --yesfrom the installed binary, which registers MCP servers, hooks, and standing instructions and stamps the stable installed path into every config.A packaged build whose recorder isn't installed offers setup once on first launch; a dev build (no embedded CLI) never prompts.
Verified end-to-end
mcp serve(MCP initialize + tools capability), the daemon (HTTP 200),onboard(writes CLAUDE.md / settings.json / .claude.json / hooks), and both hook forms (statusline-m, SessionStartscript.py).build-dmg.shproduces a 29MB DMG; mounting it and running the app's embedded CLI works (--version,mcp serve).Not in scope (blocked on Apple Developer account)
Code signing + notarization — wired and env-gated, runs once
DEVELOPER_ID+NOTARY_PROFILEare set. Until then the DMG is unsigned (first launch needs right-click → Open).