Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
5b8a1ef
Porthole groundwork: add macOS 26 platform
kyleve Jul 24, 2026
bf7436f
PortholeCore: wire protocol, framing, pairing crypto, secure channel
kyleve Jul 24, 2026
8961773
PortholeKit runtime: composition root, connector protocol, session ro…
kyleve Jul 24, 2026
2dad158
PortholeKit network layer: Bonjour listener, pairing manager, secure …
kyleve Jul 24, 2026
bbc238d
PortholeClientKit: Mac-side discovery, pairing, and sessions
kyleve Jul 24, 2026
dc5bd8c
porthole CLI: PortholeCLICore + command-line tool
kyleve Jul 24, 2026
b47b5a4
PortholeMCP: expose a session as an MCP stdio server
kyleve Jul 24, 2026
f6d7e31
PortholeKitUI: Broadway-styled pairing UI
kyleve Jul 24, 2026
74a02cb
Porthole Mac app: Catalyst client + macOS CI build job
kyleve Jul 24, 2026
3dd2cee
UI connector: view/accessibility tree, screenshot, open-url
kyleve Jul 24, 2026
05271d0
FileBrowserConnector + precise handler errors
kyleve Jul 24, 2026
da9cf2b
Notifications + Permissions connectors
kyleve Jul 24, 2026
72dd6c4
PortholeLifecycle: launch-state connector over LifecycleKit
kyleve Jul 24, 2026
e4658d1
SwiftDataInspector: promote the reader to a public headless API
kyleve Jul 24, 2026
54e614c
PortholeSwiftData: read-only SwiftData store connector
kyleve Jul 24, 2026
7a5e754
PortholePeriscope: logs connector (events, live tail, scopes)
kyleve Jul 24, 2026
e96076c
WherePorthole: WhereConnector + wire Porthole into the Where dev menu
kyleve Jul 24, 2026
57c19d7
Porthole: roadmap TODOs + suite README + final doc pass
kyleve Jul 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,18 @@ jobs:
path: diagnostics
if-no-files-found: warn
retention-days: 7

macos:
name: Build (macOS)
needs: format
runs-on: xcode-27
steps:
- uses: actions/checkout@v4
- uses: jdx/mise-action@v3
# The Porthole suite adds macOS-only targets that the iOS test scheme
# can't build: the `porthole` command-line tool and the Mac Catalyst
# `Porthole` app. Build both so a break in the Mac client is caught.
- name: Build Porthole CLI (macOS)
run: mise exec -- tuist build PortholeCLI -- -destination 'platform=macOS'
- name: Build Porthole app (Mac Catalyst)
run: mise exec -- tuist build PortholeApp -- -destination 'platform=macOS,variant=Mac Catalyst'
11 changes: 11 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ by `./sync-agents`.
| Platform | Minimum OS |
|------------------------------|-------------|
| iPhone, iPad, Mac Catalyst | iOS 26.0 |
| macOS (Catalyst app + CLI) | macOS 26.0 |

The root [`Package.swift`](Package.swift) targets both `.iOS(.v26)` and
`.macOS(.v26)`: most library targets are iOS-first, but the **Porthole** suite
(under `Shared/Porthole/`, see [`Shared/Porthole/README.md`](Shared/Porthole/README.md))
adds macOS-capable libraries plus a `.commandLineTool` (`porthole`) and a Mac
Catalyst app (`PortholeApp`, productName `Porthole`) in
[`Project.swift`](Project.swift). Those macOS-only targets aren't in the iOS
test scheme; CI builds them in a separate **`macos`** job
(`.github/workflows/ci.yml`) — `tuist build PortholeCLI` +
`tuist build PortholeApp` for Mac Catalyst.

Install the Where app to a connected iPhone from the CLI (no Xcode UI) with
`./Where/install` — it builds + code-signs Release and installs/launches via
Expand Down
73 changes: 72 additions & 1 deletion Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

107 changes: 107 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ let package = Package(
defaultLocalization: "en",
platforms: [
.iOS(.v26),
.macOS(.v26),
],
products: [
.library(name: "StuffCore", targets: ["StuffCore"]),
Expand All @@ -22,9 +23,28 @@ let package = Package(
.library(name: "WhereIntents", targets: ["WhereIntents"]),
.library(name: "BroadwayCore", targets: ["BroadwayCore"]),
.library(name: "BroadwayUI", targets: ["BroadwayUI"]),
.library(name: "PortholeCore", targets: ["PortholeCore"]),
.library(name: "PortholeKit", targets: ["PortholeKit"]),
.library(name: "PortholeKitUI", targets: ["PortholeKitUI"]),
.library(name: "PortholeClientKit", targets: ["PortholeClientKit"]),
.library(name: "PortholeMCP", targets: ["PortholeMCP"]),
.library(name: "PortholeCLICore", targets: ["PortholeCLICore"]),
.library(name: "PortholeLifecycle", targets: ["PortholeLifecycle"]),
.library(name: "PortholeSwiftData", targets: ["PortholeSwiftData"]),
.library(name: "PortholePeriscope", targets: ["PortholePeriscope"]),
.library(name: "WherePorthole", targets: ["WherePorthole"]),
],
dependencies: [
.package(url: "https://github.com/weichsel/ZIPFoundation", from: "0.9.20"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.8.2"),
// Pinned to a main revision rather than 0.9.0: that release's
// NetworkTransport has a Swift 6 strict-concurrency error under the
// current toolchain, fixed on main (a `MainFlag` reference replaced a
// captured `var`). Move to the next tagged release that includes it.
.package(
url: "https://github.com/modelcontextprotocol/swift-sdk",
revision: "a0ae212ebf6eab5f754c3129608bc5557637e605",
),
],
targets: [
.target(
Expand Down Expand Up @@ -108,6 +128,17 @@ let package = Package(
.target(name: "PeriscopeUI"),
.target(name: "RegionKit"),
.target(name: "SwiftDataInspector"),
// Porthole (DEBUG developer surface). Everything reaches the app
// *through* WhereUI (a dynamic framework) so a duplicate copy of
// any of these can't split type-keyed metadata — see the root
// AGENTS.md "Targets" note. The app target and WhereUITests add
// nothing new.
.target(name: "PortholeKit"),
.target(name: "PortholeKitUI"),
.target(name: "PortholePeriscope"),
.target(name: "PortholeSwiftData"),
.target(name: "PortholeLifecycle"),
.target(name: "WherePorthole"),
],
path: "Where/WhereUI/Sources",
resources: [
Expand Down Expand Up @@ -138,5 +169,81 @@ let package = Package(
],
path: "Shared/Broadway/BroadwayUI/Sources",
),
.target(
name: "PortholeCore",
path: "Shared/Porthole/PortholeCore/Sources",
),
.target(
name: "PortholeKit",
dependencies: [
.target(name: "PortholeCore"),
],
path: "Shared/Porthole/PortholeKit/Sources",
),
.target(
name: "PortholeKitUI",
dependencies: [
.target(name: "PortholeKit"),
.target(name: "BroadwayCore"),
.target(name: "BroadwayUI"),
],
path: "Shared/Porthole/PortholeKitUI/Sources",
),
.target(
name: "PortholeClientKit",
dependencies: [
.target(name: "PortholeCore"),
],
path: "Shared/Porthole/PortholeClientKit/Sources",
),
.target(
name: "PortholeMCP",
dependencies: [
.target(name: "PortholeClientKit"),
.product(name: "MCP", package: "swift-sdk"),
],
path: "Shared/Porthole/PortholeMCP/Sources",
),
.target(
name: "PortholeCLICore",
dependencies: [
.target(name: "PortholeClientKit"),
.target(name: "PortholeMCP"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
],
path: "Shared/Porthole/PortholeCLICore/Sources",
),
.target(
name: "PortholeLifecycle",
dependencies: [
.target(name: "PortholeKit"),
.target(name: "LifecycleKit"),
],
path: "Shared/Porthole/PortholeLifecycle/Sources",
),
.target(
name: "PortholeSwiftData",
dependencies: [
.target(name: "PortholeKit"),
.target(name: "SwiftDataInspector"),
],
path: "Shared/Porthole/PortholeSwiftData/Sources",
),
.target(
name: "PortholePeriscope",
dependencies: [
.target(name: "PortholeKit"),
.target(name: "PeriscopeCore"),
],
path: "Shared/Porthole/PortholePeriscope/Sources",
),
.target(
name: "WherePorthole",
dependencies: [
.target(name: "PortholeKit"),
.target(name: "WhereCore"),
],
path: "Where/WherePorthole/Sources",
),
],
)
Loading
Loading