[PR #910] [MERGED] feat(native): open iOS-style and iOS-on-Mac wrapper app bundles #2339

Closed
opened 2026-10-16 21:17:29 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/910
Author: @kaoru0822-kitauji
Created: 10/7/2026
Status: ✅ Merged
Merged: 10/7/2026
Merged by: @kaoru0822-kitauji

Base: main ← Head: feat/ios-app-bundles


📝 Commits (3)

  • aeebba3 feat(native): open iOS-style and iOS-on-Mac wrapper app bundles
  • 418a556 fix(native): keep the resolved bundle plist and permission failures
  • 4d2faef chore: merge main and resolve PR #910 conflicts

📊 Changes

6 files changed (+345 additions, -88 deletions)

View changed files

➕ src/application/AppBundleExecutable.ts (+186 -0)
📝 src/application/BinaryTargetResolver.ts (+13 -82)
📝 src/domain/binaryTargetTypes.ts (+2 -0)
📝 src/native/NativeMacOSProvider.ts (+4 -5)
📝 tests/boundary/filesystem/binaryTarget.test.ts (+117 -1)
📝 tests/boundary/providers/native/nativeProvider.test.ts (+23 -0)

📄 Description

Summary

REA could not open iOS app bundles at all. BinaryTargetResolver understood only the macOS layout (Contents/Info.plist and Contents/MacOS/<CFBundleExecutable>). Two common iOS layouts were therefore rejected by every command with app has no readable CFBundleExecutable:

  • Flat iOS-style bundles: Info.plist and the program file sit at the bundle root, as in an .app extracted from an IPA's Payload/.
  • iOS apps installed on Apple silicon Macs: /Applications/Stash.app is a wrapper holding Wrapper/Stash.app (a flat iOS bundle) and a WrappedBundle → Wrapper/Stash.app symlink, with no Contents/.

Real example, /Applications/Stash.app (Stash 3.4.1 from the App Store):

Command main this PR
inspect-plist, inspect-macho, list-architectures, inspect-signature target_unavailable: app has no readable CFBundleExecutable succeed. inspect-plist reads Wrapper/Stash.app/Info.plist (ws.stash.app 3.4.1). The signature shows Apple iPhone OS Application Signing.
decode-interface-builder same error 64 documents
inspect-asset-catalog same error 6 catalogs, 856 records

Changes:

  • Bundle resolution moves from BinaryTargetResolver into src/application/AppBundleExecutable.ts, unchanged for macOS bundles. It tries the macOS layout, then the flat layout, then a wrapper. For a wrapper, it lists the real Wrapper directory and requires exactly one real *.app directory inside. It never follows the WrappedBundle symlink. The program file must still resolve inside its own program directory, and a flat bundle's escaping symlink reports app program file leaves the bundle root.
  • inspect_plist with no path still defaults to Contents/Info.plist. When that file does not exist and the target is a bundle, it uses the Info.plist beside the resolved program file instead. That file is the flat or wrapped bundle's own plist.
  • The directory error now says "requires a file or app bundle" instead of "macOS app bundle".

The opened target's sourcePath remains the bundle the caller passed, such as the wrapper. Bundle-walking tools (decode_interface_builder, inspect_asset_catalog) therefore see the whole wrapper. The wrapper's link is not followed, so they visit Wrapper/Stash.app once.

No schema or catalog change.

Verification

  • tests/boundary/filesystem/binaryTarget.test.ts: new cases cover a flat bundle resolving to its root program file; a wrapper resolving through Wrapper/Phone.app with a WrappedBundle symlink present, then rejected once a second Wrapper/*.app appears; and a flat bundle's program symlink leaving the bundle being rejected.
  • tests/boundary/providers/native/nativeProvider.test.ts: inspect_plist on a flat bundle defaults to <bundle>/Info.plist.
    All of these fail on main. The existing macOS bundle, unsafe-name, missing-file, and escaping-symlink cases still pass.
  • Real app, through the built CLI: the table above, plus /System/Applications/Calculator.app still reads Contents/Info.plist.
  • npm run check:changed, plus the filesystem, native CLI, and native provider boundary suites (85 files), pass. The 7 ibtool-compiled NIB tests fail the same way on main on this host, which has only the Command Line Tools.

No Hopper/Ghidra/IDA verification is needed.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/910 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/7/2026 **Status:** ✅ Merged **Merged:** 10/7/2026 **Merged by:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Base:** `main` ← **Head:** `feat/ios-app-bundles` --- ### 📝 Commits (3) - [`aeebba3`](https://github.com/morluto/rea/commit/aeebba391ee5c288b75cd828e7d18d32b40aa698) feat(native): open iOS-style and iOS-on-Mac wrapper app bundles - [`418a556`](https://github.com/morluto/rea/commit/418a5562f6f640503c751924a4787586c2c5c98a) fix(native): keep the resolved bundle plist and permission failures - [`4d2faef`](https://github.com/morluto/rea/commit/4d2faef37b23a41c371b74f5eab3dac9d88b0dd1) chore: merge main and resolve PR #910 conflicts ### 📊 Changes **6 files changed** (+345 additions, -88 deletions) <details> <summary>View changed files</summary> ➕ `src/application/AppBundleExecutable.ts` (+186 -0) 📝 `src/application/BinaryTargetResolver.ts` (+13 -82) 📝 `src/domain/binaryTargetTypes.ts` (+2 -0) 📝 `src/native/NativeMacOSProvider.ts` (+4 -5) 📝 `tests/boundary/filesystem/binaryTarget.test.ts` (+117 -1) 📝 `tests/boundary/providers/native/nativeProvider.test.ts` (+23 -0) </details> ### 📄 Description ## Summary REA could not open iOS app bundles at all. `BinaryTargetResolver` understood only the macOS layout (`Contents/Info.plist` and `Contents/MacOS/<CFBundleExecutable>`). Two common iOS layouts were therefore rejected by every command with `app has no readable CFBundleExecutable`: - **Flat iOS-style bundles**: `Info.plist` and the program file sit at the bundle root, as in an `.app` extracted from an IPA's `Payload/`. - **iOS apps installed on Apple silicon Macs**: `/Applications/Stash.app` is a wrapper holding `Wrapper/Stash.app` (a flat iOS bundle) and a `WrappedBundle → Wrapper/Stash.app` symlink, with no `Contents/`. Real example, `/Applications/Stash.app` (Stash 3.4.1 from the App Store): | Command | `main` | this PR | | --- | --- | --- | | `inspect-plist`, `inspect-macho`, `list-architectures`, `inspect-signature` | `target_unavailable: app has no readable CFBundleExecutable` | succeed. `inspect-plist` reads `Wrapper/Stash.app/Info.plist` (`ws.stash.app` 3.4.1). The signature shows `Apple iPhone OS Application Signing`. | | `decode-interface-builder` | same error | 64 documents | | `inspect-asset-catalog` | same error | 6 catalogs, 856 records | Changes: - Bundle resolution moves from `BinaryTargetResolver` into `src/application/AppBundleExecutable.ts`, unchanged for macOS bundles. It tries the macOS layout, then the flat layout, then a wrapper. For a wrapper, it lists the real `Wrapper` directory and requires exactly one real `*.app` directory inside. It never follows the `WrappedBundle` symlink. The program file must still resolve inside its own program directory, and a flat bundle's escaping symlink reports `app program file leaves the bundle root`. - `inspect_plist` with no `path` still defaults to `Contents/Info.plist`. When that file does not exist and the target is a bundle, it uses the `Info.plist` beside the resolved program file instead. That file is the flat or wrapped bundle's own plist. - The directory error now says "requires a file or app bundle" instead of "macOS app bundle". The opened target's `sourcePath` remains the bundle the caller passed, such as the wrapper. Bundle-walking tools (`decode_interface_builder`, `inspect_asset_catalog`) therefore see the whole wrapper. The wrapper's link is not followed, so they visit `Wrapper/Stash.app` once. No schema or catalog change. ## Verification - `tests/boundary/filesystem/binaryTarget.test.ts`: new cases cover a flat bundle resolving to its root program file; a wrapper resolving through `Wrapper/Phone.app` with a `WrappedBundle` symlink present, then rejected once a second `Wrapper/*.app` appears; and a flat bundle's program symlink leaving the bundle being rejected. - `tests/boundary/providers/native/nativeProvider.test.ts`: `inspect_plist` on a flat bundle defaults to `<bundle>/Info.plist`. All of these fail on `main`. The existing macOS bundle, unsafe-name, missing-file, and escaping-symlink cases still pass. - Real app, through the built CLI: the table above, plus `/System/Applications/Calculator.app` still reads `Contents/Info.plist`. - `npm run check:changed`, plus the filesystem, native CLI, and native provider boundary suites (85 files), pass. The 7 ibtool-compiled NIB tests fail the same way on `main` on this host, which has only the Command Line Tools. No Hopper/Ghidra/IDA verification is needed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-16 21:17:29 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#2339
No description provided.