Skip to content
Merged
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
372ce3d
feat: add Bonjour self-discovery filtering and local server advertise…
Mx-Iris Mar 3, 2026
1d6d70a
refactor: pin dependencies to versioned releases instead of branch refs
Mx-Iris Mar 15, 2026
0b6db56
refactor: pin remaining dependencies to versioned releases and remove…
Mx-Iris Mar 16, 2026
d947453
refactor: pin Core dependencies and update RunningApplicationKit version
Mx-Iris Mar 16, 2026
46bc416
refactor: consolidate LaunchServices extensions and update dependencies
Mx-Iris Mar 17, 2026
da792e2
fix: re-establish settings observation after MCP server disabled
Mx-Iris Mar 18, 2026
575487d
refactor: update dependency configurations and module aliases
Mx-Iris Mar 18, 2026
5ee48dd
chore: remove unnecessary ASSETCATALOG_COMPILER_APPICON_NAME build se…
Mx-Iris Mar 19, 2026
d4180ab
fix: use PID as identifier and improve arm64e build configuration
Mx-Iris Mar 20, 2026
a460d88
fix: differentiate MCP config names between Debug and Release builds
Mx-Iris Mar 20, 2026
f6b7497
Merge branch 'feature/bonjour-self-discovery' into improve/general-re…
Mx-Iris Mar 21, 2026
4a1b040
docs: add remote engine mirroring design spec
Mx-Iris Mar 21, 2026
84baaa1
docs: revert EngineKind, use RuntimeSource directly in RemoteEngineDe…
Mx-Iris Mar 21, 2026
983df86
refactor: update dependency versions and module configuration
Mx-Iris Mar 21, 2026
ad439c5
fix: use PID instead of bundle identifier for caller tracking
Mx-Iris Mar 21, 2026
7889188
feat: add printVTableOffset option for Swift interface generation
Mx-Iris Mar 21, 2026
4c67687
fix: improve Bonjour discovery reliability
Mx-Iris Mar 21, 2026
1820db0
fix: prevent tokenizer from destroying existing token attachments
Mx-Iris Mar 21, 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
3 changes: 1 addition & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,7 @@ jobs:
-archivePath $BUILD_PATH/RuntimeViewer.xcarchive \
-skipPackagePluginValidation \
-skipMacroValidation \
CURRENT_PROJECT_VERSION=$(date +"%Y%m%d.%H.%M") \
ASSETCATALOG_COMPILER_APPICON_NAME=""
CURRENT_PROJECT_VERSION=$(date +"%Y%m%d.%H.%M")

- name: Export macOS app
run: |
Expand Down
436 changes: 436 additions & 0 deletions Documentations/Plans/2026-03-21-remote-engine-mirroring-design.md

Large diffs are not rendered by default.

23 changes: 14 additions & 9 deletions RuntimeViewerCore/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,18 @@ let package = Package(
),
remote: .package(
url: "https://github.com/MxIris-Reverse-Engineering/MachOKit.git",
branch: "main"
from: "0.46.100"
)
),
.package(
local: .package(
path: "../../MachOObjCSection",
isRelative: true,
isEnabled: false
isEnabled: true
),
remote: .package(
url: "https://github.com/MxIris-Reverse-Engineering/MachOObjCSection.git",
from: "0.6.0-patch.1"
from: "0.6.101"
)
),
.package(
Expand All @@ -91,7 +91,7 @@ let package = Package(
),
remote: .package(
url: "https://github.com/MxIris-Reverse-Engineering/MachOSwiftSection",
branch: "main"
from: "0.8.1"
)
),
.package(
Expand All @@ -100,15 +100,15 @@ let package = Package(
),
.package(
url: "https://github.com/MxIris-Library-Forks/Semaphore",
branch: "main"
from: "0.1.0"
),
.package(
url: "https://github.com/Mx-Iris/FrameworkToolbox.git",
branch: "main"
from: "0.4.0"
),
.package(
url: "https://github.com/MxIris-macOS-Library-Forks/SwiftyXPC",
branch: "main"
from: "0.5.100"
),
.package(
url: "https://github.com/MxIris-Library-Forks/swift-memberwise-init-macro",
Expand All @@ -124,7 +124,11 @@ let package = Package(
),
.package(
url: "https://github.com/p-x9/swift-mobile-gestalt",
branch: "main"
from: "0.4.0"
),
.package(
url: "https://github.com/MxIris-Reverse-Engineering/LaunchServicesPrivate",
from: "0.1.0"
),
],
targets: [
Expand Down Expand Up @@ -165,7 +169,8 @@ let package = Package(
.target(
name: "RuntimeViewerUtilities",
dependencies: [
.product(name: "SwiftMobileGestalt", package: "swift-mobile-gestalt"),
.product(name: "SMobileGestalt", package: "swift-mobile-gestalt"),
.product(name: "LaunchServicesPrivate", package: "LaunchServicesPrivate"),
]
),
.testTarget(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ final class RuntimeNetworkServerConnection: RuntimeConnectionBase<RuntimeNetwork
#log(.info, "Creating Bonjour server with name: \(name, privacy: .public), service type: \(RuntimeNetworkBonjour.type, privacy: .public)")

let listener = try NWListener(using: parameters)
listener.service = NWListener.Service(name: name, type: RuntimeNetworkBonjour.type)
listener.service = RuntimeNetworkBonjour.makeService(name: name)
self.listener = listener

#log(.info, "Waiting for incoming Bonjour connection...")
Expand Down Expand Up @@ -484,7 +484,7 @@ final class RuntimeNetworkServerConnection: RuntimeConnectionBase<RuntimeNetwork
ownStateSubject.send(.connecting)

let newListener = try NWListener(using: listenerParameters)
newListener.service = NWListener.Service(name: serviceName, type: RuntimeNetworkBonjour.type)
newListener.service = RuntimeNetworkBonjour.makeService(name: serviceName)
self.listener = newListener

newListener.stateUpdateHandler = { [weak self] state in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ public struct OpenApplicationRequest: Codable, RuntimeRequest {

public let url: URL

public let callerBundleIdentifier: String
public let callerPID: Int32

public init(url: URL, callerBundleIdentifier: String) {
public init(url: URL, callerPID: Int32) {
self.url = url
self.callerBundleIdentifier = callerBundleIdentifier
self.callerPID = callerPID
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,44 @@ struct RuntimeRequestData: Codable {

public enum RuntimeNetworkBonjour {
public static let type = "_runtimeviewer._tcp"
public static let instanceIDKey = "rv-instance-id"

/// Unique identifier for this app process, used to filter out self-discovery in Bonjour browsing.
public static let localInstanceID = UUID().uuidString

static func makeService(name: String) -> NWListener.Service {
var txtRecord = NWTXTRecord()
txtRecord[instanceIDKey] = localInstanceID
return NWListener.Service(name: name, type: type, txtRecord: txtRecord)
}

static func instanceID(from metadata: NWBrowser.Result.Metadata) -> String? {
guard case .bonjour(let txtRecord) = metadata else { return nil }
return txtRecord[instanceIDKey]
}
}

public struct RuntimeNetworkEndpoint: Sendable, Hashable {
public let name: String

public let instanceID: String?

let endpoint: NWEndpoint
init(name: String, endpoint: NWEndpoint) {

init(name: String, instanceID: String? = nil, endpoint: NWEndpoint) {
self.name = name
self.instanceID = instanceID
self.endpoint = endpoint
}

// Exclude instanceID from equality — it is metadata, not identity.
public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.name == rhs.name && lhs.endpoint == rhs.endpoint
}

public func hash(into hasher: inout Hasher) {
hasher.combine(name)
hasher.combine(endpoint)
}
}

@Loggable
Expand All @@ -56,7 +83,7 @@ public class RuntimeNetworkBrowser {
let parameters = NWParameters()
parameters.includePeerToPeer = true

self.browser = NWBrowser(for: .bonjour(type: RuntimeNetworkBonjour.type, domain: nil), using: parameters)
self.browser = NWBrowser(for: .bonjourWithTXTRecord(type: RuntimeNetworkBonjour.type, domain: nil), using: parameters)
}

public func start(
Expand All @@ -73,13 +100,15 @@ public class RuntimeNetworkBrowser {
switch change {
case .added(let result):
if case .service(let name, _, _, _) = result.endpoint {
#log(.info, "Discovered new endpoint: \(name, privacy: .public)")
onAdded(.init(name: name, endpoint: result.endpoint))
let instanceID = RuntimeNetworkBonjour.instanceID(from: result.metadata)
#log(.info, "Discovered new endpoint: \(name, privacy: .public), instanceID: \(instanceID ?? "nil", privacy: .public)")
onAdded(.init(name: name, instanceID: instanceID, endpoint: result.endpoint))
}
case .removed(let result):
if case .service(let name, _, _, _) = result.endpoint {
let instanceID = RuntimeNetworkBonjour.instanceID(from: result.metadata)
#log(.info, "Endpoint removed: \(name, privacy: .public)")
onRemoved(.init(name: name, endpoint: result.endpoint))
onRemoved(.init(name: name, instanceID: instanceID, endpoint: result.endpoint))
}
default:
break
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ extension RuntimeObjectInterface {
printStrippedSymbolicItem: true,
emitOffsetComments: true,
printMemberAddress: true,
printVTableOffset: true,
printTypeLayout: true,
printEnumLayout: true,
synthesizeOpaqueType: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public struct SwiftGenerationOptions: Sendable, Equatable {
@Default(false)
public var printMemberAddress: Bool
@Default(false)
public var printVTableOffset: Bool
@Default(false)
public var printTypeLayout: Bool
@Default(false)
public var printEnumLayout: Bool
Expand Down Expand Up @@ -220,6 +222,7 @@ actor RuntimeSwiftSection {
printStrippedSymbolicItem: options.printStrippedSymbolicItem,
printFieldOffset: options.emitOffsetComments,
printMemberAddress: options.printMemberAddress,
printVTableOffset: options.printVTableOffset,
printTypeLayout: options.printTypeLayout,
printEnumLayout: options.printEnumLayout,
fieldOffsetTransformer: fieldOffsetTransformer,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation
import Security
import SwiftMobileGestalt
import SMobileGestalt

public enum DeviceIdentifier {
/// Returns the device's UniqueDeviceID (UDID) from MobileGestalt.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#if os(macOS) || targetEnvironment(macCatalyst)

import AppKit
import LaunchServicesPrivate

extension LSBundleProxy {
public var isSandbox: Bool {
guard let entitlements = entitlements else { return false }
guard let isSandbox = entitlements["com.apple.security.app-sandbox"] as? Bool else { return false }
return isSandbox
}
}

extension NSRunningApplication {
public var applicationProxy: LSApplicationProxy? {
guard let bundleIdentifier else { return nil }
return LSApplicationProxy(forIdentifier: bundleIdentifier)
}

public var isSandbox: Bool {
applicationProxy?.isSandbox ?? false
}
}
Comment on lines +6 to +23
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduces public APIs that mention and extend types from LaunchServicesPrivate (e.g., LSBundleProxy, LSApplicationProxy). If RuntimeViewerUtilities is intended to be a reusable/public module, this risks leaking private-framework types into its public surface area. Prefer making these extensions internal (or gating behind SPI) and expose any needed behavior through your own public wrapper types/protocols instead.

Copilot uses AI. Check for mistakes.

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ struct SwiftGenerationOptionsTests {
#expect(options.printStrippedSymbolicItem == true)
#expect(options.emitOffsetComments == false)
#expect(options.printMemberAddress == false)
#expect(options.printVTableOffset == false)
#expect(options.printTypeLayout == false)
#expect(options.printEnumLayout == false)
#expect(options.synthesizeOpaqueType == false)
Expand All @@ -81,13 +82,15 @@ struct SwiftGenerationOptionsTests {
printStrippedSymbolicItem: false,
emitOffsetComments: true,
printMemberAddress: true,
printVTableOffset: true,
printTypeLayout: true,
printEnumLayout: true,
synthesizeOpaqueType: true
)
#expect(options.printStrippedSymbolicItem == false)
#expect(options.emitOffsetComments == true)
#expect(options.printMemberAddress == true)
#expect(options.printVTableOffset == true)
#expect(options.printTypeLayout == true)
#expect(options.printEnumLayout == true)
#expect(options.synthesizeOpaqueType == true)
Expand Down Expand Up @@ -140,6 +143,7 @@ struct GenerationOptionsTests {
#expect(mcp.swiftInterfaceOptions.printStrippedSymbolicItem == true)
#expect(mcp.swiftInterfaceOptions.emitOffsetComments == true)
#expect(mcp.swiftInterfaceOptions.printMemberAddress == true)
#expect(mcp.swiftInterfaceOptions.printVTableOffset == true)
#expect(mcp.swiftInterfaceOptions.printTypeLayout == true)
#expect(mcp.swiftInterfaceOptions.printEnumLayout == true)
#expect(mcp.swiftInterfaceOptions.synthesizeOpaqueType == true)
Expand Down
2 changes: 1 addition & 1 deletion RuntimeViewerMCP/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ let package = Package(
dependencies: [
.package(path: "../RuntimeViewerCore"),
.package(path: "../RuntimeViewerPackages"),
.package(url: "https://github.com/MxIris-Library-Forks/SwiftMCP", branch: "main"),
.package(url: "https://github.com/Cocoanetics/SwiftMCP", from: "1.4.0"),
],
targets: [
.target(
Expand Down
Loading
Loading