Skip to content

Commit 5c0c5d6

Browse files
committed
Use Companion bundle id for BetterCodex
1 parent 73eccb2 commit 5c0c5d6

6 files changed

Lines changed: 26 additions & 6 deletions

File tree

STATUS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ Current marketplace architecture update:
112112
- `/Applications/BetterCodex.app` was refreshed with `npm run desktop -- install --launch=false`; bundle id remains `com.openai.codex.bettercodex`.
113113
- Current installed-app verification after the icon refresh: `/Applications/BetterCodex.app` has loader `yes`, repair agent `no`, ASAR integrity `yes`, codesign `yes`, and `CFBundleIconFile` is `bettercodex.icns`.
114114
- BetterCodex icon install now overwrites the sibling app's legacy icon resources as well as `bettercodex.icns`: `icon.icns`, `electron.icns`, `app.icns`, `icon.png`, `icon-codex-dark-color.png`, `icon-codex-light.png`, and `default_app/icon.png` all point to the gradient BetterCodex asset in `/Applications/BetterCodex.app`; Launch Services was re-registered, Quick Look cache reset, Dock restarted, and the app relaunched.
115+
- BetterCodex sibling app identity is now Companion-owned: `/Applications/BetterCodex.app` uses bundle id `com.companion.bettercodex` so macOS does not associate it with the official `com.openai.codex` app identity or icon cache.
115116
- Official `/Applications/Codex.app` stayed clean after the BetterCodex icon/install refresh: loader `no`, repair agent `no`, ASAR integrity `yes`, and codesign `yes`.
116117
- Live `https://bettercodex-web.companion-inc.workers.dev/api/addons` returned schema version `1`, zero add-ons, and `Focus Contrast` absent after the registry cleanup.
117118
- Disposable CDP smoke against `/tmp/Codex-BetterCodex-MarketplaceSmoke.app` verified top-level tabs `Plugins`/`Themes`, no `Skills`/`Store` text inside BetterCodex, Marketplace under both active surfaces, `Example Theme` install from Themes Marketplace, and local file write to `/tmp/bettercodex-marketplace-smoke-home/themes/example.theme.css`.

apps/desktop/src/bundler.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ function createBundle(options = {}) {
1313
const installRoot = options.installRoot || defaultInstallRoot;
1414
const name = normalizeName(options.name || "BetterCodex");
1515
const slug = slugify(name);
16+
const bundleId = bundleIdentifierForSlug(slug);
1617
const destination = options.destination
1718
? path.resolve(options.destination)
1819
: defaultDestination(appRoot, name);
@@ -24,7 +25,7 @@ function createBundle(options = {}) {
2425
if (!options.replace) {
2526
throw new Error(`Destination exists: ${destination}. Rerun with --replace to overwrite it.`);
2627
}
27-
quitBundle(`com.openai.codex.${slug}`, destination);
28+
quitBundle(bundleId, destination);
2829
fs.rmSync(destination, {force: true, recursive: true});
2930
}
3031

@@ -41,7 +42,7 @@ function createBundle(options = {}) {
4142
const infoPlist = path.join(destination, "Contents", "Info.plist");
4243
setPlist(infoPlist, "CFBundleDisplayName", name);
4344
setPlist(infoPlist, "CFBundleName", name);
44-
setPlist(infoPlist, "CFBundleIdentifier", `com.openai.codex.${slug}`);
45+
setPlist(infoPlist, "CFBundleIdentifier", bundleId);
4546
if (installBetterCodexIcon(destination)) {
4647
setPlist(infoPlist, "CFBundleIconFile", "bettercodex.icns");
4748
}
@@ -53,7 +54,7 @@ function createBundle(options = {}) {
5354
}
5455

5556
return {
56-
bundleId: `com.openai.codex.${slug}`,
57+
bundleId,
5758
destination,
5859
installResult: result,
5960
userDataDir: userDataDir(name),
@@ -87,6 +88,10 @@ function slugify(name) {
8788
.replace(/-+/g, "-") || "bettercodex";
8889
}
8990

91+
function bundleIdentifierForSlug(slug) {
92+
return `com.companion.${slug || "bettercodex"}`;
93+
}
94+
9095
function quitBundle(bundleId, destination) {
9196
const ownPids = new Set([process.pid, process.ppid].filter(Boolean).map(String));
9297
childProcess.spawnSync("/usr/bin/osascript", [
@@ -221,6 +226,7 @@ function signAndVerify(destination) {
221226

222227
module.exports = {
223228
betterCodexIconPython,
229+
bundleIdentifierForSlug,
224230
createBundle,
225231
defaultDestination,
226232
normalizeName,

apps/desktop/src/installer.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,12 +222,15 @@ function assertCodexApp(paths) {
222222
}
223223
const bundleId = readPlistValue(paths.infoPlistPath, "CFBundleIdentifier");
224224
if (!isCodexBundleIdentifier(bundleId)) {
225-
throw new Error(`Expected com.openai.codex or com.openai.codex.<name>, found ${bundleId || "unknown bundle id"}`);
225+
throw new Error(`Expected com.openai.codex or a supported BetterCodex sibling id, found ${bundleId || "unknown bundle id"}`);
226226
}
227227
}
228228

229229
function isCodexBundleIdentifier(bundleId) {
230-
return bundleId === "com.openai.codex" || String(bundleId || "").startsWith("com.openai.codex.");
230+
const value = String(bundleId || "");
231+
return value === "com.openai.codex"
232+
|| value.startsWith("com.openai.codex.")
233+
|| value === "com.companion.bettercodex";
231234
}
232235

233236
function backupAppState(paths, installRoot, metadata) {

apps/desktop/src/runtimeFiles.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,11 @@ function assertCodexApp(paths) {
159159
if (!fs.existsSync(paths.asarPath)) throw new Error("Codex app.asar not found: " + paths.asarPath);
160160
if (!fs.existsSync(paths.infoPlistPath)) throw new Error("Codex Info.plist not found: " + paths.infoPlistPath);
161161
const bundleId = readPlistValue(paths.infoPlistPath, "CFBundleIdentifier");
162-
if (bundleId !== "com.openai.codex" && !String(bundleId || "").startsWith("com.openai.codex.")) {
162+
if (
163+
bundleId !== "com.openai.codex"
164+
&& !String(bundleId || "").startsWith("com.openai.codex.")
165+
&& bundleId !== "com.companion.bettercodex"
166+
) {
163167
throw new Error("Expected Codex bundle id, found " + (bundleId || "unknown"));
164168
}
165169
}

test/bundler.test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const assert = require("node:assert/strict");
55

66
const {
77
betterCodexIconPython,
8+
bundleIdentifierForSlug,
89
defaultDestination,
910
normalizeName,
1011
slugify,
@@ -32,6 +33,10 @@ test("default BetterCodex user data path is not Codex-prefixed", () => {
3233
assert.match(userDataDir("BetterCodex"), /\/Library\/Application Support\/BetterCodex$/);
3334
});
3435

36+
test("BetterCodex bundle id is Companion-owned", () => {
37+
assert.equal(bundleIdentifierForSlug(slugify("BetterCodex")), "com.companion.bettercodex");
38+
});
39+
3540
test("BetterCodex icon generator resizes the generated app icon asset", () => {
3641
const source = betterCodexIconPython();
3742
assert.match(source, /Image\.open\(source\)\.convert\("RGBA"\)/);

test/installer.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const {isCodexBundleIdentifier} = require("../apps/desktop/src/installer");
88
test("isCodexBundleIdentifier accepts official and BetterCodex sibling ids", () => {
99
assert.equal(isCodexBundleIdentifier("com.openai.codex"), true);
1010
assert.equal(isCodexBundleIdentifier("com.openai.codex.smoke"), true);
11+
assert.equal(isCodexBundleIdentifier("com.companion.bettercodex"), true);
1112
assert.equal(isCodexBundleIdentifier("com.openai.codexmalformed"), false);
1213
assert.equal(isCodexBundleIdentifier("com.example.codex"), false);
1314
});

0 commit comments

Comments
 (0)