mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-21 16:02:20 +00:00
feat(cli): ship the CLI in every installer and put it on PATH at launch
The `tty7` CLI was built by every release run and thrown away: all four bundle scripts copied only `tty7-app`, and the upload glob covers `dist/`, which the CLI never reached. Nothing put it on PATH either, so the agent-facing half of the product was unreachable from a shipped install. Bundle it on all four platforms, and have the GUI link it up itself rather than hiding the step behind a menu item most people never find. The install has two halves. The environment half prepends the CLI's directory to this process's PATH before the daemon is spawned, so every pane inherits it — that alone makes `tty7` work where agents actually run, writes nothing to disk, and behaves the same everywhere. The on-disk half symlinks into a directory already on PATH (Unix) or appends to HKCU\Environment (Windows), and is allowed to fail. Candidate directories are a fixed list intersected with PATH, not the first writable entry on it: pyenv/rbenv/asdf/mise shim directories sit at the front of PATH on many machines and are writable, and anything dropped there is deleted on the next rehash — silently, days later. Debug builds get the environment half only. `target/debug` holds a `tty7` too, so otherwise a `cargo run` would repoint the developer's real `tty7` at a debug binary, and each isolated dev-verify instance would rewrite the PATH of the machine it is meant to stay away from.
This commit is contained in:
@@ -55,6 +55,13 @@ mkdir -p "$APPDIR/usr/bin"
|
||||
cp "target/${TARGET}/release/tty7-app" "$APPDIR/usr/bin/tty7-app"
|
||||
chmod +x "$APPDIR/usr/bin/tty7-app"
|
||||
|
||||
# The CLI, beside the GUI as everywhere else. Unlike the tarball, an AppImage is
|
||||
# mounted at a fresh /tmp/.mount_XXXX per run, so `core::cli_install` must copy
|
||||
# this onto PATH rather than symlink it — a link into the mount dies the moment
|
||||
# the app exits. That branch keys off $APPIMAGE, which the runtime sets.
|
||||
cp "target/${TARGET}/release/tty7" "$APPDIR/usr/bin/tty7"
|
||||
chmod +x "$APPDIR/usr/bin/tty7"
|
||||
|
||||
# A desktop entry + icon are mandatory AppImage metadata; linuxdeploy places
|
||||
# them and generates AppRun. Icon basename must match the desktop's Icon= key.
|
||||
cat > "$TOOLS/tty7.desktop" <<'DESKTOP'
|
||||
|
||||
@@ -29,9 +29,14 @@ mkdir -p "$STAGE"
|
||||
|
||||
cp "target/${TARGET}/release/tty7-app" "$STAGE/tty7-app"
|
||||
chmod +x "$STAGE/tty7-app"
|
||||
# The CLI ships beside the GUI, which symlinks it onto PATH at launch (see
|
||||
# core::cli_install) by resolving it relative to its own executable.
|
||||
cp "target/${TARGET}/release/tty7" "$STAGE/tty7"
|
||||
chmod +x "$STAGE/tty7"
|
||||
# Release builds keep symbols (thin LTO, no profile strip); drop them here so
|
||||
# the archive isn't ~100 MB of debug info.
|
||||
strip "$STAGE/tty7-app" || echo "⚠️ strip unavailable — shipping unstripped binary"
|
||||
strip "$STAGE/tty7" || true
|
||||
mkdir -p "$STAGE/completions"
|
||||
cp assets/completions/*.json "$STAGE/completions/"
|
||||
cp LICENSE "$STAGE/LICENSE"
|
||||
|
||||
@@ -27,6 +27,13 @@ rm -rf dist
|
||||
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
|
||||
cp "target/${TARGET}/release/tty7-app" "$APP/Contents/MacOS/tty7-app"
|
||||
chmod +x "$APP/Contents/MacOS/tty7-app"
|
||||
# The CLI rides inside the bundle rather than beside it: a DMG is drag-to-
|
||||
# Applications, so anything not in the .app never reaches the user's disk. The
|
||||
# GUI symlinks it onto PATH at launch (see core::cli_install), which is why it
|
||||
# sits next to tty7-app under MacOS/ — that is the directory the GUI resolves
|
||||
# relative to its own executable.
|
||||
cp "target/${TARGET}/release/tty7" "$APP/Contents/MacOS/tty7"
|
||||
chmod +x "$APP/Contents/MacOS/tty7"
|
||||
cp assets/tty7.icns "$APP/Contents/Resources/tty7.icns"
|
||||
# Completion signatures are loaded at runtime (not embedded), resolved relative
|
||||
# to the executable as ../Resources/completions — see terminal::signature.
|
||||
@@ -94,7 +101,17 @@ if [[ -n "$SIGN_ID" && -n "${APPLE_CERTIFICATE:-}" ]]; then
|
||||
</plist>
|
||||
ENT
|
||||
|
||||
# Sign inner-out: the executable first, then the bundle.
|
||||
# Sign inner-out: the executables first, then the bundle. The CLI must be
|
||||
# signed explicitly — notarization rejects a bundle carrying an unsigned
|
||||
# Mach-O, and the outer `codesign "$APP"` does not descend into MacOS/ for
|
||||
# anything but CFBundleExecutable.
|
||||
#
|
||||
# It gets hardened runtime (notarization requires it) but none of the GUI's
|
||||
# entitlements: the JIT and library-validation exemptions exist for gpui's
|
||||
# Metal path, and a CLI that never renders anything has no business holding
|
||||
# them.
|
||||
codesign --force --options runtime --timestamp \
|
||||
--sign "$SIGN_ID" "$APP/Contents/MacOS/tty7"
|
||||
codesign --force --options runtime --timestamp --entitlements "$ENTITLEMENTS" \
|
||||
--sign "$SIGN_ID" "$APP/Contents/MacOS/tty7-app"
|
||||
codesign --force --options runtime --timestamp --entitlements "$ENTITLEMENTS" \
|
||||
|
||||
@@ -21,6 +21,10 @@ Remove-Item -Recurse -Force dist -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Force -Path $Stage | Out-Null
|
||||
|
||||
Copy-Item "target/$Target/release/tty7-app.exe" "$Stage/tty7-app.exe"
|
||||
# The CLI, staged beside the GUI so both the zip and the installer carry it.
|
||||
# `core::cli_install` resolves it relative to tty7-app.exe and puts that
|
||||
# directory on the user's PATH.
|
||||
Copy-Item "target/$Target/release/tty7.exe" "$Stage/tty7.exe"
|
||||
New-Item -ItemType Directory -Force -Path "$Stage/completions" | Out-Null
|
||||
Copy-Item "assets/completions/*.json" "$Stage/completions/"
|
||||
Copy-Item LICENSE "$Stage/LICENSE.txt"
|
||||
|
||||
@@ -58,11 +58,23 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{
|
||||
; it. The user would keep launching the previous version from their pinned icon,
|
||||
; against the same daemon endpoint as the new one. Delete it on upgrade; a fresh
|
||||
; install simply has nothing to remove.
|
||||
;
|
||||
; That name is now reused by the CLI below, which lands at the very same path.
|
||||
; The order saves us: [InstallDelete] runs before [Files], so the stale GUI is
|
||||
; gone before the CLI is written, and what survives is never a mix of the two.
|
||||
; The taskbar pin is the loose end — post-upgrade it points at a CLI, so
|
||||
; clicking it flashes a console instead of opening a window. That is louder
|
||||
; than the bug it replaces (silently running last release's GUI), and the pin
|
||||
; is not ours to rewrite; the Start Menu entry in [Icons] is correct either way.
|
||||
[InstallDelete]
|
||||
Type: files; Name: "{app}\tty7.exe"
|
||||
|
||||
[Files]
|
||||
Source: "{#StageDir}\tty7-app.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
; The CLI. `core::cli_install` adds {app} to the user's PATH at first launch,
|
||||
; so this is not registered as an [Env] change here — the portable zip has no
|
||||
; installer to do it, and one code path serving both is one behaviour to debug.
|
||||
Source: "{#StageDir}\tty7.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#StageDir}\completions\*"; DestDir: "{app}\completions"; Flags: ignoreversion recursesubdirs
|
||||
Source: "{#StageDir}\LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#StageDir}\README.md"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
Reference in New Issue
Block a user