From 1de72fd16f71c4d6807da74db5aba13e567347f0 Mon Sep 17 00:00:00 2001 From: Allen Houchins <32207388+allenhouchins@users.noreply.github.com> Date: Thu, 11 Jun 2026 20:58:36 -0500 Subject: [PATCH] Add Codex as a macOS FMA (#47480) Add support for the OpenAI Codex desktop app: create Homebrew input (ee/maintained-apps/inputs/homebrew/codex-app.json) and a new app output entry (ee/maintained-apps/outputs/codex-app/darwin.json) containing version, installer URL, sha256 and install/uninstall scripts that handle quitting/relaunch/trashing. Also add the Codex entry to ee/maintained-apps/outputs/apps.json. Frontend assets: add a Codex SVG icon component and update the icons index (frontend/pages/SoftwarePage/components/icons/...), and include the PNG app icon (website/assets/images/app-icon-codex-app-60x60@2x.png). These changes register Codex (bundle id com.openai.codex) as a maintained developer tool and provide installation/uninstallation logic and UI assets. **Related issue:** Resolves #47462 --- .../inputs/homebrew/codex-app.json | 8 +++++++ ee/maintained-apps/outputs/apps.json | 7 ++++++ .../outputs/codex-app/darwin.json | 22 ++++++++++++++++++ .../components/icons/CodexApp.tsx | 14 +++++++++++ .../SoftwarePage/components/icons/index.ts | 2 ++ .../images/app-icon-codex-app-60x60@2x.png | Bin 0 -> 15120 bytes 6 files changed, 53 insertions(+) create mode 100644 ee/maintained-apps/inputs/homebrew/codex-app.json create mode 100644 ee/maintained-apps/outputs/codex-app/darwin.json create mode 100644 frontend/pages/SoftwarePage/components/icons/CodexApp.tsx create mode 100644 website/assets/images/app-icon-codex-app-60x60@2x.png diff --git a/ee/maintained-apps/inputs/homebrew/codex-app.json b/ee/maintained-apps/inputs/homebrew/codex-app.json new file mode 100644 index 0000000000..7d8345b93f --- /dev/null +++ b/ee/maintained-apps/inputs/homebrew/codex-app.json @@ -0,0 +1,8 @@ +{ + "name": "Codex", + "unique_identifier": "com.openai.codex", + "token": "codex-app", + "installer_format": "zip", + "slug": "codex-app/darwin", + "default_categories": ["Developer tools"] +} diff --git a/ee/maintained-apps/outputs/apps.json b/ee/maintained-apps/outputs/apps.json index e8b3d4c8f7..4e49520953 100644 --- a/ee/maintained-apps/outputs/apps.json +++ b/ee/maintained-apps/outputs/apps.json @@ -1268,6 +1268,13 @@ "unique_identifier": "com.krill.CodeRunner", "description": "CodeRunner is a multi-language programming editor." }, + { + "name": "Codex", + "slug": "codex-app/darwin", + "platform": "darwin", + "unique_identifier": "com.openai.codex", + "description": "Codex is OpenAI's desktop app for managing coding agents." + }, { "name": "CodexBar", "slug": "codexbar/darwin", diff --git a/ee/maintained-apps/outputs/codex-app/darwin.json b/ee/maintained-apps/outputs/codex-app/darwin.json new file mode 100644 index 0000000000..19b5665630 --- /dev/null +++ b/ee/maintained-apps/outputs/codex-app/darwin.json @@ -0,0 +1,22 @@ +{ + "versions": [ + { + "version": "26.609.30741", + "queries": { + "exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'com.openai.codex';", + "patched": "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM apps WHERE bundle_identifier = 'com.openai.codex' AND version_compare(bundle_short_version, '26.609.30741') < 0);" + }, + "installer_url": "https://persistent.oaistatic.com/codex-app-prod/Codex-darwin-arm64-26.609.30741.zip", + "install_script_ref": "5e3efa57", + "uninstall_script_ref": "a2b5ee81", + "sha256": "24cd7b226c70aefb9143571cdcfb2d39fb38794a98ba3935d406d89e7202bf7e", + "default_categories": [ + "Developer tools" + ] + } + ], + "refs": { + "5e3efa57": "#!/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 'com.openai.codex'\nif [ -d \"$APPDIR/Codex.app\" ]; then\n\tsudo mv \"$APPDIR/Codex.app\" \"$TMPDIR/Codex.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/Codex.app\" \"$APPDIR\"\nrelaunch_application 'com.openai.codex'\n", + "a2b5ee81": "#!/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\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\nquit_application 'com.openai.codex'\nsudo rm -rf \"$APPDIR/Codex.app\"\nsudo rmdir '~/.codex'\ntrash $LOGGED_IN_USER '~/Library/Application Support/Codex'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.openai.codex.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.openai.codex'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.openai.codex'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.openai.codex.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Logs/com.openai.codex'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.openai.codex.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.openai.codex.savedState'\n" + } +} diff --git a/frontend/pages/SoftwarePage/components/icons/CodexApp.tsx b/frontend/pages/SoftwarePage/components/icons/CodexApp.tsx new file mode 100644 index 0000000000..ab8c599a3d --- /dev/null +++ b/frontend/pages/SoftwarePage/components/icons/CodexApp.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +import type { SVGProps } from "react"; + +const CodexApp = (props: SVGProps) => ( + + + +); +export default CodexApp; diff --git a/frontend/pages/SoftwarePage/components/icons/index.ts b/frontend/pages/SoftwarePage/components/icons/index.ts index 002bd9e659..30e9fe99b9 100644 --- a/frontend/pages/SoftwarePage/components/icons/index.ts +++ b/frontend/pages/SoftwarePage/components/icons/index.ts @@ -142,6 +142,7 @@ import Cmux from "./Cmux"; import Coconutbattery from "./Coconutbattery"; import Codeedit from "./Codeedit"; import Coderunner from "./Coderunner"; +import CodexApp from "./CodexApp"; import Codexbar from "./Codexbar"; import CogApp from "./CogApp"; import Colorsnapper from "./Colorsnapper"; @@ -1040,6 +1041,7 @@ export const SOFTWARE_NAME_TO_ICON_MAP = { code: VisualStudioCode, codeedit: Codeedit, coderunner: Coderunner, + codex: CodexApp, codexbar: Codexbar, cog: CogApp, "colorsnapper 2": Colorsnapper, diff --git a/website/assets/images/app-icon-codex-app-60x60@2x.png b/website/assets/images/app-icon-codex-app-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ceca716385d231d94bf597155800b271737c34f6 GIT binary patch literal 15120 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 F{|m1D%Wwby literal 0 HcmV?d00001