impel-cli 0.18.15-beta.7 → 0.18.15-beta.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/apps.js +25 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "impel-cli",
3
- "version": "0.18.15-beta.7",
3
+ "version": "0.18.15-beta.8",
4
4
  "description": "Prepare isolated Claude and Codex workspaces for every accessible Impel tenant",
5
5
  "type": "module",
6
6
  "bin": {
package/src/apps.js CHANGED
@@ -269,7 +269,7 @@ export const CURRENT_CONFIG_VERSION = 26;
269
269
  // — which is what made every `impel update` re-trigger macOS permission
270
270
  // prompts. Bump this ONLY when a code change alters the bytes of a built
271
271
  // bundle; leave it alone for changes that don't touch bundle contents.
272
- export const BUNDLE_BUILD_FINGERPRINT = "bundle-2026-07-31.1";
272
+ export const BUNDLE_BUILD_FINGERPRINT = "bundle-2026-07-31.2";
273
273
 
274
274
  /** Parse the tenant's install manifest, or null when absent/corrupt. */
275
275
  export function readTenantManifest(homeDir = os.homedir(), tenantId = null) {
@@ -347,7 +347,7 @@ export function bundleIsCurrent(status, {
347
347
  compatibility.vendorSignaturesPreserved === true
348
348
  && compatibility.resourceRoot === CHATGPT_RESOURCE_ROOT
349
349
  && compatibility.patches?.fastModeForGatewayAuth > 0
350
- && compatibility.patches?.tenantDisplayNamePreload === 1
350
+ && compatibility.patches?.tenantDisplayNamePreload === 2
351
351
  )),
352
352
  );
353
353
  }
@@ -1772,7 +1772,7 @@ function writeVendoredChatGPTBundleLocked(paths, vendorPath, gatewayUrl, homeDir
1772
1772
  patches: {
1773
1773
  fastModeForGatewayAuth: fastModePatchCount,
1774
1774
  desktopAPIForGatewayAuth: 0,
1775
- tenantDisplayNamePreload: 1,
1775
+ tenantDisplayNamePreload: 2,
1776
1776
  },
1777
1777
  vendorSignaturesPreserved: true,
1778
1778
  resourceRoot: CHATGPT_RESOURCE_ROOT,
@@ -2299,6 +2299,7 @@ secure_codex_home
2299
2299
  BROWSER_DATA=${shellQuote(paths.chatgpt.browserData)}
2300
2300
  mkdir -p "$BROWSER_DATA"
2301
2301
  export IMPEL_APP_DISPLAY_NAME=${shellQuote(paths.chatgpt.displayName)}
2302
+ export IMPEL_APP_BUNDLE_ID=${shellQuote(paths.chatgpt.bundleIdentifier)}
2302
2303
  PRELOAD="$HERE/../Resources/impel-chatgpt-preload.cjs"
2303
2304
  export NODE_OPTIONS="--require=\\\"$PRELOAD\\\""
2304
2305
  # Electron resolves resources from the Impel wrapper when the nested app is
@@ -2313,13 +2314,14 @@ function vendoredChatGPTDisplayNamePreload() {
2313
2314
  return `"use strict";
2314
2315
 
2315
2316
  const displayName = process.env.IMPEL_APP_DISPLAY_NAME;
2316
- if (process.type === "browser" && displayName) {
2317
+ const bundleIdentifier = process.env.IMPEL_APP_BUNDLE_ID;
2318
+ if (process.type === "browser" && displayName && bundleIdentifier) {
2317
2319
  const Module = require("node:module");
2318
2320
  const path = require("node:path");
2319
2321
  const load = Module._load;
2320
2322
  let objc;
2321
2323
  let appKit;
2322
- let launchServicesNamed = false;
2324
+ let launchServicesIdentified = false;
2323
2325
  let activationPolicyRefreshed = false;
2324
2326
 
2325
2327
  const loadNativeBridge = () => {
@@ -2331,20 +2333,31 @@ if (process.type === "browser" && displayName) {
2331
2333
  return { objc, appKit };
2332
2334
  };
2333
2335
 
2334
- const setLaunchServicesApplicationName = () => {
2335
- if (launchServicesNamed) return;
2336
+ const setLaunchServicesApplicationIdentity = () => {
2337
+ if (launchServicesIdentified) return;
2336
2338
  try {
2337
2339
  const native = loadNativeBridge();
2338
2340
  new native.objc.NobjcLibrary(
2339
2341
  "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices",
2340
2342
  );
2341
2343
  const title = native.appKit.NSString.stringWithUTF8String$(displayName);
2344
+ const identifier = native.appKit.NSString.stringWithUTF8String$(bundleIdentifier);
2342
2345
  const info = native.appKit.NSMutableDictionary.dictionaryWithDictionary$(
2343
2346
  native.appKit.NSBundle.mainBundle().infoDictionary(),
2344
2347
  );
2345
2348
  for (const key of ["CFBundleName", "CFBundleDisplayName"]) {
2346
2349
  info.setObject$forKey$(title, native.appKit.NSString.stringWithUTF8String$(key));
2347
2350
  }
2351
+ // The signed vendor executable runs from the nested ChatGPT.app, so
2352
+ // LaunchServices initially checks it in as com.openai.codex. When the
2353
+ // native app is also open, Dock groups both processes under ChatGPT even
2354
+ // though LSDisplayName was already tenant-branded. Check the process in
2355
+ // again with the unique outer-wrapper identity to keep each tenant's
2356
+ // Dock item distinct while preserving the vendor executable signature.
2357
+ info.setObject$forKey$(
2358
+ identifier,
2359
+ native.appKit.NSString.stringWithUTF8String$("CFBundleIdentifier"),
2360
+ );
2348
2361
  native.objc.callFunction(
2349
2362
  "_LSSetApplicationLaunchServicesServerConnectionStatus",
2350
2363
  { returns: "v", args: ["Q", "@"] },
@@ -2368,7 +2381,7 @@ if (process.type === "browser" && displayName) {
2368
2381
  title,
2369
2382
  null,
2370
2383
  );
2371
- launchServicesNamed = status === 0;
2384
+ launchServicesIdentified = status === 0;
2372
2385
  } catch {
2373
2386
  // Keep the signed vendor app usable if LaunchServices changes this
2374
2387
  // private-but-established process-title API in a future macOS release.
@@ -2403,7 +2416,7 @@ if (process.type === "browser" && displayName) {
2403
2416
  const setName = loaded.app.setName.bind(loaded.app);
2404
2417
  setName(displayName);
2405
2418
  loaded.app.setName = () => setName(displayName);
2406
- setLaunchServicesApplicationName();
2419
+ setLaunchServicesApplicationIdentity();
2407
2420
  setNativeApplicationMenuTitle();
2408
2421
  const setApplicationMenu = loaded.Menu.setApplicationMenu.bind(loaded.Menu);
2409
2422
  loaded.Menu.setApplicationMenu = (menu) => {
@@ -2414,11 +2427,11 @@ if (process.type === "browser" && displayName) {
2414
2427
  loaded.app.setActivationPolicy("accessory");
2415
2428
  setTimeout(() => {
2416
2429
  loaded.app.setActivationPolicy("regular");
2417
- launchServicesNamed = false;
2418
- setLaunchServicesApplicationName();
2430
+ launchServicesIdentified = false;
2431
+ setLaunchServicesApplicationIdentity();
2419
2432
  setApplicationMenu(menu);
2420
2433
  setNativeApplicationMenuTitle();
2421
- }, 0);
2434
+ }, 100);
2422
2435
  }
2423
2436
  setTimeout(setNativeApplicationMenuTitle, 0);
2424
2437
  return result;