Skip to content

Commit 1cf856f

Browse files
committed
fix(install): macOS .app bundle when piped through curl|bash
Original script used `sudo` to write into /Applications, but curl|bash has no TTY — sudo couldn't read a password and the .app bundle silently failed to create. User ran the installer, daemora ran fine, but no app icon ever appeared. Two fixes layered: 1. osascript with administrator privileges shows a GUI password dialog that works in non-TTY shells (same trick Homebrew/Anthropic/Cursor installers use). User sees a real macOS auth prompt. 2. If they cancel or osascript isn't available, fall back to ~/Applications/Daemora.app. User-writable, no sudo, indexed by Spotlight + Finder. Only downside: not in Launchpad (only /Applications is). Better than no icon at all. Either way the .app gets created — `mdimport` triggers Spotlight to index it immediately so 'Daemora' is searchable right after install.
1 parent 15ff9dc commit 1cf856f

1 file changed

Lines changed: 73 additions & 8 deletions

File tree

scripts/install.sh

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,14 @@ EOF
147147
chmod +x "$LAUNCHER"
148148

149149
# ── 4. Desktop shortcut ─────────────────────────────────────────────────
150-
if [ "$PLATFORM" = macos ]; then
151-
APP="/Applications/Daemora.app"
152-
log "creating $APP"
153-
if [ ! -w /Applications ]; then SUDO=sudo; else SUDO=""; fi
154-
$SUDO mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
155-
$SUDO cp "$LAUNCHER" "$APP/Contents/MacOS/Daemora"
156-
$SUDO chmod +x "$APP/Contents/MacOS/Daemora"
157-
$SUDO tee "$APP/Contents/Info.plist" >/dev/null <<EOF
150+
# write_macos_app PATH — build the .app bundle at PATH. Caller picks
151+
# the location (/Applications or ~/Applications). Returns 0 on success.
152+
write_macos_app() {
153+
local target="$1"
154+
mkdir -p "$target/Contents/MacOS" "$target/Contents/Resources" || return 1
155+
cp "$LAUNCHER" "$target/Contents/MacOS/Daemora" || return 1
156+
chmod +x "$target/Contents/MacOS/Daemora" || return 1
157+
cat > "$target/Contents/Info.plist" <<EOF || return 1
158158
<?xml version="1.0" encoding="UTF-8"?>
159159
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
160160
<plist version="1.0">
@@ -172,6 +172,71 @@ if [ "$PLATFORM" = macos ]; then
172172
</dict>
173173
</plist>
174174
EOF
175+
return 0
176+
}
177+
178+
if [ "$PLATFORM" = macos ]; then
179+
GLOBAL_APP="/Applications/Daemora.app"
180+
USER_APP="$HOME/Applications/Daemora.app"
181+
INSTALLED_APP=""
182+
183+
# Prefer /Applications because Launchpad indexes it. When piped
184+
# through `curl | bash` there's no TTY for sudo to prompt, so we
185+
# use osascript — it shows a GUI password dialog that works in
186+
# non-TTY shells (same trick Homebrew / Anthropic / Cursor use).
187+
if [ -w /Applications ]; then
188+
log "creating $GLOBAL_APP"
189+
if write_macos_app "$GLOBAL_APP"; then
190+
INSTALLED_APP="$GLOBAL_APP"
191+
fi
192+
elif command -v osascript >/dev/null 2>&1; then
193+
log "creating $GLOBAL_APP — macOS will ask for your password (needed to write to /Applications)…"
194+
PLIST_TMP="$(mktemp -t daemora-plist)"
195+
cat > "$PLIST_TMP" <<'PLIST_EOF'
196+
<?xml version="1.0" encoding="UTF-8"?>
197+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
198+
<plist version="1.0">
199+
<dict>
200+
<key>CFBundleName</key><string>Daemora</string>
201+
<key>CFBundleDisplayName</key><string>Daemora</string>
202+
<key>CFBundleExecutable</key><string>Daemora</string>
203+
<key>CFBundleIdentifier</key><string>ai.daemora.app</string>
204+
<key>CFBundlePackageType</key><string>APPL</string>
205+
<key>CFBundleVersion</key><string>1.0</string>
206+
<key>CFBundleShortVersionString</key><string>1.0</string>
207+
<key>LSMinimumSystemVersion</key><string>11.0</string>
208+
<key>NSHighResolutionCapable</key><true/>
209+
<key>LSUIElement</key><false/>
210+
</dict>
211+
</plist>
212+
PLIST_EOF
213+
if osascript -e "do shell script \"mkdir -p '$GLOBAL_APP/Contents/MacOS' '$GLOBAL_APP/Contents/Resources' && cp '$LAUNCHER' '$GLOBAL_APP/Contents/MacOS/Daemora' && chmod +x '$GLOBAL_APP/Contents/MacOS/Daemora' && cp '$PLIST_TMP' '$GLOBAL_APP/Contents/Info.plist'\" with administrator privileges with prompt \"Daemora needs permission to install its app icon into /Applications.\"" >/dev/null 2>&1; then
214+
INSTALLED_APP="$GLOBAL_APP"
215+
else
216+
log "password prompt cancelled — falling back to ~/Applications (no admin needed)."
217+
fi
218+
rm -f "$PLIST_TMP"
219+
fi
220+
221+
# Fallback path: ~/Applications/Daemora.app. Always user-writable,
222+
# zero sudo, indexed by Spotlight + Finder. Trade-off: Launchpad only
223+
# indexes /Applications, so the user-scoped .app won't appear there —
224+
# everywhere else does.
225+
if [ -z "$INSTALLED_APP" ]; then
226+
mkdir -p "$HOME/Applications"
227+
log "creating $USER_APP (no admin password needed)…"
228+
if write_macos_app "$USER_APP"; then
229+
INSTALLED_APP="$USER_APP"
230+
else
231+
err "failed to create the .app bundle. The launcher at $LAUNCHER still works — run it directly."
232+
fi
233+
fi
234+
235+
# Force Spotlight to index the new bundle so 'Daemora' shows up
236+
# immediately in Spotlight search instead of a few minutes later.
237+
if [ -n "$INSTALLED_APP" ] && command -v mdimport >/dev/null 2>&1; then
238+
mdimport "$INSTALLED_APP" 2>/dev/null || true
239+
fi
175240
else
176241
DESKTOP="$HOME/.local/share/applications/daemora.desktop"
177242
mkdir -p "$(dirname "$DESKTOP")"

0 commit comments

Comments
 (0)