Skip to content

Add macOS Fleet-maintained apps (A) - #47536

Merged
allenhouchins merged 2 commits into
mainfrom
new-mac-fmas-a
Jun 12, 2026
Merged

Add macOS Fleet-maintained apps (A)#47536
allenhouchins merged 2 commits into
mainfrom
new-mac-fmas-a

Conversation

@allenhouchins

@allenhouchins allenhouchins commented Jun 12, 2026

Copy link
Copy Markdown
Member

Adds 43 Fleet-maintained app(s) whose cask token starts with 'A': inputs, outputs, app icons (TSX + website PNG), and the matching index.ts and apps.json entries.

Summary by CodeRabbit

  • New Features
    • Added support for 50+ macOS applications including Acorn, Affinity Suite, AirDroid, Anytype, Audio Hijack, and more
    • Applications now include automated installation and management capabilities with version tracking

Adds 43 Fleet-maintained app(s) whose cask token starts with 'A':
inputs, outputs, app icons (TSX + website PNG), and the matching
index.ts and apps.json entries. Split out from #47121.
@codecov

codecov Bot commented Jun 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 50.00000% with 37 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.17%. Comparing base (2ad7671) to head (f1946fa).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...twarePage/components/icons/ABetterFinderRename.tsx 50.00% 1 Missing ⚠️
...tend/pages/SoftwarePage/components/icons/Acorn.tsx 50.00% 1 Missing ⚠️
...pages/SoftwarePage/components/icons/Activedock.tsx 50.00% 1 Missing ⚠️
...es/SoftwarePage/components/icons/Activitywatch.tsx 50.00% 1 Missing ⚠️
...end/pages/SoftwarePage/components/icons/Actual.tsx 50.00% 1 Missing ⚠️
...nd/pages/SoftwarePage/components/icons/Adguard.tsx 50.00% 1 Missing ⚠️
...end/pages/SoftwarePage/components/icons/Adlock.tsx 50.00% 1 Missing ⚠️
.../SoftwarePage/components/icons/AdvancedRenamer.tsx 50.00% 1 Missing ⚠️
...SoftwarePage/components/icons/AffinityDesigner.tsx 50.00% 1 Missing ⚠️
...oftwarePage/components/icons/AffinityDesigner1.tsx 50.00% 1 Missing ⚠️
... and 27 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #47536      +/-   ##
==========================================
- Coverage   67.18%   67.17%   -0.01%     
==========================================
  Files        3548     3591      +43     
  Lines      228890   228976      +86     
  Branches    11909    11909              
==========================================
+ Hits       153782   153825      +43     
- Misses      61264    61307      +43     
  Partials    13844    13844              
Flag Coverage Δ
frontend 57.92% <50.00%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@allenhouchins
allenhouchins marked this pull request as ready for review June 12, 2026 19:33
@allenhouchins
allenhouchins requested a review from a team as a code owner June 12, 2026 19:33
Copilot AI review requested due to automatic review settings June 12, 2026 19:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR expands Fleet’s macOS Fleet-maintained apps catalog for Homebrew casks starting with “A” by adding new app manifests (inputs + generated outputs) and wiring corresponding software icons into the Software page icon map.

Changes:

  • Added many new Fleet-maintained app input manifests under ee/maintained-apps/inputs/homebrew/ (name, unique_identifier, token, installer format, categories).
  • Added corresponding generated macOS outputs under ee/maintained-apps/outputs/*/darwin.json (versioning queries, installer URLs, install/uninstall script refs).
  • Updated frontend/pages/SoftwarePage/components/icons/index.ts to import new icon components and map additional software names to icons.

Reviewed changes

Copilot reviewed 124 out of 174 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
frontend/pages/SoftwarePage/components/icons/index.ts Imports new icon components and maps additional “A*” software names to their icons.
ee/maintained-apps/outputs/a-better-finder-rename/darwin.json Adds maintained-app output (installer metadata + scripts) for A Better Finder Rename.
ee/maintained-apps/outputs/acorn/darwin.json Adds maintained-app output for Acorn.
ee/maintained-apps/outputs/activedock/darwin.json Adds maintained-app output for ActiveDock.
ee/maintained-apps/outputs/activitywatch/darwin.json Adds maintained-app output for ActivityWatch.
ee/maintained-apps/outputs/actual/darwin.json Adds maintained-app output for Actual.
ee/maintained-apps/outputs/advanced-renamer/darwin.json Adds maintained-app output for Advanced Renamer.
ee/maintained-apps/outputs/affinity-designer/darwin.json Adds maintained-app output for Affinity Designer 2.
ee/maintained-apps/outputs/affinity-designer@1/darwin.json Adds maintained-app output for Affinity Designer (v1).
ee/maintained-apps/outputs/affinity-photo/darwin.json Adds maintained-app output for Affinity Photo 2.
ee/maintained-apps/outputs/affinity-photo@1/darwin.json Adds maintained-app output for Affinity Photo (v1).
ee/maintained-apps/outputs/affinity-publisher/darwin.json Adds maintained-app output for Affinity Publisher 2.
ee/maintained-apps/outputs/affinity-publisher@1/darwin.json Adds maintained-app output for Affinity Publisher (v1).
ee/maintained-apps/outputs/airbuddy/darwin.json Adds maintained-app output for AirBuddy.
ee/maintained-apps/outputs/airdroid/darwin.json Adds maintained-app output for AirDroid.
ee/maintained-apps/outputs/airparrot/darwin.json Adds maintained-app output for AirParrot.
ee/maintained-apps/outputs/airserver/darwin.json Adds maintained-app output for AirServer.
ee/maintained-apps/outputs/airtable/darwin.json Adds maintained-app output for Airtable.
ee/maintained-apps/outputs/airy/darwin.json Adds maintained-app output for Airy.
ee/maintained-apps/outputs/akiflow/darwin.json Adds maintained-app output for Akiflow.
ee/maintained-apps/outputs/alcove/darwin.json Adds maintained-app output for Alcove.
ee/maintained-apps/outputs/alloy/darwin.json Adds maintained-app output for Alloy.
ee/maintained-apps/outputs/alt-tab/darwin.json Adds maintained-app output for AltTab.
ee/maintained-apps/outputs/altair-graphql-client/darwin.json Adds maintained-app output for Altair GraphQL Client.
ee/maintained-apps/outputs/amadeus-pro/darwin.json Adds maintained-app output for Amadeus Pro.
ee/maintained-apps/outputs/amadine/darwin.json Adds maintained-app output for Amadine.
ee/maintained-apps/outputs/amethyst/darwin.json Adds maintained-app output for Amethyst.
ee/maintained-apps/outputs/amie/darwin.json Adds maintained-app output for Amie.
ee/maintained-apps/outputs/angry-ip-scanner/darwin.json Adds maintained-app output for Angry IP Scanner.
ee/maintained-apps/outputs/antinote/darwin.json Adds maintained-app output for Antinote.
ee/maintained-apps/outputs/anydo/darwin.json Adds maintained-app output for Any.do.
ee/maintained-apps/outputs/anytype/darwin.json Adds maintained-app output for Anytype.
ee/maintained-apps/outputs/apidog/darwin.json Adds maintained-app output for Apidog.
ee/maintained-apps/outputs/app-fair/darwin.json Adds maintained-app output for App Fair.
ee/maintained-apps/outputs/appium-inspector/darwin.json Adds maintained-app output for Appium Inspector GUI.
ee/maintained-apps/outputs/applite/darwin.json Adds maintained-app output for Applite.
ee/maintained-apps/outputs/asset-catalog-tinkerer/darwin.json Adds maintained-app output for Asset Catalog Tinkerer.
ee/maintained-apps/outputs/atext/darwin.json Adds maintained-app output for aText.
ee/maintained-apps/outputs/audio-hijack/darwin.json Adds maintained-app output for Audio Hijack.
ee/maintained-apps/outputs/axure-rp/darwin.json Adds maintained-app output for Axure RP.
ee/maintained-apps/outputs/aviatrix-vpn-client/darwin.json Adds maintained-app output for Aviatrix VPN Client.
ee/maintained-apps/inputs/homebrew/a-better-finder-rename.json Adds Homebrew input manifest for A Better Finder Rename.
ee/maintained-apps/inputs/homebrew/acorn.json Adds Homebrew input manifest for Acorn.
ee/maintained-apps/inputs/homebrew/activedock.json Adds Homebrew input manifest for ActiveDock.
ee/maintained-apps/inputs/homebrew/activitywatch.json Adds Homebrew input manifest for ActivityWatch.
ee/maintained-apps/inputs/homebrew/actual.json Adds Homebrew input manifest for Actual.
ee/maintained-apps/inputs/homebrew/adguard.json Adds Homebrew input manifest for AdGuard.
ee/maintained-apps/inputs/homebrew/adlock.json Adds Homebrew input manifest for AdLock.
ee/maintained-apps/inputs/homebrew/advanced-renamer.json Adds Homebrew input manifest for Advanced Renamer.
ee/maintained-apps/inputs/homebrew/affinity-designer.json Adds Homebrew input manifest for Affinity Designer 2.
ee/maintained-apps/inputs/homebrew/affinity-designer@1.json Adds Homebrew input manifest for Affinity Designer (v1).
ee/maintained-apps/inputs/homebrew/affinity-photo.json Adds Homebrew input manifest for Affinity Photo 2.
ee/maintained-apps/inputs/homebrew/affinity-photo@1.json Adds Homebrew input manifest for Affinity Photo (v1).
ee/maintained-apps/inputs/homebrew/affinity-publisher.json Adds Homebrew input manifest for Affinity Publisher 2.
ee/maintained-apps/inputs/homebrew/affinity-publisher@1.json Adds Homebrew input manifest for Affinity Publisher (v1).
ee/maintained-apps/inputs/homebrew/airbuddy.json Adds Homebrew input manifest for AirBuddy.
ee/maintained-apps/inputs/homebrew/airdroid.json Adds Homebrew input manifest for AirDroid.
ee/maintained-apps/inputs/homebrew/airparrot.json Adds Homebrew input manifest for AirParrot.
ee/maintained-apps/inputs/homebrew/airserver.json Adds Homebrew input manifest for AirServer.
ee/maintained-apps/inputs/homebrew/airtable.json Adds Homebrew input manifest for Airtable.
ee/maintained-apps/inputs/homebrew/airy.json Adds Homebrew input manifest for Airy.
ee/maintained-apps/inputs/homebrew/akiflow.json Adds Homebrew input manifest for Akiflow.
ee/maintained-apps/inputs/homebrew/alcove.json Adds Homebrew input manifest for Alcove.
ee/maintained-apps/inputs/homebrew/aldente.json Adds Homebrew input manifest for AlDente.
ee/maintained-apps/inputs/homebrew/alloy.json Adds Homebrew input manifest for Alloy.
ee/maintained-apps/inputs/homebrew/alt-tab.json Adds Homebrew input manifest for AltTab.
ee/maintained-apps/inputs/homebrew/altair-graphql-client.json Adds Homebrew input manifest for Altair GraphQL Client.
ee/maintained-apps/inputs/homebrew/amadeus-pro.json Adds Homebrew input manifest for Amadeus Pro.
ee/maintained-apps/inputs/homebrew/amadine.json Adds Homebrew input manifest for Amadine.
ee/maintained-apps/inputs/homebrew/amethyst.json Adds Homebrew input manifest for Amethyst.
ee/maintained-apps/inputs/homebrew/amie.json Adds Homebrew input manifest for Amie.
ee/maintained-apps/inputs/homebrew/angry-ip-scanner.json Adds Homebrew input manifest for Angry IP Scanner.
ee/maintained-apps/inputs/homebrew/antinote.json Adds Homebrew input manifest for Antinote.
ee/maintained-apps/inputs/homebrew/anydo.json Adds Homebrew input manifest for Any.do.
ee/maintained-apps/inputs/homebrew/anytype.json Adds Homebrew input manifest for Anytype.
ee/maintained-apps/inputs/homebrew/apidog.json Adds Homebrew input manifest for Apidog.
ee/maintained-apps/inputs/homebrew/app-fair.json Adds Homebrew input manifest for App Fair.
ee/maintained-apps/inputs/homebrew/appium-inspector.json Adds Homebrew input manifest for Appium Inspector GUI.
ee/maintained-apps/inputs/homebrew/applite.json Adds Homebrew input manifest for Applite.
ee/maintained-apps/inputs/homebrew/asset-catalog-tinkerer.json Adds Homebrew input manifest for Asset Catalog Tinkerer.
ee/maintained-apps/inputs/homebrew/atext.json Adds Homebrew input manifest for aText.
ee/maintained-apps/inputs/homebrew/audio-hijack.json Adds Homebrew input manifest for Audio Hijack.
ee/maintained-apps/inputs/homebrew/aviatrix-vpn-client.json Adds Homebrew input manifest for Aviatrix VPN Client.
ee/maintained-apps/inputs/homebrew/axure-rp.json Adds Homebrew input manifest for Axure RP.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ee/maintained-apps/outputs/aviatrix-vpn-client/darwin.json
Comment thread ee/maintained-apps/outputs/airtable/darwin.json
Comment thread ee/maintained-apps/outputs/app-fair/darwin.json
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR adds new maintained-app metadata for a large set of macOS apps. It introduces Homebrew input manifests, corresponding Darwin release manifests with install/uninstall scripts, updates the aggregated apps.json catalog, and adds frontend SVG icon components for the same app set.

Possibly related PRs

  • fleetdm/fleet#47360: Also extends the maintained-app catalog with new app entries and matching frontend icon additions.
  • fleetdm/fleet#47402: Also updates the maintained-app registry and icon mapping for a different batch of apps.
  • fleetdm/fleet#47401: Also modifies ee/maintained-apps/outputs/apps.json to insert new app entries.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch new-mac-fmas-a

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

🧹 Nitpick comments (3)
ee/maintained-apps/outputs/audio-hijack/darwin.json (1)

12-12: ⚡ Quick win

Consider adding SHA256 verification for installer integrity.

Using "no_check" means the downloaded installer's integrity isn't verified. If the vendor provides stable checksums or if you can pin to a specific version URL, adding a SHA256 hash would protect against corrupted or tampered downloads.

If this is intentional because the URL is a "latest version" endpoint with changing checksums, consider documenting that rationale.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/audio-hijack/darwin.json` at line 12, Replace the
placeholder "no_check" SHA256 in darwin.json's "sha256" field with a real
checksum or document why verification is skipped: compute the installer SHA256
for the pinned installer URL and set "sha256": "<actual-sha256-hex>" in the JSON
(or if the source is a rolling "latest" endpoint, change the download to a
versioned URL and supply its checksum, or add a README entry explaining the
intentional waiver of verification and linking the vendor's checksum source).
Ensure you update any code that constructs the download URL to use the versioned
artifact if you choose to pin.
ee/maintained-apps/outputs/aviatrix-vpn-client/darwin.json (1)

12-12: ⚡ Quick win

Consider adding SHA256 verification for installer integrity.

Using "no_check" means the downloaded installer's integrity isn't verified. For a security-sensitive VPN client, consider adding a SHA256 hash if the vendor provides stable checksums.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/aviatrix-vpn-client/darwin.json` at line 12,
Replace the placeholder "sha256": "no_check" with a real SHA256 checksum for the
downloaded installer to enable integrity verification; obtain the stable SHA256
from the vendor (or compute it by downloading the exact installer artifact
referenced in the manifest) and set that hex digest in the "sha256" field so the
installer is validated during installation (if the vendor does not publish a
checksum, include a reproducible source or verification step in the manifest and
avoid using "no_check").
ee/maintained-apps/outputs/angry-ip-scanner/darwin.json (1)

18-20: 💤 Low value

Backup file is created but never cleaned up on successful install.

The install script creates $TMPDIR/Angry IP Scanner.app.bkp when upgrading but never removes it after a successful installation. This leaves orphaned backup files in temp directories.

Consider adding cleanup logic after the copy succeeds:

 sudo cp -R "$TMPDIR/Angry IP Scanner.app" "$APPDIR"
+rm -rf "$TMPDIR/Angry IP Scanner.app.bkp" 2>/dev/null || true
 relaunch_application 'net.azib.ipscan'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/angry-ip-scanner/darwin.json` around lines 18 -
20, The backup "$TMPDIR/Angry IP Scanner.app.bkp" is created by the sudo mv call
but never removed; after the install step (the sudo cp -R "$TMPDIR/Angry IP
Scanner.app" "$APPDIR" in the script that uses TMPDIR, APPDIR and the helper
functions quit_and_track_application/relaunch_application) add cleanup logic to
detect a successful copy (check exit status of cp) and then remove the backup
(rm -rf "$TMPDIR/Angry IP Scanner.app.bkp"); also ensure the cleanup runs only
on success and consider also removing the backup in a failure/rollback path to
avoid leaving orphaned files.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ee/maintained-apps/outputs/a-better-finder-rename/darwin.json`:
- Line 19: The current installer does non-atomic replacement (it may move/remove
the live app before the new copy is verified); modify the install sequence
around the copy/move operations so the new app is first fully copied to a
temporary verified location (use the existing TMPDIR or create a unique temp
path), verify the copy succeeded (check exit status and that the .app bundle
exists and is not empty), then atomically rename/move the original app to a
backup (e.g. rename to .bkp) and move/rename the verified temp app into $APPDIR
(use mv for atomic replacement), and if any step fails restore the original from
the backup and exit non-zero; update the logic that currently calls sudo cp -R
"$TMPDIR/A Better Finder Rename 12.app" "$APPDIR" and the preceding sudo mv
backup step to follow this verified-copy → backup-original → atomic-rename →
rollback-on-failure pattern, and apply the same pattern in the equivalent
acorn/darwin.json generator/template.
- Line 20: The issue is that LOGGED_IN_USER is passed unquoted to the trash
function (calls like trash $LOGGED_IN_USER '~/Library/...'), which allows
word-splitting or empty-value positional shifts; update every call to the trash
function (and similar invocations) to quote the variable (use trash
"$LOGGED_IN_USER" '...') so the trash function receives the intended single
argument; check both occurrences in the diff and the files referencing
LOGGED_IN_USER and the trash function to ensure all calls are updated.

In `@ee/maintained-apps/outputs/affinity-designer/darwin.json`:
- Line 19: The uninstall script uses an unvalidated LOGGED_IN_USER when calling
the trash function (trash) which can be root, loginwindow or empty; update the
script to validate LOGGED_IN_USER after it is set (LOGGED_IN_USER=$(scutil ...))
and skip or avoid calling trash when LOGGED_IN_USER is empty or equals "root" or
"loginwindow"; implement a small guard block that checks LOGGED_IN_USER and only
invokes the existing trash function calls if the check passes, and apply the
same guard to the other three manifest variants that also call trash (the files
that call trash with LOGGED_IN_USER for Affinity Designer/Photo/Publisher and
their `@1` variants).

In `@ee/maintained-apps/outputs/akiflow/darwin.json`:
- Around line 19-20: Install/uninstall scripts lack fail-fast/error propagation
causing silent partial success; enable strict failure handling and ensure
cleanup/traps. Add a top-level strict mode (set -euo pipefail) and a trap to
clean MOUNT_POINT/TMPDIR on exit, then update critical operations referenced in
quit_and_track_application, relaunch_application, the hdiutil/cp/mv sequence,
and the sudo rm -rf/trash flow to check exit status and exit non-zero on failure
(or let set -e propagate), and ensure
relaunch_application/quit_and_track_application return non-zero on unrecoverable
errors so the installer fails fast instead of reporting success after
destructive actions.

In `@ee/maintained-apps/outputs/aldente/darwin.json`:
- Line 20: The remove_launchctl_service function uses launchctl list to set
plist_status but always runs the non‑sudo command; update the status check so
when should_sudo == "true" you run sudo launchctl list "${service}" (and when
false run launchctl list "${service}") so privileged daemons are detected
correctly; change the assignment to plist_status to branch on should_sudo (refer
to remove_launchctl_service, plist_status, and should_sudo).

In `@ee/maintained-apps/outputs/altair-graphql-client/darwin.json`:
- Line 20: The install script can leave /Applications without the app because it
moves the existing app to a .bkp before verifying the new payload was
successfully copied; change to a rollback-safe pattern: after unzip
"$INSTALLER_PATH" to "$TMPDIR" validate the extracted "Altair GraphQL
Client.app" exists and is copyable, copy the new app to a temporary location
(not over APPDIR), only then move the existing "$APPDIR/Altair GraphQL
Client.app" to the backup name (Altair GraphQL Client.app.bkp) and atomically
rename/move the new app into $APPDIR; on any failure (unzip, cp, mv, or
relaunch_application) restore the .bkp back to its original location and exit
non-zero. Implement this around the existing functions/variables
quit_and_track_application, relaunch_application, APPDIR, TMPDIR,
INSTALLER_PATH, using traps to ensure rollback on errors.

In `@ee/maintained-apps/outputs/amadeus-pro/darwin.json`:
- Line 12: The manifest entries currently set the integrity field "sha256" to
"no_check" for the amadeus-pro and amadine darwin manifests; replace those
placeholders with the deterministic SHA-256 hex digest of the actual installer
binaries (compute the checksum for the downloaded .dmg/.zip you intend to ship
and paste the resulting hash string into the "sha256" field) and ensure both
manifests use the real pinned values rather than "no_check" so installer
integrity is enforced.

In `@ee/maintained-apps/outputs/angry-ip-scanner/darwin.json`:
- Line 9: The installer_url currently references an ARM64-only package
("ipscan-macArm64-3.9.3.zip"); update the catalog metadata to reflect that this
entry is architecture-specific by either replacing the URL with a
universal/intel-compatible download if a universal binary is available, or keep
the ARM URL and add an explicit arch field (e.g., "arch": "arm64") and/or create
a separate Intel macOS entry with the Intel-specific installer_url so Intel
hosts won't attempt to use the ARM-only package; locate and update the
"installer_url" key and related package metadata accordingly.

In `@ee/maintained-apps/outputs/anytype/darwin.json`:
- Around line 13-15: The default_categories array currently contains "Security"
which is incorrect for Anytype; update the "default_categories" entry in the
JSON (replace the "Security" string) to "Productivity" so the app is categorized
as a knowledge/productivity tool instead of a security tool, ensuring the value
is a string inside the existing default_categories array.
- Line 9: Replace the ARM64-only URL in
ee/maintained-apps/outputs/anytype/darwin.json by updating the "installer_url"
value (the installer_url key) to an architecture-neutral macOS installer URL
(e.g., Anytype-0.55.5-mac.dmg) or, if a universal build is not available, point
it to the Intel-compatible mac x64 image (e.g., Anytype-0.55.5-mac-x64.dmg) so
Intel Macs do not download an ARM-only DMG; if you cannot change the URL, add
arch-aware fields in the same JSON and ensure the maintained-apps generator
picks the correct key for x86_64 vs arm64.

In `@ee/maintained-apps/outputs/app-fair/darwin.json`:
- Line 19: The installer can report success even if unzip/copy fail; enable
strict-fail handling before the critical extract/copy steps in this script by
turning on bash strict mode (e.g., set -euo pipefail or equivalent) immediately
before the unzip \"$INSTALLER_PATH\" -d \"$TMPDIR\" and sudo cp -R \"$TMPDIR/App
Fair.app\" \"$APPDIR\" operations so any failure aborts the script; locate the
extract/copy sequence and add the strict-mode invocation (or explicit
exit-on-error checks) near the TMPDIR/INSTALLER_PATH usage and before calling
quit_and_track_application/relaunch_application so failures aren’t masked.
- Around line 19-20: The symlink is created and removed with a relative name
("app-fair"), making install/uninstall nondeterministic; update the installer ln
invocation (the line using ln -h -f -s -- "$APPDIR/App
Fair.app/Contents/MacOS/App Fair" "app-fair") to create the link at an absolute
path (e.g. use a canonical location such as "$TMPDIR/app-fair" or
"/usr/local/bin/app-fair"), remove the useless mkdir -p ., and update the
uninstaller removal (the sudo rm -rf 'app-fair' call) to remove the same
absolute path so both install and uninstall reference the identical absolute
symlink location.

In `@ee/maintained-apps/outputs/atext/darwin.json`:
- Line 12: Replace the weak integrity marker by pinning the artifact's SHA-256
digest: locate the JSON entry containing the "sha256" key for the manifest
version "3.21" and replace the value "no_check" with the actual SHA-256 checksum
of the installer binary for that version (compute or obtain the canonical digest
from the release artifact), ensuring the "sha256" value is a valid hex digest
string so the manifest enforces binary integrity.
- Line 19: The install script currently moves the live app to backup before
ensuring the new app is fully installed (risking uninstall-on-failure); change
the flow in the install block that uses APPDIR/TMPDIR and functions
quit_and_track_application/relaunch_application so you first copy the new bundle
into a temporary path (e.g. "$APPDIR/aText.app.new" or inside TMPDIR), validate
the copy succeeded, then perform atomic renames: move the existing
"$APPDIR/aText.app" to a backup name only after the new copy is complete, and
finally mv the new bundle into "$APPDIR/aText.app"; ensure all copy/move
operations use sudo as currently done and add rollback logic to restore the
original if any step fails so the app is never removed on a partial failure.

In `@ee/maintained-apps/outputs/aviatrix-vpn-client/darwin.json`:
- Line 20: The install script calls sudo installer -pkg "$TMPDIR/AVPNC_mac.pkg"
-target / which hardcodes the package filename and breaks when the downloaded
file uses a temp name; update the installer invocation to use the actual
installer path variable (use "$INSTALLER_PATH") instead of
"$TMPDIR/AVPNC_mac.pkg", preserving quoting, so the installer uses the real
downloaded file; locate this change in the embedded script near the end where
installer is invoked (function context: quit_and_track_application /
relaunch_application surrounding calls).

In `@frontend/pages/SoftwarePage/components/icons/Activedock.tsx`:
- Line 10: The Activedock.tsx currently embeds a large inline base64 PNG (the
data URI assigned to href) which bloats bundles; replace that data URI by
exporting the image as a static asset (e.g., put the PNG under your
public/static assets or src/assets and optimize/strip metadata first), then
update the Activedock component to reference the optimized asset via a URL
import or public path (replace the data URI used in the href with the asset URL)
so the bundler/server serves the image file instead of inlining it.

---

Nitpick comments:
In `@ee/maintained-apps/outputs/angry-ip-scanner/darwin.json`:
- Around line 18-20: The backup "$TMPDIR/Angry IP Scanner.app.bkp" is created by
the sudo mv call but never removed; after the install step (the sudo cp -R
"$TMPDIR/Angry IP Scanner.app" "$APPDIR" in the script that uses TMPDIR, APPDIR
and the helper functions quit_and_track_application/relaunch_application) add
cleanup logic to detect a successful copy (check exit status of cp) and then
remove the backup (rm -rf "$TMPDIR/Angry IP Scanner.app.bkp"); also ensure the
cleanup runs only on success and consider also removing the backup in a
failure/rollback path to avoid leaving orphaned files.

In `@ee/maintained-apps/outputs/audio-hijack/darwin.json`:
- Line 12: Replace the placeholder "no_check" SHA256 in darwin.json's "sha256"
field with a real checksum or document why verification is skipped: compute the
installer SHA256 for the pinned installer URL and set "sha256":
"<actual-sha256-hex>" in the JSON (or if the source is a rolling "latest"
endpoint, change the download to a versioned URL and supply its checksum, or add
a README entry explaining the intentional waiver of verification and linking the
vendor's checksum source). Ensure you update any code that constructs the
download URL to use the versioned artifact if you choose to pin.

In `@ee/maintained-apps/outputs/aviatrix-vpn-client/darwin.json`:
- Line 12: Replace the placeholder "sha256": "no_check" with a real SHA256
checksum for the downloaded installer to enable integrity verification; obtain
the stable SHA256 from the vendor (or compute it by downloading the exact
installer artifact referenced in the manifest) and set that hex digest in the
"sha256" field so the installer is validated during installation (if the vendor
does not publish a checksum, include a reproducible source or verification step
in the manifest and avoid using "no_check").
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c6bdca42-5691-4c0a-ae21-e38f6a57399b

📥 Commits

Reviewing files that changed from the base of the PR and between 2ad7671 and f1946fa.

⛔ Files ignored due to path filters (43)
  • website/assets/images/app-icon-a-better-finder-rename-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-acorn-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-activedock-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-activitywatch-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-actual-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-adguard-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-adlock-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-advanced-renamer-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-affinity-designer-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-affinity-designer@1-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-affinity-photo-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-affinity-photo@1-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-affinity-publisher-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-affinity-publisher@1-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-airbuddy-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-airdroid-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-airparrot-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-airserver-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-airtable-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-airy-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-akiflow-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-alcove-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-aldente-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-alloy-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-alt-tab-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-altair-graphql-client-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-amadeus-pro-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-amadine-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-amethyst-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-amie-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-angry-ip-scanner-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-antinote-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-anydo-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-anytype-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-apidog-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-app-fair-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-appium-inspector-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-applite-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-asset-catalog-tinkerer-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-atext-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-audio-hijack-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-aviatrix-vpn-client-60x60@2x.png is excluded by !**/*.png
  • website/assets/images/app-icon-axure-rp-60x60@2x.png is excluded by !**/*.png
📒 Files selected for processing (131)
  • ee/maintained-apps/inputs/homebrew/a-better-finder-rename.json
  • ee/maintained-apps/inputs/homebrew/acorn.json
  • ee/maintained-apps/inputs/homebrew/activedock.json
  • ee/maintained-apps/inputs/homebrew/activitywatch.json
  • ee/maintained-apps/inputs/homebrew/actual.json
  • ee/maintained-apps/inputs/homebrew/adguard.json
  • ee/maintained-apps/inputs/homebrew/adlock.json
  • ee/maintained-apps/inputs/homebrew/advanced-renamer.json
  • ee/maintained-apps/inputs/homebrew/affinity-designer.json
  • ee/maintained-apps/inputs/homebrew/affinity-designer@1.json
  • ee/maintained-apps/inputs/homebrew/affinity-photo.json
  • ee/maintained-apps/inputs/homebrew/affinity-photo@1.json
  • ee/maintained-apps/inputs/homebrew/affinity-publisher.json
  • ee/maintained-apps/inputs/homebrew/affinity-publisher@1.json
  • ee/maintained-apps/inputs/homebrew/airbuddy.json
  • ee/maintained-apps/inputs/homebrew/airdroid.json
  • ee/maintained-apps/inputs/homebrew/airparrot.json
  • ee/maintained-apps/inputs/homebrew/airserver.json
  • ee/maintained-apps/inputs/homebrew/airtable.json
  • ee/maintained-apps/inputs/homebrew/airy.json
  • ee/maintained-apps/inputs/homebrew/akiflow.json
  • ee/maintained-apps/inputs/homebrew/alcove.json
  • ee/maintained-apps/inputs/homebrew/aldente.json
  • ee/maintained-apps/inputs/homebrew/alloy.json
  • ee/maintained-apps/inputs/homebrew/alt-tab.json
  • ee/maintained-apps/inputs/homebrew/altair-graphql-client.json
  • ee/maintained-apps/inputs/homebrew/amadeus-pro.json
  • ee/maintained-apps/inputs/homebrew/amadine.json
  • ee/maintained-apps/inputs/homebrew/amethyst.json
  • ee/maintained-apps/inputs/homebrew/amie.json
  • ee/maintained-apps/inputs/homebrew/angry-ip-scanner.json
  • ee/maintained-apps/inputs/homebrew/antinote.json
  • ee/maintained-apps/inputs/homebrew/anydo.json
  • ee/maintained-apps/inputs/homebrew/anytype.json
  • ee/maintained-apps/inputs/homebrew/apidog.json
  • ee/maintained-apps/inputs/homebrew/app-fair.json
  • ee/maintained-apps/inputs/homebrew/appium-inspector.json
  • ee/maintained-apps/inputs/homebrew/applite.json
  • ee/maintained-apps/inputs/homebrew/asset-catalog-tinkerer.json
  • ee/maintained-apps/inputs/homebrew/atext.json
  • ee/maintained-apps/inputs/homebrew/audio-hijack.json
  • ee/maintained-apps/inputs/homebrew/aviatrix-vpn-client.json
  • ee/maintained-apps/inputs/homebrew/axure-rp.json
  • ee/maintained-apps/outputs/a-better-finder-rename/darwin.json
  • ee/maintained-apps/outputs/acorn/darwin.json
  • ee/maintained-apps/outputs/activedock/darwin.json
  • ee/maintained-apps/outputs/activitywatch/darwin.json
  • ee/maintained-apps/outputs/actual/darwin.json
  • ee/maintained-apps/outputs/adguard/darwin.json
  • ee/maintained-apps/outputs/adlock/darwin.json
  • ee/maintained-apps/outputs/advanced-renamer/darwin.json
  • ee/maintained-apps/outputs/affinity-designer/darwin.json
  • ee/maintained-apps/outputs/affinity-designer@1/darwin.json
  • ee/maintained-apps/outputs/affinity-photo/darwin.json
  • ee/maintained-apps/outputs/affinity-photo@1/darwin.json
  • ee/maintained-apps/outputs/affinity-publisher/darwin.json
  • ee/maintained-apps/outputs/affinity-publisher@1/darwin.json
  • ee/maintained-apps/outputs/airbuddy/darwin.json
  • ee/maintained-apps/outputs/airdroid/darwin.json
  • ee/maintained-apps/outputs/airparrot/darwin.json
  • ee/maintained-apps/outputs/airserver/darwin.json
  • ee/maintained-apps/outputs/airtable/darwin.json
  • ee/maintained-apps/outputs/airy/darwin.json
  • ee/maintained-apps/outputs/akiflow/darwin.json
  • ee/maintained-apps/outputs/alcove/darwin.json
  • ee/maintained-apps/outputs/aldente/darwin.json
  • ee/maintained-apps/outputs/alloy/darwin.json
  • ee/maintained-apps/outputs/alt-tab/darwin.json
  • ee/maintained-apps/outputs/altair-graphql-client/darwin.json
  • ee/maintained-apps/outputs/amadeus-pro/darwin.json
  • ee/maintained-apps/outputs/amadine/darwin.json
  • ee/maintained-apps/outputs/amethyst/darwin.json
  • ee/maintained-apps/outputs/amie/darwin.json
  • ee/maintained-apps/outputs/angry-ip-scanner/darwin.json
  • ee/maintained-apps/outputs/antinote/darwin.json
  • ee/maintained-apps/outputs/anydo/darwin.json
  • ee/maintained-apps/outputs/anytype/darwin.json
  • ee/maintained-apps/outputs/apidog/darwin.json
  • ee/maintained-apps/outputs/app-fair/darwin.json
  • ee/maintained-apps/outputs/appium-inspector/darwin.json
  • ee/maintained-apps/outputs/applite/darwin.json
  • ee/maintained-apps/outputs/apps.json
  • ee/maintained-apps/outputs/asset-catalog-tinkerer/darwin.json
  • ee/maintained-apps/outputs/atext/darwin.json
  • ee/maintained-apps/outputs/audio-hijack/darwin.json
  • ee/maintained-apps/outputs/aviatrix-vpn-client/darwin.json
  • ee/maintained-apps/outputs/axure-rp/darwin.json
  • frontend/pages/SoftwarePage/components/icons/ABetterFinderRename.tsx
  • frontend/pages/SoftwarePage/components/icons/Acorn.tsx
  • frontend/pages/SoftwarePage/components/icons/Activedock.tsx
  • frontend/pages/SoftwarePage/components/icons/Activitywatch.tsx
  • frontend/pages/SoftwarePage/components/icons/Actual.tsx
  • frontend/pages/SoftwarePage/components/icons/Adguard.tsx
  • frontend/pages/SoftwarePage/components/icons/Adlock.tsx
  • frontend/pages/SoftwarePage/components/icons/AdvancedRenamer.tsx
  • frontend/pages/SoftwarePage/components/icons/AffinityDesigner.tsx
  • frontend/pages/SoftwarePage/components/icons/AffinityDesigner1.tsx
  • frontend/pages/SoftwarePage/components/icons/AffinityPhoto.tsx
  • frontend/pages/SoftwarePage/components/icons/AffinityPhoto1.tsx
  • frontend/pages/SoftwarePage/components/icons/AffinityPublisher.tsx
  • frontend/pages/SoftwarePage/components/icons/AffinityPublisher1.tsx
  • frontend/pages/SoftwarePage/components/icons/Airbuddy.tsx
  • frontend/pages/SoftwarePage/components/icons/Airdroid.tsx
  • frontend/pages/SoftwarePage/components/icons/Airparrot.tsx
  • frontend/pages/SoftwarePage/components/icons/Airserver.tsx
  • frontend/pages/SoftwarePage/components/icons/Airtable.tsx
  • frontend/pages/SoftwarePage/components/icons/Airy.tsx
  • frontend/pages/SoftwarePage/components/icons/Akiflow.tsx
  • frontend/pages/SoftwarePage/components/icons/Alcove.tsx
  • frontend/pages/SoftwarePage/components/icons/Aldente.tsx
  • frontend/pages/SoftwarePage/components/icons/Alloy.tsx
  • frontend/pages/SoftwarePage/components/icons/AltTab.tsx
  • frontend/pages/SoftwarePage/components/icons/AltairGraphqlClient.tsx
  • frontend/pages/SoftwarePage/components/icons/AmadeusPro.tsx
  • frontend/pages/SoftwarePage/components/icons/Amadine.tsx
  • frontend/pages/SoftwarePage/components/icons/Amethyst.tsx
  • frontend/pages/SoftwarePage/components/icons/Amie.tsx
  • frontend/pages/SoftwarePage/components/icons/AngryIpScanner.tsx
  • frontend/pages/SoftwarePage/components/icons/Antinote.tsx
  • frontend/pages/SoftwarePage/components/icons/Anydo.tsx
  • frontend/pages/SoftwarePage/components/icons/Anytype.tsx
  • frontend/pages/SoftwarePage/components/icons/Apidog.tsx
  • frontend/pages/SoftwarePage/components/icons/AppFair.tsx
  • frontend/pages/SoftwarePage/components/icons/AppiumInspector.tsx
  • frontend/pages/SoftwarePage/components/icons/Applite.tsx
  • frontend/pages/SoftwarePage/components/icons/AssetCatalogTinkerer.tsx
  • frontend/pages/SoftwarePage/components/icons/Atext.tsx
  • frontend/pages/SoftwarePage/components/icons/AudioHijack.tsx
  • frontend/pages/SoftwarePage/components/icons/AviatrixVpnClient.tsx
  • frontend/pages/SoftwarePage/components/icons/AxureRp.tsx
  • frontend/pages/SoftwarePage/components/icons/index.ts

}
],
"refs": {
"1cbc7e4f": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath \"$INSTALLER_PATH\")\")\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local timeout_duration=10\n\n # check if the application is running\n local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; then\n eval \"export $var_name=0\"\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n eval \"export $var_name=0\"\n return\n fi\n\n # App was running, mark it for relaunch\n eval \"export $var_name=1\"\n echo \"Application '$bundle_id' was running; will relaunch after installation.\"\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local was_running\n\n # Check if the app was running before installation\n eval \"was_running=\\$$var_name\"\n if [[ \"$was_running\" != \"1\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping relaunching application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Relaunching application '$bundle_id'...\"\n\n # Launch the app in the logged-in user's GUI session. Apps launched by root\n # won't register with the user's Dock/GUI, so run 'open' as the console user.\n # Use 'launchctl asuser' to bootstrap into the console user's Mach namespace\n # and GUI session — 'sudo -u' alone doesn't do this, which can cause\n # LSOpenURLsWithRole() failures even when 'open' exits 0.\n local open_status=0\n if [[ $EUID -eq 0 ]]; then\n local console_uid\n console_uid=$(id -u \"$console_user\")\n /bin/launchctl asuser \"$console_uid\" sudo -u \"$console_user\" open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n else\n open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n fi\n\n if [[ $open_status -eq 0 ]]; then\n echo \"Application '$bundle_id' relaunched successfully.\"\n else\n echo \"Failed to relaunch application '$bundle_id'.\"\n fi\n}\n\n\n# extract contents\nMOUNT_POINT=$(mktemp -d /tmp/dmg_mount_XXXXXX)\nyes | hdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\" || exit 1\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\" || true\n# copy to the applications folder\nquit_and_track_application 'net.publicspace.abfr12'\nif [ -d \"$APPDIR/A Better Finder Rename 12.app\" ]; then\n\tsudo mv \"$APPDIR/A Better Finder Rename 12.app\" \"$TMPDIR/A Better Finder Rename 12.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/A Better Finder Rename 12.app\" \"$APPDIR\"\nrelaunch_application 'net.publicspace.abfr12'\n",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Shared installer root cause across ee/maintained-apps/outputs/a-better-finder-rename/darwin.json and ee/maintained-apps/outputs/acorn/darwin.json: non-atomic app replacement.
Both scripts can move/remove a working app before the replacement is validated, so a failed extract/copy can leave the app missing. Please enforce fail-fast checks and rollback-safe replacement logic in both files (ideally in the generator/template).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/a-better-finder-rename/darwin.json` at line 19,
The current installer does non-atomic replacement (it may move/remove the live
app before the new copy is verified); modify the install sequence around the
copy/move operations so the new app is first fully copied to a temporary
verified location (use the existing TMPDIR or create a unique temp path), verify
the copy succeeded (check exit status and that the .app bundle exists and is not
empty), then atomically rename/move the original app to a backup (e.g. rename to
.bkp) and move/rename the verified temp app into $APPDIR (use mv for atomic
replacement), and if any step fails restore the original from the backup and
exit non-zero; update the logic that currently calls sudo cp -R "$TMPDIR/A
Better Finder Rename 12.app" "$APPDIR" and the preceding sudo mv backup step to
follow this verified-copy → backup-original → atomic-rename →
rollback-on-failure pattern, and apply the same pattern in the equivalent
acorn/darwin.json generator/template.

],
"refs": {
"1cbc7e4f": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath \"$INSTALLER_PATH\")\")\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local timeout_duration=10\n\n # check if the application is running\n local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; then\n eval \"export $var_name=0\"\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n eval \"export $var_name=0\"\n return\n fi\n\n # App was running, mark it for relaunch\n eval \"export $var_name=1\"\n echo \"Application '$bundle_id' was running; will relaunch after installation.\"\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local was_running\n\n # Check if the app was running before installation\n eval \"was_running=\\$$var_name\"\n if [[ \"$was_running\" != \"1\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping relaunching application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Relaunching application '$bundle_id'...\"\n\n # Launch the app in the logged-in user's GUI session. Apps launched by root\n # won't register with the user's Dock/GUI, so run 'open' as the console user.\n # Use 'launchctl asuser' to bootstrap into the console user's Mach namespace\n # and GUI session — 'sudo -u' alone doesn't do this, which can cause\n # LSOpenURLsWithRole() failures even when 'open' exits 0.\n local open_status=0\n if [[ $EUID -eq 0 ]]; then\n local console_uid\n console_uid=$(id -u \"$console_user\")\n /bin/launchctl asuser \"$console_uid\" sudo -u \"$console_user\" open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n else\n open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n fi\n\n if [[ $open_status -eq 0 ]]; then\n echo \"Application '$bundle_id' relaunched successfully.\"\n else\n echo \"Failed to relaunch application '$bundle_id'.\"\n fi\n}\n\n\n# extract contents\nMOUNT_POINT=$(mktemp -d /tmp/dmg_mount_XXXXXX)\nyes | hdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\" || exit 1\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\" || true\n# copy to the applications folder\nquit_and_track_application 'net.publicspace.abfr12'\nif [ -d \"$APPDIR/A Better Finder Rename 12.app\" ]; then\n\tsudo mv \"$APPDIR/A Better Finder Rename 12.app\" \"$TMPDIR/A Better Finder Rename 12.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/A Better Finder Rename 12.app\" \"$APPDIR\"\nrelaunch_application 'net.publicspace.abfr12'\n",
"25b72319": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nsudo rm -rf \"$APPDIR/A Better Finder Rename 12.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Support/A Better Finder Rename 12'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.apple.helpd/Generated/net.publicspace.abfr12.help*'\ntrash $LOGGED_IN_USER '~/Library/Caches/net.publicspace.abfr*'\ntrash $LOGGED_IN_USER '~/Library/Cookies/net.publicspace.abfr*.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/net.publicspace.abfr*'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/net.publicspace.abfr*.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Preferences/net.publicspace.abfr*.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/net.publicspace.abfr*.savedState'\n"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Shared uninstall root cause across ee/maintained-apps/outputs/a-better-finder-rename/darwin.json and ee/maintained-apps/outputs/acorn/darwin.json: unquoted LOGGED_IN_USER arguments.
Both files pass LOGGED_IN_USER to trash unquoted, which can shift positional parameters when empty/unexpected and break cleanup path targeting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/a-better-finder-rename/darwin.json` at line 20,
The issue is that LOGGED_IN_USER is passed unquoted to the trash function (calls
like trash $LOGGED_IN_USER '~/Library/...'), which allows word-splitting or
empty-value positional shifts; update every call to the trash function (and
similar invocations) to quote the variable (use trash "$LOGGED_IN_USER" '...')
so the trash function receives the intended single argument; check both
occurrences in the diff and the files referencing LOGGED_IN_USER and the trash
function to ensure all calls are updated.

}
],
"refs": {
"8f0b4ab9": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nsudo rm -rf \"$APPDIR/Affinity Designer 2.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Support/Affinity Designer 2'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.seriflabs.affinitydesigner2'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.seriflabs.affinitydesigner2'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.seriflabs.affinitydesigner2.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.seriflabs.affinitydesigner2.savedState'\ntrash $LOGGED_IN_USER '~/Library/WebKit/com.seriflabs.affinitydesigner2'\n",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Root cause: uninstall scripts use unvalidated console-user identity for user-home cleanup.
ee/maintained-apps/outputs/affinity-designer/darwin.json, ee/maintained-apps/outputs/affinity-designer@1/darwin.json, ee/maintained-apps/outputs/affinity-photo/darwin.json, ee/maintained-apps/outputs/affinity-photo@1/darwin.json, ee/maintained-apps/outputs/affinity-publisher/darwin.json, and ee/maintained-apps/outputs/affinity-publisher@1/darwin.json all call trash using LOGGED_IN_USER without guarding root/loginwindow/empty cases. A shared guard + conditional trash block should be applied to all six uninstall refs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/affinity-designer/darwin.json` at line 19, The
uninstall script uses an unvalidated LOGGED_IN_USER when calling the trash
function (trash) which can be root, loginwindow or empty; update the script to
validate LOGGED_IN_USER after it is set (LOGGED_IN_USER=$(scutil ...)) and skip
or avoid calling trash when LOGGED_IN_USER is empty or equals "root" or
"loginwindow"; implement a small guard block that checks LOGGED_IN_USER and only
invokes the existing trash function calls if the check passes, and apply the
same guard to the other three manifest variants that also call trash (the files
that call trash with LOGGED_IN_USER for Affinity Designer/Photo/Publisher and
their `@1` variants).

Comment on lines +19 to +20
"19097683": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath \"$INSTALLER_PATH\")\")\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local timeout_duration=10\n\n # check if the application is running\n local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; then\n eval \"export $var_name=0\"\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n eval \"export $var_name=0\"\n return\n fi\n\n # App was running, mark it for relaunch\n eval \"export $var_name=1\"\n echo \"Application '$bundle_id' was running; will relaunch after installation.\"\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local was_running\n\n # Check if the app was running before installation\n eval \"was_running=\\$$var_name\"\n if [[ \"$was_running\" != \"1\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping relaunching application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Relaunching application '$bundle_id'...\"\n\n # Launch the app in the logged-in user's GUI session. Apps launched by root\n # won't register with the user's Dock/GUI, so run 'open' as the console user.\n # Use 'launchctl asuser' to bootstrap into the console user's Mach namespace\n # and GUI session — 'sudo -u' alone doesn't do this, which can cause\n # LSOpenURLsWithRole() failures even when 'open' exits 0.\n local open_status=0\n if [[ $EUID -eq 0 ]]; then\n local console_uid\n console_uid=$(id -u \"$console_user\")\n /bin/launchctl asuser \"$console_uid\" sudo -u \"$console_user\" open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n else\n open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n fi\n\n if [[ $open_status -eq 0 ]]; then\n echo \"Application '$bundle_id' relaunched successfully.\"\n else\n echo \"Failed to relaunch application '$bundle_id'.\"\n fi\n}\n\n\n# extract contents\nMOUNT_POINT=$(mktemp -d /tmp/dmg_mount_XXXXXX)\nyes | hdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\" || exit 1\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\" || true\n# copy to the applications folder\nquit_and_track_application 'com.akiflow.akiflow'\nif [ -d \"$APPDIR/Akiflow.app\" ]; then\n\tsudo mv \"$APPDIR/Akiflow.app\" \"$TMPDIR/Akiflow.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/Akiflow.app\" \"$APPDIR\"\nrelaunch_application 'com.akiflow.akiflow'\n",
"9d10ecef": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nsudo rm -rf \"$APPDIR/Akiflow.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Support/Akiflow'\ntrash $LOGGED_IN_USER '~/Library/Preferences/Akiflow'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.akiflow.akiflow.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.akiflow.akiflow.savedState'\n"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Shared root cause across Akiflow, Alcove, AlDente, Alloy, and AltTab manifests: missing fail-fast script behavior.

ee/maintained-apps/outputs/akiflow/darwin.json, ee/maintained-apps/outputs/alcove/darwin.json, ee/maintained-apps/outputs/aldente/darwin.json, ee/maintained-apps/outputs/alloy/darwin.json, and ee/maintained-apps/outputs/alt-tab/darwin.json all embed install/uninstall scripts that perform destructive operations without strict failure handling, enabling partial state with success exit codes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/akiflow/darwin.json` around lines 19 - 20,
Install/uninstall scripts lack fail-fast/error propagation causing silent
partial success; enable strict failure handling and ensure cleanup/traps. Add a
top-level strict mode (set -euo pipefail) and a trap to clean MOUNT_POINT/TMPDIR
on exit, then update critical operations referenced in
quit_and_track_application, relaunch_application, the hdiutil/cp/mv sequence,
and the sudo rm -rf/trash flow to check exit status and exit non-zero on failure
(or let set -e propagate), and ensure
relaunch_application/quit_and_track_application return non-zero on unrecoverable
errors so the installer fails fast instead of reporting success after
destructive actions.

],
"refs": {
"5e5ca0a4": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath \"$INSTALLER_PATH\")\")\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local timeout_duration=10\n\n # check if the application is running\n local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; then\n eval \"export $var_name=0\"\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n eval \"export $var_name=0\"\n return\n fi\n\n # App was running, mark it for relaunch\n eval \"export $var_name=1\"\n echo \"Application '$bundle_id' was running; will relaunch after installation.\"\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local was_running\n\n # Check if the app was running before installation\n eval \"was_running=\\$$var_name\"\n if [[ \"$was_running\" != \"1\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping relaunching application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Relaunching application '$bundle_id'...\"\n\n # Launch the app in the logged-in user's GUI session. Apps launched by root\n # won't register with the user's Dock/GUI, so run 'open' as the console user.\n # Use 'launchctl asuser' to bootstrap into the console user's Mach namespace\n # and GUI session — 'sudo -u' alone doesn't do this, which can cause\n # LSOpenURLsWithRole() failures even when 'open' exits 0.\n local open_status=0\n if [[ $EUID -eq 0 ]]; then\n local console_uid\n console_uid=$(id -u \"$console_user\")\n /bin/launchctl asuser \"$console_uid\" sudo -u \"$console_user\" open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n else\n open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n fi\n\n if [[ $open_status -eq 0 ]]; then\n echo \"Application '$bundle_id' relaunched successfully.\"\n else\n echo \"Failed to relaunch application '$bundle_id'.\"\n fi\n}\n\n\n# extract contents\nMOUNT_POINT=$(mktemp -d /tmp/dmg_mount_XXXXXX)\nyes | hdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\" || exit 1\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\" || true\n# copy to the applications folder\nquit_and_track_application 'com.apphousekitchen.aldente-pro'\nif [ -d \"$APPDIR/AlDente.app\" ]; then\n\tsudo mv \"$APPDIR/AlDente.app\" \"$TMPDIR/AlDente.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/AlDente.app\" \"$APPDIR\"\nrelaunch_application 'com.apphousekitchen.aldente-pro'\n",
"7abc4904": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\nquit_application() {\n local bundle_id=\"$1\"\n local timeout_duration=10\n\n # check if the application is running\n local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nremove_launchctl_service() {\n local service=\"$1\"\n local booleans=(\"true\" \"false\")\n local plist_status\n local paths\n local should_sudo\n\n echo \"Removing launchctl service ${service}\"\n\n for should_sudo in \"${booleans[@]}\"; do\n plist_status=$(launchctl list \"${service}\" 2>/dev/null)\n\n if [[ $plist_status == \\{* ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo launchctl remove \"${service}\"\n else\n launchctl remove \"${service}\"\n fi\n sleep 1\n fi\n\n paths=(\n \"/Library/LaunchAgents/${service}.plist\"\n \"/Library/LaunchDaemons/${service}.plist\"\n )\n\n # if not using sudo, prepend the home directory to the paths\n if [[ $should_sudo == \"false\" ]]; then\n for i in \"${!paths[@]}\"; do\n paths[i]=\"${HOME}${paths[i]}\"\n done\n fi\n\n for path in \"${paths[@]}\"; do\n if [[ -e \"$path\" ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo rm -f -- \"$path\"\n else\n rm -f -- \"$path\"\n fi\n fi\n done\n done\n}\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nremove_launchctl_service 'com.apphousekitchen.aldente-pro.helper'\nquit_application 'com.apphousekitchen.aldente-pro'\nsudo rm -rf '/Library/PrivilegedHelperTools/com.apphousekitchen.aldente-pro.helper'\nsudo rm -rf \"$APPDIR/AlDente.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Support/AlDente'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.apphousekitchen.aldente-pro'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.apphousekitchen.aldente-pro'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.apphousekitchen.aldente-pro.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.apphousekitchen.aldente-pro.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.apphousekitchen.aldente-pro_backup.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.apphousekitchen.aldente-pro_stats.sqlite3'\n"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

remove_launchctl_service checks privileged services without sudo.

In Line 20, plist_status=$(launchctl list "${service}") is used for both branches. When should_sudo=="true", the status check should also use sudo launchctl list ...; otherwise system helper services may not be detected/removed in non-root runs.

Targeted fix
-    plist_status=$(launchctl list "${service}" 2>/dev/null)
+    if [[ $should_sudo == "true" ]]; then
+      plist_status=$(sudo launchctl list "${service}" 2>/dev/null)
+    else
+      plist_status=$(launchctl list "${service}" 2>/dev/null)
+    fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"7abc4904": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\nquit_application() {\n local bundle_id=\"$1\"\n local timeout_duration=10\n\n # check if the application is running\n local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nremove_launchctl_service() {\n local service=\"$1\"\n local booleans=(\"true\" \"false\")\n local plist_status\n local paths\n local should_sudo\n\n echo \"Removing launchctl service ${service}\"\n\n for should_sudo in \"${booleans[@]}\"; do\n plist_status=$(launchctl list \"${service}\" 2>/dev/null)\n\n if [[ $plist_status == \\{* ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo launchctl remove \"${service}\"\n else\n launchctl remove \"${service}\"\n fi\n sleep 1\n fi\n\n paths=(\n \"/Library/LaunchAgents/${service}.plist\"\n \"/Library/LaunchDaemons/${service}.plist\"\n )\n\n # if not using sudo, prepend the home directory to the paths\n if [[ $should_sudo == \"false\" ]]; then\n for i in \"${!paths[@]}\"; do\n paths[i]=\"${HOME}${paths[i]}\"\n done\n fi\n\n for path in \"${paths[@]}\"; do\n if [[ -e \"$path\" ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo rm -f -- \"$path\"\n else\n rm -f -- \"$path\"\n fi\n fi\n done\n done\n}\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nremove_launchctl_service 'com.apphousekitchen.aldente-pro.helper'\nquit_application 'com.apphousekitchen.aldente-pro'\nsudo rm -rf '/Library/PrivilegedHelperTools/com.apphousekitchen.aldente-pro.helper'\nsudo rm -rf \"$APPDIR/AlDente.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Support/AlDente'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.apphousekitchen.aldente-pro'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.apphousekitchen.aldente-pro'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.apphousekitchen.aldente-pro.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.apphousekitchen.aldente-pro.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.apphousekitchen.aldente-pro_backup.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.apphousekitchen.aldente-pro_stats.sqlite3'\n"
"7abc4904": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\nquit_application() {\n local bundle_id=\"$1\"\n local timeout_duration=10\n\n # check if the application is running\n local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nremove_launchctl_service() {\n local service=\"$1\"\n local booleans=(\"true\" \"false\")\n local plist_status\n local paths\n local should_sudo\n\n echo \"Removing launchctl service ${service}\"\n\n for should_sudo in \"${booleans[@]}\"; do\n if [[ $should_sudo == \"true\" ]]; then\n plist_status=$(sudo launchctl list \"${service}\" 2>/dev/null)\n else\n plist_status=$(launchctl list \"${service}\" 2>/dev/null)\n fi\n\n if [[ $plist_status == \\{* ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo launchctl remove \"${service}\"\n else\n launchctl remove \"${service}\"\n fi\n sleep 1\n fi\n\n paths=(\n \"/Library/LaunchAgents/${service}.plist\"\n \"/Library/LaunchDaemons/${service}.plist\"\n )\n\n # if not using sudo, prepend the home directory to the paths\n if [[ $should_sudo == \"false\" ]]; then\n for i in \"${!paths[@]}\"; do\n paths[i]=\"${HOME}${paths[i]}\"\n done\n fi\n\n for path in \"${paths[@]}\"; do\n if [[ -e \"$path\" ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo rm -f -- \"$path\"\n else\n rm -f -- \"$path\"\n fi\n fi\n done\n done\n}\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nremove_launchctl_service 'com.apphousekitchen.aldente-pro.helper'\nquit_application 'com.apphousekitchen.aldente-pro'\nsudo rm -rf '/Library/PrivilegedHelperTools/com.apphousekitchen.aldente-pro.helper'\nsudo rm -rf \"$APPDIR/AlDente.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Support/AlDente'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.apphousekitchen.aldente-pro'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.apphousekitchen.aldente-pro'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.apphousekitchen.aldente-pro.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.apphousekitchen.aldente-pro.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.apphousekitchen.aldente-pro_backup.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.apphousekitchen.aldente-pro_stats.sqlite3'\n"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/aldente/darwin.json` at line 20, The
remove_launchctl_service function uses launchctl list to set plist_status but
always runs the non‑sudo command; update the status check so when should_sudo ==
"true" you run sudo launchctl list "${service}" (and when false run launchctl
list "${service}") so privileged daemons are detected correctly; change the
assignment to plist_status to branch on should_sudo (refer to
remove_launchctl_service, plist_status, and should_sudo).

Comment on lines +19 to +20
"29c2c125": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath \"$INSTALLER_PATH\")\")\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local timeout_duration=10\n\n # check if the application is running\n local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; then\n eval \"export $var_name=0\"\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n eval \"export $var_name=0\"\n return\n fi\n\n # App was running, mark it for relaunch\n eval \"export $var_name=1\"\n echo \"Application '$bundle_id' was running; will relaunch after installation.\"\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local was_running\n\n # Check if the app was running before installation\n eval \"was_running=\\$$var_name\"\n if [[ \"$was_running\" != \"1\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping relaunching application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Relaunching application '$bundle_id'...\"\n\n # Launch the app in the logged-in user's GUI session. Apps launched by root\n # won't register with the user's Dock/GUI, so run 'open' as the console user.\n # Use 'launchctl asuser' to bootstrap into the console user's Mach namespace\n # and GUI session — 'sudo -u' alone doesn't do this, which can cause\n # LSOpenURLsWithRole() failures even when 'open' exits 0.\n local open_status=0\n if [[ $EUID -eq 0 ]]; then\n local console_uid\n console_uid=$(id -u \"$console_user\")\n /bin/launchctl asuser \"$console_uid\" sudo -u \"$console_user\" open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n else\n open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n fi\n\n if [[ $open_status -eq 0 ]]; then\n echo \"Application '$bundle_id' relaunched successfully.\"\n else\n echo \"Failed to relaunch application '$bundle_id'.\"\n fi\n}\n\n\n# extract contents\nunzip \"$INSTALLER_PATH\" -d \"$TMPDIR\"\n# copy to the applications folder\nquit_and_track_application 'app.App-Fair'\nif [ -d \"$APPDIR/App Fair.app\" ]; then\n\tsudo mv \"$APPDIR/App Fair.app\" \"$TMPDIR/App Fair.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/App Fair.app\" \"$APPDIR\"\nrelaunch_application 'app.App-Fair'\nmkdir -p .\n/bin/ln -h -f -s -- \"$APPDIR/App Fair.app/Contents/MacOS/App Fair\" \"app-fair\"\n",
"576e1f90": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nsudo rm -rf \"$APPDIR/App Fair.app\"\nsudo rm -rf 'app-fair'\nsudo rmdir '/Applications/App Fair'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/app.App-Fair'\ntrash $LOGGED_IN_USER '~/Library/Application Support/app.App-Fair'\ntrash $LOGGED_IN_USER '~/Library/Caches/app.App-Fair'\ntrash $LOGGED_IN_USER '~/Library/Containers/app.App-Fair'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/app.App-Fair'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/app.App-Fair.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Preferences/app.App-Fair.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/app.App-Fair.savedState'\n"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use an absolute path for the app-fair symlink.

Line 19 creates app-fair in the current working directory, and Line 20 removes 'app-fair' relatively. That makes CLI link creation/removal nondeterministic across execution environments.

Suggested direction
- mkdir -p .
- /bin/ln -h -f -s -- "$APPDIR/App Fair.app/Contents/MacOS/App Fair" "app-fair"
+ BIN_DIR="/usr/local/bin"
+ sudo mkdir -p "$BIN_DIR"
+ sudo /bin/ln -h -f -s -- "$APPDIR/App Fair.app/Contents/MacOS/App Fair" "$BIN_DIR/app-fair"

- sudo rm -rf 'app-fair'
+ sudo rm -rf '/usr/local/bin/app-fair'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/app-fair/darwin.json` around lines 19 - 20, The
symlink is created and removed with a relative name ("app-fair"), making
install/uninstall nondeterministic; update the installer ln invocation (the line
using ln -h -f -s -- "$APPDIR/App Fair.app/Contents/MacOS/App Fair" "app-fair")
to create the link at an absolute path (e.g. use a canonical location such as
"$TMPDIR/app-fair" or "/usr/local/bin/app-fair"), remove the useless mkdir -p .,
and update the uninstaller removal (the sudo rm -rf 'app-fair' call) to remove
the same absolute path so both install and uninstall reference the identical
absolute symlink location.

"installer_url": "https://www.trankynam.com/atext/downloads/aText.dmg",
"install_script_ref": "5d9d0a57",
"uninstall_script_ref": "6f097dc3",
"sha256": "no_check",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pin installer integrity for this versioned artifact.

Line 12 uses "sha256": "no_check", which disables binary integrity verification. For a fixed manifest version ("3.21"), this weakens supply-chain guarantees and allows silent artifact drift.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/atext/darwin.json` at line 12, Replace the weak
integrity marker by pinning the artifact's SHA-256 digest: locate the JSON entry
containing the "sha256" key for the manifest version "3.21" and replace the
value "no_check" with the actual SHA-256 checksum of the installer binary for
that version (compute or obtain the canonical digest from the release artifact),
ensuring the "sha256" value is a valid hex digest string so the manifest
enforces binary integrity.

}
],
"refs": {
"5d9d0a57": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath \"$INSTALLER_PATH\")\")\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local timeout_duration=10\n\n # check if the application is running\n local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; then\n eval \"export $var_name=0\"\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n eval \"export $var_name=0\"\n return\n fi\n\n # App was running, mark it for relaunch\n eval \"export $var_name=1\"\n echo \"Application '$bundle_id' was running; will relaunch after installation.\"\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local was_running\n\n # Check if the app was running before installation\n eval \"was_running=\\$$var_name\"\n if [[ \"$was_running\" != \"1\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping relaunching application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Relaunching application '$bundle_id'...\"\n\n # Launch the app in the logged-in user's GUI session. Apps launched by root\n # won't register with the user's Dock/GUI, so run 'open' as the console user.\n # Use 'launchctl asuser' to bootstrap into the console user's Mach namespace\n # and GUI session — 'sudo -u' alone doesn't do this, which can cause\n # LSOpenURLsWithRole() failures even when 'open' exits 0.\n local open_status=0\n if [[ $EUID -eq 0 ]]; then\n local console_uid\n console_uid=$(id -u \"$console_user\")\n /bin/launchctl asuser \"$console_uid\" sudo -u \"$console_user\" open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n else\n open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n fi\n\n if [[ $open_status -eq 0 ]]; then\n echo \"Application '$bundle_id' relaunched successfully.\"\n else\n echo \"Failed to relaunch application '$bundle_id'.\"\n fi\n}\n\n\n# extract contents\nMOUNT_POINT=$(mktemp -d /tmp/dmg_mount_XXXXXX)\nyes | hdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\" || exit 1\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\" || true\n# copy to the applications folder\nquit_and_track_application 'com.trankynam.aText'\nif [ -d \"$APPDIR/aText.app\" ]; then\n\tsudo mv \"$APPDIR/aText.app\" \"$TMPDIR/aText.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/aText.app\" \"$APPDIR\"\nrelaunch_application 'com.trankynam.aText'\n",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make app replacement atomic to avoid uninstall-on-failure behavior.

On Line 19, the install script moves /Applications/aText.app to backup before the new app is fully validated in place. If the subsequent copy fails, the installed app can be left missing.

Suggested install-flow hardening
- if [ -d "$APPDIR/aText.app" ]; then
-   sudo mv "$APPDIR/aText.app" "$TMPDIR/aText.app.bkp"
- fi
- sudo cp -R "$TMPDIR/aText.app" "$APPDIR"
+ STAGED_APP="$TMPDIR/aText.staged.app"
+ sudo rm -rf "$STAGED_APP"
+ sudo cp -R "$TMPDIR/aText.app" "$STAGED_APP" || exit 1
+ [ -d "$STAGED_APP" ] || exit 1
+ if [ -d "$APPDIR/aText.app" ]; then
+   sudo mv "$APPDIR/aText.app" "$TMPDIR/aText.app.bkp" || exit 1
+ fi
+ sudo mv "$STAGED_APP" "$APPDIR/aText.app" || {
+   [ -d "$TMPDIR/aText.app.bkp" ] && sudo mv "$TMPDIR/aText.app.bkp" "$APPDIR/aText.app"
+   exit 1
+ }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/atext/darwin.json` at line 19, The install script
currently moves the live app to backup before ensuring the new app is fully
installed (risking uninstall-on-failure); change the flow in the install block
that uses APPDIR/TMPDIR and functions
quit_and_track_application/relaunch_application so you first copy the new bundle
into a temporary path (e.g. "$APPDIR/aText.app.new" or inside TMPDIR), validate
the copy succeeded, then perform atomic renames: move the existing
"$APPDIR/aText.app" to a backup name only after the new copy is complete, and
finally mv the new bundle into "$APPDIR/aText.app"; ensure all copy/move
operations use sudo as currently done and add rollback logic to restore the
original if any step fails so the app is never removed on a partial failure.

],
"refs": {
"4ad80bc8": "#!/bin/bash\n\n# variables\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\nexpand_pkgid_and_map() {\n local PKGID=\"$1\"\n local FUNC=\"$2\"\n if [[ \"$PKGID\" == *\"*\" ]]; then\n local prefix=\"${PKGID%\\*}\"\n echo \"Expanding wildcard for PKGID: $PKGID\"\n for receipt in $(pkgutil --pkgs | grep \"^${prefix}\"); do\n echo \"Processing $receipt\"\n \"$FUNC\" \"$receipt\"\n done\n else\n \"$FUNC\" \"$PKGID\"\n fi\n}\n\nforget_pkg() {\n local PKGID=\"$1\"\n expand_pkgid_and_map \"$PKGID\" forget_receipt\n}\n\nforget_receipt() {\n local PKGID=\"$1\"\n sudo pkgutil --forget \"$PKGID\"\n}\n\nremove_launchctl_service() {\n local service=\"$1\"\n local booleans=(\"true\" \"false\")\n local plist_status\n local paths\n local should_sudo\n\n echo \"Removing launchctl service ${service}\"\n\n for should_sudo in \"${booleans[@]}\"; do\n plist_status=$(launchctl list \"${service}\" 2>/dev/null)\n\n if [[ $plist_status == \\{* ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo launchctl remove \"${service}\"\n else\n launchctl remove \"${service}\"\n fi\n sleep 1\n fi\n\n paths=(\n \"/Library/LaunchAgents/${service}.plist\"\n \"/Library/LaunchDaemons/${service}.plist\"\n )\n\n # if not using sudo, prepend the home directory to the paths\n if [[ $should_sudo == \"false\" ]]; then\n for i in \"${!paths[@]}\"; do\n paths[i]=\"${HOME}${paths[i]}\"\n done\n fi\n\n for path in \"${paths[@]}\"; do\n if [[ -e \"$path\" ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo rm -f -- \"$path\"\n else\n rm -f -- \"$path\"\n fi\n fi\n done\n done\n}\n\nremove_pkg_files() {\n local PKGID=\"$1\"\n expand_pkgid_and_map \"$PKGID\" remove_receipt_files\n}\n\nremove_receipt_files() {\n local PKGID=\"$1\"\n local PKGINFO VOLUME INSTALL_LOCATION FULL_INSTALL_LOCATION\n\n echo \"pkgutil --pkg-info-plist \\\"$PKGID\\\"\"\n PKGINFO=$(pkgutil --pkg-info-plist \"$PKGID\")\n VOLUME=$(echo \"$PKGINFO\" | awk '/<key>volume<\\/key>/ {getline; gsub(/.*<string>|<\\/string>.*/, \"\"); print}')\n INSTALL_LOCATION=$(echo \"$PKGINFO\" | awk '/<key>install-location<\\/key>/ {getline; gsub(/.*<string>|<\\/string>.*/, \"\"); print}')\n\n if [ -z \"$INSTALL_LOCATION\" ] || [ \"$INSTALL_LOCATION\" = \"/\" ]; then\n FULL_INSTALL_LOCATION=\"$VOLUME\"\n else\n FULL_INSTALL_LOCATION=\"$VOLUME/$INSTALL_LOCATION\"\n FULL_INSTALL_LOCATION=$(echo \"$FULL_INSTALL_LOCATION\" | sed 's|//|/|g')\n fi\n\n echo \"sudo pkgutil --only-files --files \\\"$PKGID\\\" | sed \\\"s|^|${FULL_INSTALL_LOCATION}/|\\\" | tr '\\\\\\\\n' '\\\\\\\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\"\n sudo pkgutil --only-files --files \"$PKGID\" | sed \"s|^|/${INSTALL_LOCATION}/|\" | tr '\\n' '\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\n\n echo \"sudo pkgutil --only-dirs --files \\\"$PKGID\\\" | sed \\\"s|^|${FULL_INSTALL_LOCATION}/|\\\" | grep '\\\\.app$' | tr '\\\\\\\\n' '\\\\\\\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\"\n sudo pkgutil --only-dirs --files \"$PKGID\" | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" | grep '\\.app$' | tr '\\n' '\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\n\n root_app_dir=$(\n sudo pkgutil --only-dirs --files \"$PKGID\" \\\n | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" \\\n | grep 'Applications' \\\n | awk '{ print length, $0 }' \\\n | sort -n \\\n | head -n1 \\\n | cut -d' ' -f2-\n )\n if [ -n \"$root_app_dir\" ]; then\n echo \"sudo rmdir -p \\\"$root_app_dir\\\" 2>/dev/null || :\"\n sudo rmdir -p \"$root_app_dir\" 2>/dev/null || :\n fi\n}\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nremove_launchctl_service 'aviatrix.vpn.client.rp.plist'\nremove_pkg_files 'com.Aviatrix.VPNClient'\nforget_pkg 'com.Aviatrix.VPNClient'\nsudo rm -rf '/Applications/Aviatrix VPN Client.app'\ntrash $LOGGED_IN_USER '~/Library/Aviatrix'\ntrash $LOGGED_IN_USER '~/Library/Logs/AviatrixVPNC'\ntrash $LOGGED_IN_USER '~/Library/Preferences/org.pythonmac.unspecified.AviatrixVPNClient.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/org.pythonmac.unspecified.AviatrixVPNClient.savedState'\n",
"717ae1f5": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath \"$INSTALLER_PATH\")\")\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local timeout_duration=10\n\n # check if the application is running\n local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; then\n eval \"export $var_name=0\"\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n eval \"export $var_name=0\"\n return\n fi\n\n # App was running, mark it for relaunch\n eval \"export $var_name=1\"\n echo \"Application '$bundle_id' was running; will relaunch after installation.\"\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local was_running\n\n # Check if the app was running before installation\n eval \"was_running=\\$$var_name\"\n if [[ \"$was_running\" != \"1\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping relaunching application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Relaunching application '$bundle_id'...\"\n\n # Launch the app in the logged-in user's GUI session. Apps launched by root\n # won't register with the user's Dock/GUI, so run 'open' as the console user.\n # Use 'launchctl asuser' to bootstrap into the console user's Mach namespace\n # and GUI session — 'sudo -u' alone doesn't do this, which can cause\n # LSOpenURLsWithRole() failures even when 'open' exits 0.\n local open_status=0\n if [[ $EUID -eq 0 ]]; then\n local console_uid\n console_uid=$(id -u \"$console_user\")\n /bin/launchctl asuser \"$console_uid\" sudo -u \"$console_user\" open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n else\n open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n fi\n\n if [[ $open_status -eq 0 ]]; then\n echo \"Application '$bundle_id' relaunched successfully.\"\n else\n echo \"Failed to relaunch application '$bundle_id'.\"\n fi\n}\n\n\n# install pkg files\nquit_and_track_application 'org.pythonmac.unspecified.AviatrixVPNClient'\nsudo installer -pkg \"$TMPDIR/AVPNC_mac.pkg\" -target /\nrelaunch_application 'org.pythonmac.unspecified.AviatrixVPNClient'\n"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Install script uses hardcoded filename instead of $INSTALLER_PATH.

The install script constructs $TMPDIR/AVPNC_mac.pkg assuming the downloaded file retains its original name. If the download system uses a temporary filename, this will fail.

Use $INSTALLER_PATH directly:

Suggested fix

In the embedded install script, change:

-sudo installer -pkg "$TMPDIR/AVPNC_mac.pkg" -target /
+sudo installer -pkg "$INSTALLER_PATH" -target /
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/aviatrix-vpn-client/darwin.json` at line 20, The
install script calls sudo installer -pkg "$TMPDIR/AVPNC_mac.pkg" -target / which
hardcodes the package filename and breaks when the downloaded file uses a temp
name; update the installer invocation to use the actual installer path variable
(use "$INSTALLER_PATH") instead of "$TMPDIR/AVPNC_mac.pkg", preserving quoting,
so the installer uses the real downloaded file; locate this change in the
embedded script near the end where installer is invoked (function context:
quit_and_track_application / relaunch_application surrounding calls).

<image
width={32}
height={32}
href="data:image/png;base64,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"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Inline base64 PNG on Line 10 will bloat frontend bundles.

Embedding the full PNG payload in TSX significantly increases JS transfer and parse cost; with this PR pattern across many icons, this compounds on Software page load. Please externalize the PNG as a static asset (or optimized import) and reference the URL, and strip embedded metadata before shipping.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/pages/SoftwarePage/components/icons/Activedock.tsx` at line 10, The
Activedock.tsx currently embeds a large inline base64 PNG (the data URI assigned
to href) which bloats bundles; replace that data URI by exporting the image as a
static asset (e.g., put the PNG under your public/static assets or src/assets
and optimize/strip metadata first), then update the Activedock component to
reference the optimized asset via a URL import or public path (replace the data
URI used in the href with the asset URL) so the bundler/server serves the image
file instead of inlining it.

@allenhouchins
allenhouchins merged commit 532c5d5 into main Jun 12, 2026
31 of 33 checks passed
@allenhouchins
allenhouchins deleted the new-mac-fmas-a branch June 12, 2026 20:33
allenhouchins added a commit that referenced this pull request Jun 12, 2026
…d upstream)

main #47536 landed the 'A' Fleet-maintained apps that this branch also added.
The line-level merge appended both copies; rebuild apps.json as main's
authoritative entries plus only the apps unique to this branch.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants