From 9e24d3703fd906db9643bb5de2c3c8479d3c8a2a Mon Sep 17 00:00:00 2001 From: Leo Li Date: Wed, 23 Sep 2026 23:35:22 -0700 Subject: [PATCH 1/8] refactor: move the surface catalog's value types into a package Redone from current main instead of rebasing #13135 (1.8k commits behind, with main-side edits to the moved files and ~100 new consumers). SurfaceMachineID, SurfaceResourceID, SurfaceResource, the CloudVM state documents and the cmux-tui snapshot parser are pure values that the Cloud sidebar, SurfaceCatalog, the socket commands and the CLI all share. They move from the app target into Packages/macOS/CmuxSurfaceCatalogModel (depends on CmuxCore, CMUXDebugLog and CMUXMobileCore only). SurfaceCatalog, the owner, stays in the app. Besides the move: declarations become public, structs that relied on the synthesized memberwise initializer get an explicit public init with the same parameters, cmuxDebugLog becomes CMUXDebugLog.logDebugEvent, and the parser's port helpers and SurfaceResourceID.portKey move out of two larger app files because the parser calls them. VMMachineKind.swift was also a member of the cmux-cli target, so the CLI links the package. Since #13135 was cut, main split SurfaceMachineID, SurfaceDeviceInstanceID and SurfaceDevicePresence out of SurfaceCatalogModel.swift; they move too (hence the CMUXMobileCore dependency, for cmxCanonicalDeviceID). Main's new fields (agent, device workspace detail/unread/pin, machine presence, displayCreationMachines, displayPorts) are public and in the explicit inits. CmuxTuiSnapshotParser.mergingDisplays stays in the app's +Displays extension, where main moved it. 303 app, CLI and test files gain `import CmuxSurfaceCatalogModel`. Co-Authored-By: Claude Opus 5.5 --- .github/swift-warning-budget.tsv | 4 +- .github/workflows/ci-macos.yml | 1 + .github/workflows/cloud-command-deadlines.yml | 2 +- CLI/CMUXCLI+VMTransfer.swift | 1 + CLI/cmux.swift | 1 + .../CmuxSurfaceCatalogModel/Package.swift | 22 + .../macOS/CmuxSurfaceCatalogModel/README.md | 44 + .../CloudTabNameAuthority.swift | 8 +- .../CloudTabNameSource.swift | 2 +- .../CloudTuiTerminalProjectionTarget.swift | 18 + .../CloudVMState+SnapshotComparison.swift | 4 +- .../CloudVMTabState.swift | 33 + .../CmuxTuiSnapshotParser.swift | 127 ++- .../SurfaceCatalogModel.swift | 819 ++++++++++++------ .../SurfaceCatalogSnapshot.swift | 44 +- .../SurfaceDeviceInstanceID.swift | 26 +- .../SurfaceDevicePresence.swift | 34 +- .../SurfaceMachineID.swift | 22 +- .../SurfaceProjectionIdentity.swift | 23 + .../SurfaceResourceID+Ports.swift | 108 +++ .../SurfaceResourcePlacement.swift | 10 +- .../VMMachineKind.swift | 25 +- .../SurfaceCatalogMetadataTests.swift | 52 ++ .../SurfaceIdentityTests.swift | 13 + Sources/App/MacDevicesComposition.swift | 1 + Sources/AppDelegate.swift | 1 + Sources/ClosedItemHistory.swift | 1 + Sources/Cloud/CloudDiagnosticFailure.swift | 1 + Sources/Cloud/CloudDisplayCoordinator.swift | 1 + Sources/Cloud/CloudGuestDisplay.swift | 1 + Sources/Cloud/CloudGuestDisplaySnapshot.swift | 1 + Sources/Cloud/CloudMachineLink.swift | 1 + Sources/Cloud/CloudMachineLinkManager.swift | 1 + .../CloudMachineRefreshCoordinator.swift | 1 + .../CloudMachineSurfacePresentation.swift | 1 + .../Cloud/CloudNotificationPlacement.swift | 1 + Sources/Cloud/CloudNotificationSync.swift | 1 + .../Cloud/CloudSidebarDebugLabWindow.swift | 1 + .../Cloud/CloudTerminalCreationRequest.swift | 1 + ...CloudTerminalCreationRetryResolution.swift | 1 + .../CloudTerminalNavigationCatalog.swift | 1 + .../Cloud/CloudTreeDeletionPresentation.swift | 1 + Sources/Cloud/CloudTreeDeviceRow.swift | 1 + Sources/Cloud/CloudTreeMachineResources.swift | 1 + Sources/Cloud/CloudTreeNode.swift | 1 + ...udTreeNodeActions+WorkspaceLifecycle.swift | 1 + Sources/Cloud/CloudTreeNodeActions.swift | 1 + .../Cloud/CloudTreeNodeBuilder+Devices.swift | 1 + .../Cloud/CloudTreeNodeBuilder+Ports.swift | 1 + .../Cloud/CloudTreeOutlineView+Devices.swift | 1 + .../Cloud/CloudTreeOutlineView+Displays.swift | 1 + .../CloudTreeOutlineView+MachineMenu.swift | 1 + Sources/Cloud/CloudTreeOutlineView.swift | 1 + Sources/Cloud/CloudTreeRemoteWorkspaces.swift | 1 + Sources/Cloud/CloudTreeRowContentView.swift | 1 + .../Cloud/CloudTreeStyleGalleryWindow.swift | 1 + ...oudTreeTerminalNavigationCoordinator.swift | 1 + .../Cloud/CloudTuiCommandLine+Placement.swift | 1 + Sources/Cloud/CloudTuiCommandLine.swift | 1 + .../Cloud/CloudTuiManualMirrorSession.swift | 1 + .../CloudTuiPersistentRequestBuilder.swift | 1 + ...CloudTuiPersistentResourceConnection.swift | 1 + .../CloudTuiTerminalProjectionTarget.swift | 11 - Sources/Cloud/MachineCreateOperation.swift | 1 + Sources/Cloud/MachineCreateRequest.swift | 1 + Sources/Cloud/MachineSnapshotBuilder.swift | 1 + Sources/Cloud/MachinesPanelView.swift | 1 + Sources/Cloud/MachinesPanelViewModel.swift | 1 + Sources/Cloud/NewMachineModel.swift | 1 + .../PortForward/CloudBrowserAccessState.swift | 1 + .../PortForward/CloudPortRoutePlan.swift | 1 + .../CloudSidebarNotificationCoordinator.swift | 1 + .../CloudSidebarOrganizationStore.swift | 1 + .../SurfaceCatalog+SidebarOrganization.swift | 1 + .../SurfaceResource+CatalogOrdering.swift | 2 + .../TerminalController+CloudSidebar.swift | 1 + .../Cloud/SurfaceResourceDragPayload.swift | 1 + Sources/Cloud/VMCapabilities.swift | 1 + Sources/Cloud/VMClient.swift | 1 + Sources/Cloud/VMClientSocketCommands.swift | 1 + .../CloudTerminalCreationCoordinator.swift | 1 + .../CurrentWorkPalettePresentation.swift | 1 + .../AppDelegate+NewDeviceWorkspace.swift | 1 + Sources/Devices/DeviceDirectory.swift | 1 + .../DeviceDirectoryIdentityIndex.swift | 1 + Sources/Devices/DeviceDirectoryMerge.swift | 1 + Sources/Devices/DeviceDirectoryRecord.swift | 1 + Sources/Devices/DeviceIrxClient.swift | 1 + .../DeviceIrxPeerTransportProvider.swift | 1 + Sources/Devices/DeviceLink.swift | 1 + Sources/Devices/DeviceLinkAuthorization.swift | 1 + Sources/Devices/DeviceLinkRuntime.swift | 1 + Sources/Devices/DevicePresenceFrame.swift | 1 + .../DeviceRegistryDirectoryClient.swift | 1 + Sources/Devices/DeviceRouteSelector.swift | 1 + .../DeviceSurfaceProvider+Mutations.swift | 1 + ...DeviceSurfaceProvider+TerminalLayout.swift | 1 + Sources/Devices/DeviceSurfaceProvider.swift | 1 + .../DeviceSurfaceProviderRegistry.swift | 1 + .../DeviceWorkspaceCreationCoordinator.swift | 1 + .../DeviceWorkspaceLayoutCoordinator.swift | 1 + .../Devices/DeviceWorkspaceLayoutHost.swift | 1 + .../DeviceWorkspaceProjection+Layout.swift | 1 + .../Devices/DeviceWorkspaceProjection.swift | 1 + Sources/Devices/DevicesPanelViewModel.swift | 1 + Sources/Devices/DevicesPreferencesModel.swift | 1 + .../Devices/SurfaceCatalog+DeviceNames.swift | 1 + Sources/Devices/Workspace+DeviceLayout.swift | 1 + .../Workspace+DeviceMachineContext.swift | 1 + Sources/DockSplitStore+BrowserActions.swift | 1 + Sources/Hive/HiveComputersService.swift | 1 + .../Panels/BrowserPanel+CloudConnection.swift | 1 + .../Panels/PanelStableSurfaceIdentity.swift | 1 + Sources/RemoteTui/RemoteTuiMachine.swift | 1 + .../SSHTuiWorkspaceCoordinator.swift | 1 + .../RemoteTui/TerminalController+SSHTui.swift | 1 + .../TerminalController+SSHTuiSessions.swift | 1 + .../RemoteTui/Workspace+SSHTuiStatus.swift | 1 + Sources/SessionBrowserPanelSnapshot.swift | 1 + Sources/SessionPersistence.swift | 1 + .../AppDelegate+SurfaceOwnership.swift | 1 + Sources/Surfaces/CloudAgentNameContext.swift | 1 + .../CloudMachineLoadingReservation.swift | 1 + .../Surfaces/CloudPaneCreationFailure.swift | 1 + .../CloudPaneCreationFailureStore.swift | 1 + .../Surfaces/CloudPlacementCoordinator.swift | 1 + Sources/Surfaces/CloudRenameCoordinator.swift | 1 + .../CloudTerminalAttachmentResolver.swift | 1 + .../CloudTerminalCreationReceipt.swift | 1 + .../CloudTerminalLayoutCreation.swift | 1 + .../CloudTerminalLayoutCreationResult.swift | 2 + .../Surfaces/CloudTerminalPaneClosure.swift | 1 + .../CloudTerminalPaneReservation.swift | 1 + .../CloudTerminalSourcePlacement.swift | 1 + Sources/Surfaces/CloudVMTabState.swift | 13 - .../CloudWorkspaceBindingReconciliation.swift | 1 + .../CloudWorkspaceCreationCoordinator.swift | 1 + .../Surfaces/CloudWorkspaceCreationHost.swift | 1 + .../CloudWorkspaceCreationOperation.swift | 1 + .../CloudWorkspaceDeletionLedger.swift | 1 + .../CloudWorkspaceLayoutTranslator.swift | 1 + .../CloudWorkspaceProjectionCoordinator.swift | 1 + .../CloudWorkspaceProjectionEnvironment.swift | 1 + .../CloudWorkspaceProjectionPlan.swift | 1 + ...loudWorkspaceRenameService+Admission.swift | 1 + ...orkspaceRenameService+Reconciliation.swift | 1 + .../CmuxTuiSnapshotParser+Displays.swift | 1 + .../CmuxTuiSnapshotParser+Placement.swift | 1 + .../CmuxTuiSurfaceProvider+AgentNaming.swift | 1 + ...CmuxTuiSurfaceProvider+CloseTerminal.swift | 1 + .../CmuxTuiSurfaceProvider+Displays.swift | 1 + .../CmuxTuiSurfaceProvider+Environment.swift | 1 + .../CmuxTuiSurfaceProvider+FileDelivery.swift | 1 + .../CmuxTuiSurfaceProvider+GuestURL.swift | 1 + .../CmuxTuiSurfaceProvider+Hosting.swift | 1 + ...muxTuiSurfaceProvider+LayoutCreation.swift | 1 + .../CmuxTuiSurfaceProvider+ManualMirror.swift | 1 + ...CmuxTuiSurfaceProvider+Notifications.swift | 1 + ...faceProvider+PendingCreationRecovery.swift | 1 + ...CmuxTuiSurfaceProvider+PlacementSync.swift | 1 + .../CmuxTuiSurfaceProvider+PortForward.swift | 1 + .../CmuxTuiSurfaceProvider+Refresh.swift | 1 + .../CmuxTuiSurfaceProvider+TabRename.swift | 1 + ...xTuiSurfaceProvider+TerminalCreation.swift | 1 + .../CmuxTuiSurfaceProvider+TerminalIO.swift | 1 + ...uiSurfaceProvider+TerminalPrimitives.swift | 1 + .../CmuxTuiSurfaceProviderRegistry.swift | 1 + .../Surfaces/CmuxTuiSurfaceProviders.swift | 1 + Sources/Surfaces/CurrentWorkInput.swift | 1 + .../Surfaces/CurrentWorkQueryService.swift | 1 + Sources/Surfaces/CurrentWorkSnapshot.swift | 1 + .../DockSplitStore+SurfaceOwnership.swift | 1 + Sources/Surfaces/LocalSurfaceProvider.swift | 1 + Sources/Surfaces/PendingRemoteCreation.swift | 1 + .../Surfaces/SurfaceCatalog+AgentIcons.swift | 1 + .../SurfaceCatalog+CloudBinding.swift | 1 + ...urfaceCatalog+CloudDirectoryMetadata.swift | 1 + .../Surfaces/SurfaceCatalog+CloudPorts.swift | 95 +- ...SurfaceCatalog+CloudRenameSubmission.swift | 1 + ...rfaceCatalog+CloudTerminalNavigation.swift | 1 + ...faceCatalog+CloudWorkspaceProjection.swift | 1 + .../Surfaces/SurfaceCatalog+Displays.swift | 1 + Sources/Surfaces/SurfaceCatalog+Groups.swift | 1 + .../SurfaceCatalog+NameAuthority.swift | 1 + .../Surfaces/SurfaceCatalog+Ownership.swift | 1 + .../SurfaceCatalog+ProjectionQueries.swift | 1 + .../Surfaces/SurfaceCatalog+Snapshot.swift | 1 + .../SurfaceCatalog+WorkspaceDeletion.swift | 1 + .../SurfaceCatalog+WorkspaceMembership.swift | 1 + Sources/Surfaces/SurfaceCatalog.swift | 1 + .../Surfaces/SurfaceCatalogQueryService.swift | 1 + ...faceCatalogSnapshot+DeviceVisibility.swift | 1 + .../SurfaceLayoutTerminalCreating.swift | 1 + Sources/Surfaces/SurfaceOwnershipPolicy.swift | 1 + ...SurfacePaneFactory+CloudManualMirror.swift | 1 + .../SurfacePaneFactory+ProjectionLayout.swift | 1 + Sources/Surfaces/SurfacePaneFactory.swift | 1 + .../Surfaces/SurfacePlacementSyncing.swift | 1 + ...urfaceProjection+WorkspaceMembership.swift | 2 + .../SurfaceProjectionIdentity+Workspace.swift | 1 + .../Surfaces/SurfaceProjectionIdentity.swift | 9 - ...SurfaceProjectionMaterialization+Key.swift | 1 + .../SurfaceProjectionMaterialization.swift | 1 + .../SurfaceProjectionRestoreStore.swift | 1 + ...ceProvider+MaterializationValidation.swift | 1 + Sources/Surfaces/SurfaceProvider.swift | 1 + Sources/Surfaces/SurfaceRemotePlacement.swift | 2 + .../Surfaces/SurfaceResource+AgentIcon.swift | 1 + .../Surfaces/SurfaceResource+CloudTitle.swift | 1 + Sources/Surfaces/SurfaceSocketCommands.swift | 9 +- .../SurfaceWorkspaceCreationReceipt.swift | 1 + ...inalController+CloudTerminalCreation.swift | 1 + .../TerminalController+CurrentWork.swift | 1 + .../Workspace+BrowserDuplication.swift | 1 + .../Workspace+CloudDisplayOwnership.swift | 1 + .../Workspace+CloudDragSplitRouting.swift | 1 + .../Workspace+CloudLayoutProjection.swift | 1 + .../Workspace+CloudMachineLoading.swift | 1 + .../Workspace+CloudManualMirror.swift | 1 + .../Surfaces/Workspace+CloudPaneRouting.swift | 1 + .../Workspace+CloudPlacementFailure.swift | 1 + .../Workspace+CloudTerminalCreation.swift | 1 + .../Workspace+CloudTerminalReservation.swift | 1 + .../Surfaces/Workspace+SurfaceCatalog.swift | 1 + .../Surfaces/Workspace+SurfaceOwnership.swift | 1 + Sources/TerminalController.swift | 1 + .../Workspace+DetachedSurfaceTransfer.swift | 1 + ...orkspace+ForkConversationContextMenu.swift | 1 + Sources/Workspace+TerminalLinkOpening.swift | 1 + Sources/Workspace.swift | 1 + Sources/WorkspaceCloudBindingState.swift | 1 + Sources/WorkspaceSurfaceResourceDrop.swift | 1 + cmux.xcodeproj/project.pbxproj | 82 +- cmux.xcworkspace/contents.xcworkspacedata | 3 + cmuxTests/CloudCatalogQueryTestProvider.swift | 1 + cmuxTests/CloudClosedPanelRestoreTests.swift | 1 + cmuxTests/CloudDesktopAccessTests.swift | 1 + cmuxTests/CloudDesktopOpenFixture.swift | 1 + cmuxTests/CloudDesktopOpenTestProvider.swift | 1 + cmuxTests/CloudDirectoryLifecycleTests.swift | 1 + cmuxTests/CloudDirectoryTestFixture.swift | 1 + cmuxTests/CloudDisplayCatalogTests.swift | 1 + cmuxTests/CloudGuestURLRoutingTests.swift | 1 + .../CloudInitialWorkspaceNamingTests.swift | 1 + .../CloudMachineNotificationEventTests.swift | 1 + cmuxTests/CloudMachineOrderingFixture.swift | 1 + .../CloudMachineWorkspaceAdoptionTests.swift | 1 + .../CloudMachineWorkspaceTestProvider.swift | 1 + .../CloudManualMirrorTransportTests.swift | 1 + cmuxTests/CloudNameAuthorityFixture.swift | 1 + .../CloudNameAuthorityTestProvider.swift | 1 + cmuxTests/CloudNameAuthorityTests.swift | 1 + .../CloudNativeLayoutProjectionTests.swift | 1 + ...loudNotificationDismissParityHarness.swift | 1 + .../CloudNotificationDismissParityTests.swift | 1 + cmuxTests/CloudNotificationSyncTests.swift | 1 + cmuxTests/CloudOperationRecorderTests.swift | 1 + cmuxTests/CloudPaneCreationRetryTests.swift | 1 + .../CloudPlacementCoordinatorTests.swift | 1 + cmuxTests/CloudPlacementTestProvider.swift | 1 + cmuxTests/CloudPortOpenRegressionTests.swift | 1 + cmuxTests/CloudPortRoutePlanTests.swift | 1 + cmuxTests/CloudPortsVPNAffordanceTests.swift | 1 + ...CloudProviderRefreshCoordinatorTests.swift | 1 + cmuxTests/CloudRenameOptimismTests.swift | 1 + cmuxTests/CloudSidebarConsistencyTests.swift | 1 + cmuxTests/CloudSidebarNativeDropTests.swift | 1 + cmuxTests/CloudSidebarNotificationTests.swift | 1 + cmuxTests/CloudSidebarOrderingTests.swift | 1 + cmuxTests/CloudSidebarRenameFixture.swift | 1 + ...loudSidebarRenameReconciliationTests.swift | 1 + .../CloudSidebarSurfaceRegressionTests.swift | 1 + cmuxTests/CloudSurfaceDragFeedbackTests.swift | 1 + cmuxTests/CloudSurfaceDragFixture.swift | 1 + .../CloudSurfaceMoveOwnershipTests.swift | 1 + cmuxTests/CloudSurfaceOwnershipTests.swift | 1 + ...loudTerminalCreationCoordinatorTests.swift | 1 + .../CloudTerminalDragSplitRoutingTests.swift | 1 + .../CloudTerminalLayoutCreationTests.swift | 1 + ...oudTerminalNavigationCapabilityTests.swift | 1 + .../CloudTerminalNavigationFixture.swift | 1 + cmuxTests/CloudTerminalNavigationTests.swift | 1 + .../CloudTerminalPaneReservationTests.swift | 1 + .../CloudTerminalPlacementTestProvider.swift | 1 + cmuxTests/CloudTerminalPlacementTests.swift | 1 + cmuxTests/CloudTreeAvailabilityTests.swift | 1 + cmuxTests/CloudTreeMachineMenuTests.swift | 1 + .../CloudTreeMachineResourcesTests.swift | 1 + .../CloudTreeNativeDragOwnershipTests.swift | 1 + ...oudTreeOneMachineManyWorkspacesTests.swift | 1 + .../CloudTreeWorkspaceTitleLayoutTests.swift | 1 + .../CloudVMStateSnapshotComparisonTests.swift | 1 + ...CloudWorkspaceBindingRegressionTests.swift | 1 + ...loudWorkspaceCreationSidebarProvider.swift | 1 + .../CloudWorkspaceDeleteOptimismTests.swift | 1 + .../CloudWorkspaceDeleteTestProvider.swift | 1 + .../CloudWorkspaceDeletionLedgerTests.swift | 1 + .../CloudWorkspaceLayoutTranslatorTests.swift | 1 + .../CloudWorkspaceLiveProjectionTests.swift | 1 + cmuxTests/CloudWorkspaceMembershipTests.swift | 1 + ...oudWorkspaceRenameSurfaceParityTests.swift | 1 + .../CloudWorkspaceRestoreNamesTests.swift | 1 + cmuxTests/CmuxTuiSurfaceProviderTests.swift | 1 + .../CurrentWorkCommandPaletteTests.swift | 1 + cmuxTests/CurrentWorkQueryServiceTests.swift | 1 + cmuxTests/CurrentWorkReducerTests.swift | 1 + cmuxTests/DeviceDirectoryLifecycleTests.swift | 1 + cmuxTests/DeviceDirectoryMergeTests.swift | 1 + cmuxTests/DevicePresenceWireTests.swift | 1 + cmuxTests/DeviceRouteSelectorTests.swift | 1 + .../DeviceWorkspaceProjectionTests.swift | 1 + cmuxTests/DevicesCloudTreeBuilderTests.swift | 1 + cmuxTests/DevicesSidebarModeTests.swift | 1 + .../LastSurfaceClosePreferenceTests.swift | 1 + cmuxTests/LocalSurfaceProviderTests.swift | 1 + cmuxTests/MachineCreateCoordinatorTests.swift | 1 + cmuxTests/MachinesPanelModelTests.swift | 1 + ...RemoteTerminalFilePreviewLoaderTests.swift | 1 + cmuxTests/SSHTuiMigrationTests.swift | 1 + ...SetAutoTitleSocketTests+CloudSidebar.swift | 1 + .../SidebarCloudWorkspaceBadgeTests.swift | 1 + .../SurfaceCatalogQueryServiceTests.swift | 1 + cmuxTests/SurfaceCatalogTests.swift | 1 + .../SurfaceMachineIDDeviceEncodingTests.swift | 1 + cmuxTests/SurfacePaneFactoryFocusTests.swift | 1 + .../SurfaceProjectionIdentityTests.swift | 1 + cmuxTests/SurfaceSocketCommandTests.swift | 1 + .../TabManagerSessionSnapshotTests.swift | 1 + .../WorkspaceCreateInitialInputTests.swift | 1 + cmuxTests/WorkspaceTitleProvenanceTests.swift | 1 + scripts/stage-cloud-navigation-tests.py | 6 +- tests/run_cloud_command_deadline_tests.sh | 9 +- 332 files changed, 1386 insertions(+), 597 deletions(-) create mode 100644 Packages/macOS/CmuxSurfaceCatalogModel/Package.swift create mode 100644 Packages/macOS/CmuxSurfaceCatalogModel/README.md rename {Sources/Surfaces => Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel}/CloudTabNameAuthority.swift (77%) rename {Sources/Surfaces => Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel}/CloudTabNameSource.swift (63%) create mode 100644 Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudTuiTerminalProjectionTarget.swift rename {Sources/Surfaces => Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel}/CloudVMState+SnapshotComparison.swift (97%) create mode 100644 Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudVMTabState.swift rename {Sources/Surfaces => Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel}/CmuxTuiSnapshotParser.swift (95%) rename {Sources/Surfaces => Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel}/SurfaceCatalogModel.swift (75%) rename {Sources/Surfaces => Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel}/SurfaceCatalogSnapshot.swift (51%) rename {Sources/Surfaces => Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel}/SurfaceDeviceInstanceID.swift (78%) rename {Sources/Surfaces => Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel}/SurfaceDevicePresence.swift (76%) rename {Sources/Surfaces => Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel}/SurfaceMachineID.swift (65%) create mode 100644 Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceProjectionIdentity.swift create mode 100644 Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceResourceID+Ports.swift rename {Sources/Surfaces => Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel}/SurfaceResourcePlacement.swift (76%) rename {Sources/Cloud => Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel}/VMMachineKind.swift (80%) create mode 100644 Packages/macOS/CmuxSurfaceCatalogModel/Tests/CmuxSurfaceCatalogModelTests/SurfaceCatalogMetadataTests.swift create mode 100644 Packages/macOS/CmuxSurfaceCatalogModel/Tests/CmuxSurfaceCatalogModelTests/SurfaceIdentityTests.swift delete mode 100644 Sources/Cloud/CloudTuiTerminalProjectionTarget.swift delete mode 100644 Sources/Surfaces/CloudVMTabState.swift delete mode 100644 Sources/Surfaces/SurfaceProjectionIdentity.swift diff --git a/.github/swift-warning-budget.tsv b/.github/swift-warning-budget.tsv index 6ee8fa68faf0..7f616d94d3a6 100644 --- a/.github/swift-warning-budget.tsv +++ b/.github/swift-warning-budget.tsv @@ -3,6 +3,8 @@ # Reduce counts when warnings are fixed. CI fails if new warnings exceed this budget. 1 CLI/cmux.swift literal value is already handled by previous pattern; consider removing it 2 CLI/cmux.swift variable 'groups' was never mutated; consider changing to 'let' constant +1 Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CmuxTuiSnapshotParser.swift default will never be executed +1 Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceCatalogModel.swift immutable value 'rowID' was never used; consider replacing with '_' or removing it 1 Sources/App/TerminalDirectoryOpenSupport.swift 'fullPath(forApplication:)' was deprecated in macOS 11.0: Use -[NSWorkspace URLForApplicationWithBundleIdentifier:] instead. 2 Sources/AppDelegate+PaneMemoryGuardrail.swift trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning 1 Sources/AppDelegate.swift 'accessibilityAttributeValue' was deprecated in macOS 10.10: Use the NSAccessibility protocol methods instead (see NSAccessibilityProtocols.h) @@ -78,8 +80,6 @@ 1 Sources/SessionIndexTableController.swift main actor-isolated property 'isApplyingRows' can not be referenced from a Sendable closure 2 Sources/SessionIndexView.swift 'onChange(of:perform:)' was deprecated in macOS 14.0: Use `onChange` with a two or zero parameter action closure instead. 1 Sources/Settings/ConfigSettingsView.swift 'onChange(of:perform:)' was deprecated in macOS 14.0: Use `onChange` with a two or zero parameter action closure instead. -1 Sources/Surfaces/CmuxTuiSnapshotParser.swift default will never be executed -1 Sources/Surfaces/SurfaceCatalogModel.swift immutable value 'rowID' was never used; consider replacing with '_' or removing it 17 Sources/TabManager.swift result of call to 'closePanel(_:force:)' is unused 1 Sources/TerminalController.swift 'CGWindowListCreateImage' was deprecated in macOS 14.0: Please use ScreenCaptureKit instead. 2 Sources/TerminalController.swift 'activateIgnoringOtherApps' was deprecated in macOS 14.0: ignoringOtherApps is deprecated in macOS 14 and will have no effect. diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index e4692800a100..b75cbd27b94d 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -2430,6 +2430,7 @@ jobs: CmuxNotifications CmuxSettings CmuxSettingsUI + CmuxSurfaceCatalogModel CmuxSudoBroker CmuxSudoBrokerUI CmuxTerminal diff --git a/.github/workflows/cloud-command-deadlines.yml b/.github/workflows/cloud-command-deadlines.yml index ea48339eb027..d701ab8b90d3 100644 --- a/.github/workflows/cloud-command-deadlines.yml +++ b/.github/workflows/cloud-command-deadlines.yml @@ -4,7 +4,7 @@ on: paths: - Sources/Cloud/CloudTuiPersistent*.swift - Sources/Cloud/CloudTuiManualIO*.swift - - Sources/Cloud/CloudTuiTerminalProjectionTarget.swift + - Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudTuiTerminalProjectionTarget.swift - Sources/Cloud/CloudTuiRemoteColors.swift - cmuxTests/CloudCommand*.swift - cmuxTests/CloudTuiManualIOConnectionTests.swift diff --git a/CLI/CMUXCLI+VMTransfer.swift b/CLI/CMUXCLI+VMTransfer.swift index ca541ecbb214..fa59180689bf 100644 --- a/CLI/CMUXCLI+VMTransfer.swift +++ b/CLI/CMUXCLI+VMTransfer.swift @@ -1,4 +1,5 @@ import CmuxSettings +import CmuxSurfaceCatalogModel import CryptoKit import Foundation diff --git a/CLI/cmux.swift b/CLI/cmux.swift index d727961af79d..8a4c5ba2b42d 100644 --- a/CLI/cmux.swift +++ b/CLI/cmux.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import CMUXAgentLaunch import CmuxAgentJournal diff --git a/Packages/macOS/CmuxSurfaceCatalogModel/Package.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Package.swift new file mode 100644 index 000000000000..dd03d249eaaa --- /dev/null +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Package.swift @@ -0,0 +1,22 @@ +// swift-tools-version: 6.0 +import PackageDescription + +let package = Package( + name: "CmuxSurfaceCatalogModel", + platforms: [.macOS(.v14)], + products: [.library(name: "CmuxSurfaceCatalogModel", targets: ["CmuxSurfaceCatalogModel"])], + dependencies: [ + .package(path: "../CMUXDebugLog"), + .package(path: "../CmuxCore"), + .package(path: "../../Shared/CMUXMobileCore") + ], + targets: [ + .target( + name: "CmuxSurfaceCatalogModel", + dependencies: ["CMUXDebugLog", "CmuxCore", "CMUXMobileCore"], + // The files moved out of the app target unchanged; keep its language mode. + swiftSettings: [.swiftLanguageMode(.v5)] + ), + .testTarget(name: "CmuxSurfaceCatalogModelTests", dependencies: ["CmuxSurfaceCatalogModel"]) + ] +) diff --git a/Packages/macOS/CmuxSurfaceCatalogModel/README.md b/Packages/macOS/CmuxSurfaceCatalogModel/README.md new file mode 100644 index 000000000000..99f94384a387 --- /dev/null +++ b/Packages/macOS/CmuxSurfaceCatalogModel/README.md @@ -0,0 +1,44 @@ +# CmuxSurfaceCatalogModel + +The surface catalog's value types: the vocabulary shared by `SurfaceCatalog`, the Cloud sidebar, the socket commands and the CLI. Pure `Hashable`/`Codable`/`Sendable` values plus the cmux-tui snapshot parser that produces them. No app imports; depends on `CmuxCore`, `CMUXDebugLog` and `CMUXMobileCore` (for device-ID canonicalization). + +```swift +import CmuxSurfaceCatalogModel + +let id = SurfaceResourceID(machine: .cloud("vm-1"), kind: .terminal, key: "t1") +assert(SurfaceResourceID(rawValue: id.rawValue) == id) +``` + +`SurfaceCatalog` itself (the owner, `@MainActor`, app-coupled) stays in the app target. + +## Moved files + +From `Sources/Surfaces/`: `SurfaceCatalogModel.swift`, `SurfaceCatalogSnapshot.swift`, `SurfaceResourcePlacement.swift`, `CloudVMTabState.swift`, `CloudTabNameAuthority.swift`, `CloudTabNameSource.swift`, `CmuxTuiSnapshotParser.swift`, `CloudVMState+SnapshotComparison.swift` (it holds `CloudVMState`'s `==`), `SurfaceMachineID.swift`, `SurfaceDeviceInstanceID.swift`, `SurfaceDevicePresence.swift`, `SurfaceProjectionIdentity.swift`. +From `Sources/Cloud/`: `VMMachineKind.swift` (was also a member of the `cmux-cli` target, so the CLI links this package), `CloudTuiTerminalProjectionTarget.swift`. + +`SurfaceResourceID+Ports.swift` is new: the parser's listening-port helpers (from `SurfaceCatalog+CloudPorts.swift`) and `SurfaceResourceID.portKey`/`desktopDisplayKey` (from `SurfaceSocketCommands.swift`), which the moved parser calls. + +## What changed besides the move + +- Declarations became `public`. Structs that relied on the synthesized memberwise initializer got an explicit `public init` with the same parameters and defaults. +- `cmuxDebugLog(...)` (an app global) became `CMUXDebugLog.logDebugEvent(...)`, still under `#if DEBUG`. + +## Current-main boundary + +`SurfaceProjectionIdentity` is also a package value: catalog exports carry stable +surface/workspace ownership metadata beside runtime projection selectors. Its +`Workspace` capture extension stays in the app; the package neither looks up nor +owns live workspaces. Snapshot creation/deletion metadata retains its optional +wire fields and backwards-compatible decoding. + +`SurfaceResourceGroup` and `SurfaceProjectionLayout` still live in the app. Their +pure declarations are candidates for a later boundary change; catalog projection +orchestration, provider lookup, and layout application must remain above this +value layer. This extraction does not move the navigation coordinator or establish +a Cloud package. + +Run the production value tests without launching the app: + +```sh +swift test --package-path Packages/macOS/CmuxSurfaceCatalogModel +``` diff --git a/Sources/Surfaces/CloudTabNameAuthority.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudTabNameAuthority.swift similarity index 77% rename from Sources/Surfaces/CloudTabNameAuthority.swift rename to Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudTabNameAuthority.swift index f53f67506652..92510a03f006 100644 --- a/Sources/Surfaces/CloudTabNameAuthority.swift +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudTabNameAuthority.swift @@ -3,11 +3,11 @@ import Foundation /// The daemon persists provenance separately from display text. Legacy named /// tabs are user-owned; a daemon without revision metadata cannot admit auto names. /// The extension map keeps snapshots compatible with released strict SDK decoders. -struct CloudTabNameAuthority: Hashable, Codable, Sendable { - let source: CloudTabNameSource - let revision: UInt64 +public struct CloudTabNameAuthority: Hashable, Codable, Sendable { + public let source: CloudTabNameSource + public let revision: UInt64 - init?(snapshot: [String: Any]) { + public init?(snapshot: [String: Any]) { guard let metadata = snapshot["extra"] as? [String: Any], let raw = metadata["name_source"] as? String, let source = CloudTabNameSource(rawValue: raw), diff --git a/Sources/Surfaces/CloudTabNameSource.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudTabNameSource.swift similarity index 63% rename from Sources/Surfaces/CloudTabNameSource.swift rename to Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudTabNameSource.swift index 052d18793625..6a6f6a396665 100644 --- a/Sources/Surfaces/CloudTabNameSource.swift +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudTabNameSource.swift @@ -1,5 +1,5 @@ /// The durable writer whose name currently overrides the process title. -enum CloudTabNameSource: String, Codable, Sendable { +public enum CloudTabNameSource: String, Codable, Sendable { case user case auto } diff --git a/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudTuiTerminalProjectionTarget.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudTuiTerminalProjectionTarget.swift new file mode 100644 index 000000000000..81ff15659322 --- /dev/null +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudTuiTerminalProjectionTarget.swift @@ -0,0 +1,18 @@ +/// A concrete remote pane location accepted by cmux-tui's `terminal.project` command. +/// +/// The public terminal resource can outlive every tab that displays it. When that happens, +/// the native macOS mirror first creates one remote view in an existing pane, then resolves +/// the daemon-local surface id for the byte attachment. +public struct CloudTuiTerminalProjectionTarget: Equatable, Sendable { + public let workspaceID: String + public let screenID: String + public let paneID: String + public let index: Int + + public init(workspaceID: String, screenID: String, paneID: String, index: Int) { + self.workspaceID = workspaceID + self.screenID = screenID + self.paneID = paneID + self.index = index + } +} diff --git a/Sources/Surfaces/CloudVMState+SnapshotComparison.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudVMState+SnapshotComparison.swift similarity index 97% rename from Sources/Surfaces/CloudVMState+SnapshotComparison.swift rename to Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudVMState+SnapshotComparison.swift index 9a26f591630e..ea1f84421281 100644 --- a/Sources/Surfaces/CloudVMState+SnapshotComparison.swift +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudVMState+SnapshotComparison.swift @@ -1,7 +1,7 @@ import Foundation extension CloudVMState { - static func == (lhs: CloudVMState, rhs: CloudVMState) -> Bool { + public static func == (lhs: CloudVMState, rhs: CloudVMState) -> Bool { lhs.hasSameModeledContent(as: rhs) && lhs.document == rhs.document } @@ -10,7 +10,7 @@ extension CloudVMState { /// them in exports without treating inspection or resize as a conflict. /// Compare resource rows by identity. Positional keys keep unkeyed arrays /// strict, and every unknown field inside a row remains part of equality. - func hasSameRevisionedContent(as other: CloudVMState) -> Bool { + public func hasSameRevisionedContent(as other: CloudVMState) -> Bool { hasSameRevisionedModeledContent(as: other) && document.values.filter { $0.key != "clients" && $0.key != "session" } == other.document.values.filter { $0.key != "clients" && $0.key != "session" } diff --git a/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudVMTabState.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudVMTabState.swift new file mode 100644 index 000000000000..01f4141b190c --- /dev/null +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudVMTabState.swift @@ -0,0 +1,33 @@ +import Foundation + +/// One accepted daemon tab, including the durable owner and revision of its name. +public struct CloudVMTabState: Hashable, Codable, Sendable { + public var id: String + public var paneID: String + public var name: String? + public var index: Int + public var focused: Bool + public var contentKind: String + public var contentID: String + public var nameAuthority: CloudTabNameAuthority? = nil + + public init( + id: String, + paneID: String, + name: String? = nil, + index: Int, + focused: Bool, + contentKind: String, + contentID: String, + nameAuthority: CloudTabNameAuthority? = nil + ) { + self.id = id + self.paneID = paneID + self.name = name + self.index = index + self.focused = focused + self.contentKind = contentKind + self.contentID = contentID + self.nameAuthority = nameAuthority + } +} diff --git a/Sources/Surfaces/CmuxTuiSnapshotParser.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CmuxTuiSnapshotParser.swift similarity index 95% rename from Sources/Surfaces/CmuxTuiSnapshotParser.swift rename to Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CmuxTuiSnapshotParser.swift index f43347cc8d6a..11eeb0d562d9 100644 --- a/Sources/Surfaces/CmuxTuiSnapshotParser.swift +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CmuxTuiSnapshotParser.swift @@ -1,3 +1,6 @@ +#if DEBUG +import CMUXDebugLog +#endif import CmuxCore import CoreFoundation import Foundation @@ -13,25 +16,35 @@ import Foundation /// `agents[{id?,terminal_id,state,source}]`. /// A tab's `name` is the user-set label (`tab.rename`, persisted in the daemon's /// registry); the terminal's `title` is PTY-derived. A named view wins over the title. -struct CloudVMStateDeltaImpact: Hashable, Sendable { +public struct CloudVMStateDeltaImpact: Hashable, Sendable { /// Resource identities whose derived rows can be rebuilt without touching unrelated rows. - var resourceIDs: Set = [] + public var resourceIDs: Set = [] /// Relationship changes can move many resources at once. These use the authoritative full /// rebuild path instead of risking a partial placement update. - var requiresFullResourceRebuild = false + public var requiresFullResourceRebuild = false + + public init(resourceIDs: Set = [], requiresFullResourceRebuild: Bool = false) { + self.resourceIDs = resourceIDs + self.requiresFullResourceRebuild = requiresFullResourceRebuild + } } -struct CloudVMStateDeltaApplication: Sendable { - let state: CloudVMState - let impact: CloudVMStateDeltaImpact +public struct CloudVMStateDeltaApplication: Sendable { + public let state: CloudVMState + public let impact: CloudVMStateDeltaImpact + + public init(state: CloudVMState, impact: CloudVMStateDeltaImpact) { + self.state = state + self.impact = impact + } } -struct CmuxTuiSnapshotParser: Sendable { +public struct CmuxTuiSnapshotParser: Sendable { /// Chooses a stable destination for projecting a terminal that currently has no remote /// tab view. The session snapshot lists structural records separately, so selection walks /// the focused workspace, focused screen, and focused pane in that order, using explicit /// row indexes when available and a legacy daemon-order fallback otherwise. A new tab is /// appended to the chosen pane. - static func terminalProjectionTarget(from snapshot: [String: Any]) -> CloudTuiTerminalProjectionTarget? { + public static func terminalProjectionTarget(from snapshot: [String: Any]) -> CloudTuiTerminalProjectionTarget? { guard requiredGraphCollectionsArePresent(in: snapshot) else { return nil } let workspaces = snapshot["workspaces"] as? [[String: Any]] ?? [] let screens = snapshot["screens"] as? [[String: Any]] ?? [] @@ -75,7 +88,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// Returns the decimal resource revision carried by a public session /// snapshot. It is used as an optimistic-concurrency fence when a detached /// terminal is projected into a pane selected from that snapshot. - static func resourceRevision(from snapshot: [String: Any]) -> String? { + public static func resourceRevision(from snapshot: [String: Any]) -> String? { guard let cursor = snapshot["cursor"] as? [String: Any] else { return nil } if let revision = cursor["revision"] as? String, !revision.isEmpty, @@ -102,7 +115,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// A missing or explicit-null cursor is a legacy snapshot-only state. A /// present cursor must be valid, because accepting a malformed ordering token /// would make later deltas target the wrong graph. - static func state(fromSnapshot snapshot: [String: Any], machine: SurfaceMachineID) -> CloudVMState? { + public static func state(fromSnapshot snapshot: [String: Any], machine: SurfaceMachineID) -> CloudVMState? { let cursor: CloudVMCursor? if let rawCursor = snapshot["cursor"], !(rawCursor is NSNull) { guard let parsed = CloudVMCursor(snapshot: snapshot) else { return nil } @@ -283,7 +296,7 @@ struct CmuxTuiSnapshotParser: Sendable { } /// The same identity and foreign-key contract gates accepted state and mutations. - static func authoritativeGraphIsValid(_ snapshot: [String: Any]) -> Bool { + public static func authoritativeGraphIsValid(_ snapshot: [String: Any]) -> Bool { identityCollectionsAreUnique(in: snapshot) && requiredGraphCollectionsArePresent(in: snapshot) && snapshotRelationshipsAreConsistent(in: snapshot) @@ -395,12 +408,12 @@ struct CmuxTuiSnapshotParser: Sendable { /// before the second, stacked panes in their listed order, viewport columns /// left to right. Panes the document does not mention, and an unreadable /// document, get no position, so callers fall back to arrival order. - static func layoutPaneOrder(fromLayout layout: Any?) -> [String: Int] { + public static func layoutPaneOrder(fromLayout layout: Any?) -> [String: Int] { RemoteWorkspacePaneOrder(document: layout).positions } /// The same order read from a screen state's opaque layout data (the delta path). - static func layoutPaneOrder(fromLayoutData data: Data?) -> [String: Int] { + public static func layoutPaneOrder(fromLayoutData data: Data?) -> [String: Int] { RemoteWorkspacePaneOrder(data: data).positions } @@ -409,7 +422,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// order remains the compatibility fallback. An explicit index wins over /// an omitted one, and equal explicit indexes use the stable id before the /// transport offset as a deterministic tie-break. - static func orderedSnapshotRows( + public static func orderedSnapshotRows( _ rows: [[String: Any]], focusedFirst: Bool = false ) -> [(offset: Int, element: [String: Any])] { @@ -466,7 +479,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// Re-derives the compatibility resources from the exact state bytes. No /// resource mutation path is allowed to maintain a second remote graph. - static func resources(from state: CloudVMState) -> [SurfaceResource] { + public static func resources(from state: CloudVMState) -> [SurfaceResource] { guard let snapshot = state.snapshotObject() else { return [] } return resources(fromSnapshot: snapshot, machine: state.machine) } @@ -475,7 +488,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// joins through pane and screen with constant-time lookups; unrelated graph rows are not /// allocated, sorted, or compared. The provider uses this for row-local deltas and reserves /// the complete path for topology changes. - static func resources( + public static func resources( from state: CloudVMState, matching resourceIDs: Set ) -> [SurfaceResource] { @@ -593,7 +606,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// provider whether it can update selected resource rows or must rebuild all /// relationships. Upserts replace an entity in place, deletes remove it, and /// unknown resource kinds refuse the batch so the caller can fetch a snapshot. - static func applying( + public static func applying( deltaPayload: Data, cursor: CloudVMCursor, to state: CloudVMState @@ -606,12 +619,12 @@ struct CmuxTuiSnapshotParser: Sendable { /// visible in the debug log instead of inferred from recovery cadence. private static func rejectDelta(_ reason: String) -> CloudVMStateDeltaApplication? { #if DEBUG - cmuxDebugLog("cloud.state.deltaRejectReason reason=\(reason)") + CMUXDebugLog.logDebugEvent("cloud.state.deltaRejectReason reason=\(reason)") #endif return nil } - static func applyingWithImpact( + public static func applyingWithImpact( deltaPayload: Data, cursor: CloudVMCursor, to state: CloudVMState @@ -732,7 +745,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// edges. private static func rejectTyped(_ reason: String) -> CloudVMState? { #if DEBUG - cmuxDebugLog("cloud.state.deltaRejectReason reason=\(reason)") + CMUXDebugLog.logDebugEvent("cloud.state.deltaRejectReason reason=\(reason)") #endif return nil } @@ -1337,7 +1350,7 @@ struct CmuxTuiSnapshotParser: Sendable { } /// Legacy entry point retained for callers that only have a one-shot snapshot. - static func terminals(fromSnapshot snapshot: [String: Any], machine: SurfaceMachineID) -> [SurfaceResource] { + public static func terminals(fromSnapshot snapshot: [String: Any], machine: SurfaceMachineID) -> [SurfaceResource] { resources(fromSnapshot: snapshot, machine: machine) } @@ -1349,7 +1362,7 @@ struct CmuxTuiSnapshotParser: Sendable { #else @Sendable #endif - nonisolated static func terminalProjectionTarget( + public nonisolated static func terminalProjectionTarget( from data: Data ) async -> (target: CloudTuiTerminalProjectionTarget, revision: String?)? { guard let snapshot = try? JSONSerialization.jsonObject(with: data) as? [String: Any], @@ -1576,7 +1589,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// The machine-local port a daemon browser's URL points at, when it does — /// `http://localhost:3000/...` and equivalents. A remote browser projects through the /// machine's port preview, so only localhost URLs are projectable today. - static func localhostPort(fromURL urlString: String) -> Int? { + public static func localhostPort(fromURL urlString: String) -> Int? { guard let url = URL(string: urlString), let host = url.host?.lowercased() else { return nil } guard ["localhost", "127.0.0.1", "::1", "[::1]", "0.0.0.0"].contains(host) else { return nil } if let port = url.port { return port } @@ -1590,7 +1603,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// The tab each terminal currently sits in. The reverse tab content edge is /// authoritative; terminal-row references are retained as a legacy fallback /// so an exited terminal can still be closed when its own selector is gone. - static func tabByTerminal(fromSnapshot snapshot: [String: Any]) -> [String: String] { + public static func tabByTerminal(fromSnapshot snapshot: [String: Any]) -> [String: String] { var result: [String: String] = [:] for tab in (snapshot["tabs"] as? [[String: Any]]) ?? [] { guard nonEmptyString(tab["content_kind"]) == "terminal", @@ -1616,7 +1629,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// User labels for tabs in the snapshot. An absent entry means the tab has no /// user label (or only whitespace), which lets rename compensation restore the /// daemon's unnamed state with an empty value. - static func tabNames(fromSnapshot snapshot: [String: Any]) -> [String: String] { + public static func tabNames(fromSnapshot snapshot: [String: Any]) -> [String: String] { var result: [String: String] = [:] for tab in (snapshot["tabs"] as? [[String: Any]]) ?? [] { guard let id = tab["id"] as? String, !id.isEmpty, @@ -1629,7 +1642,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// The workspace and first terminal a `workspace create` mutation made /// (`{value: {workspace_id, terminal_id, …}}`). - static func createdWorkspaceTerminal(fromResult result: [String: Any]) -> (workspaceID: String, terminalID: String?)? { + public static func createdWorkspaceTerminal(fromResult result: [String: Any]) -> (workspaceID: String, terminalID: String?)? { let path = (result["value"] as? [String: Any]) ?? result guard let workspaceID = ((path["workspace_id"] as? String) ?? (path["id"] as? String)), !workspaceID.isEmpty else { return nil } return (workspaceID, (path["terminal_id"] as? String).flatMap { $0.isEmpty ? nil : $0 }) @@ -1637,7 +1650,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// The daemon's workspaces, in its order — including empty ones, which have no /// terminal to derive them from. - static func workspaces(fromSnapshot snapshot: [String: Any]) -> [SurfaceRemoteWorkspace] { + public static func workspaces(fromSnapshot snapshot: [String: Any]) -> [SurfaceRemoteWorkspace] { let workspacesRaw = (snapshot["workspaces"] as? [[String: Any]]) ?? [] return orderedSnapshotRows(workspacesRaw).compactMap { entry in let raw = entry.element @@ -1652,7 +1665,7 @@ struct CmuxTuiSnapshotParser: Sendable { } } - static func terminal( + public static func terminal( fromSnapshotEntry raw: [String: Any], machine: SurfaceMachineID, agents: [String: SurfaceAgentBadge] = [:] @@ -1674,23 +1687,41 @@ struct CmuxTuiSnapshotParser: Sendable { ) } - struct CreatedTerminalPath: Equatable, Sendable { - var attachment: CloudCreationAttachment? = nil - let terminalID: String - let workspaceID: String? - let screenID: String? - let paneID: String? - let tabID: String? + public struct CreatedTerminalPath: Equatable, Sendable { + public var attachment: CloudCreationAttachment? = nil + public let terminalID: String + public let workspaceID: String? + public let screenID: String? + public let paneID: String? + public let tabID: String? /// The daemon commit position for this creation. A mutation result is /// also a read-your-write receipt, so a lagging snapshot can be /// recognized without guessing how long the event feed needs. - let cursor: CloudVMCursor? + public let cursor: CloudVMCursor? + + public init( + attachment: CloudCreationAttachment? = nil, + terminalID: String, + workspaceID: String?, + screenID: String?, + paneID: String?, + tabID: String?, + cursor: CloudVMCursor? + ) { + self.attachment = attachment + self.terminalID = terminalID + self.workspaceID = workspaceID + self.screenID = screenID + self.paneID = paneID + self.tabID = tabID + self.cursor = cursor + } } /// The exact path a `workspace run` / `tab create terminal` mutation /// created. The committed result is a read-your-write placement receipt, so /// callers do not need to guess a tab while the next snapshot is in flight. - static func createdTerminal(fromRunResult result: [String: Any]) -> CreatedTerminalPath? { + public static func createdTerminal(fromRunResult result: [String: Any]) -> CreatedTerminalPath? { let path = (result["value"] as? [String: Any]) ?? result guard let terminalID = path["terminal_id"] as? String, !terminalID.isEmpty else { return nil } func optionalID(_ key: String) -> String? { @@ -1716,7 +1747,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// object in `result` or put the cursor under `cursor`. Accept those /// equivalent shapes, but never invent a generation or revision from a /// partial response. - static func mutationCursor( + public static func mutationCursor( fromResult result: [String: Any], fallbackGeneration: String? = nil ) -> CloudVMCursor? { @@ -1744,7 +1775,7 @@ struct CmuxTuiSnapshotParser: Sendable { } /// The workspace a `workspace create` mutation created. - static func createdWorkspace(fromResult result: [String: Any]) -> String? { + public static func createdWorkspace(fromResult result: [String: Any]) -> String? { let path = (result["value"] as? [String: Any]) ?? result let id = (path["workspace_id"] as? String) ?? (path["workspace"] as? String) @@ -1754,7 +1785,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// `remote connect --headless --json` prints `{"event":"connection-snapshot","local_socket":…}` /// lines; the first one carries the mux socket path. - static func localSocket(fromLinkLine line: String) -> String? { + public static func localSocket(fromLinkLine line: String) -> String? { guard let data = line.data(using: .utf8), let object = try? JSONSerialization.jsonObject(with: data) as? [String: Any], (object["event"] as? String) == "connection-snapshot", @@ -1765,7 +1796,7 @@ struct CmuxTuiSnapshotParser: Sendable { } /// Listening TCP ports from `ss -ltn` / `netstat -ltn` output (what `cmux vm ports` runs). - static func listeningPorts(fromSocketListing text: String) -> [Int] { + public static func listeningPorts(fromSocketListing text: String) -> [Int] { Set(listeningPortBindings(fromSocketListing: text).map(\.port)).sorted() } @@ -1775,21 +1806,21 @@ struct CmuxTuiSnapshotParser: Sendable { /// image's internal 8080 listener. /// Guest display slots use these private RFB/noVNC ports; they are owned by /// the display catalog and must never become generic forwarded-port rows. - static let internalPorts: Set = [22, 1337, 8080] - static let displayPorts: Set = Set(Array(5901...5916) + Array(6901...6916)) + public static let internalPorts: Set = [22, 1337, 8080] + public static let displayPorts: Set = Set(Array(5901...5916) + Array(6901...6916)) - static let desktopPort = 6901 + public static let desktopPort = 6901 /// Fallback for callers that only hold an image id. Prefer /// ``VMSummary/resolvedKind``, which honors the backend's explicit `kind`. /// Only VNC markers count: see ``VMMachineKind/inferred(fromImage:)``. - static func machineHasDesktop(image: String) -> Bool { + public static func machineHasDesktop(image: String) -> Bool { VMMachineKind.inferred(fromImage: image).hasDesktop } /// The VNC display of a desktop machine (`display:1`; the key is the daemon's content id /// once a workspace points at it). - static func display( + public static func display( machine: SurfaceMachineID, key: String = "display:1", directURL: String? = nil @@ -1811,7 +1842,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// address over the WireGuard tunnel, never a provider port-forwarding /// proxy (Freestyle's public platform has none for arbitrary ports). nil /// means the resource cannot open until the machine has a private address. - static func portBrowser(machine: SurfaceMachineID, port: Int, directURL: String? = nil) -> SurfaceResource { + public static func portBrowser(machine: SurfaceMachineID, port: Int, directURL: String? = nil) -> SurfaceResource { SurfaceResource( id: SurfaceResourceID(machine: machine, kind: .browser, key: SurfaceResourceID.portKey(port)), title: ":\(port)", @@ -1826,7 +1857,7 @@ struct CmuxTuiSnapshotParser: Sendable { /// The noVNC page recipe `cmux vm desktop` uses: auto-connect, follow the pane's size, /// reconnect after a sleep. - static func desktopURL(openURL: String) -> String { + public static func desktopURL(openURL: String) -> String { openURL + "&autoconnect=1&resize=remote&reconnect=1&reconnect_delay=2000" } diff --git a/Sources/Surfaces/SurfaceCatalogModel.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceCatalogModel.swift similarity index 75% rename from Sources/Surfaces/SurfaceCatalogModel.swift rename to Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceCatalogModel.swift index 34ffaea12c57..ca94866bbe53 100644 --- a/Sources/Surfaces/SurfaceCatalogModel.swift +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceCatalogModel.swift @@ -7,7 +7,7 @@ import Foundation // "is this terminal open somewhere?" has one answer and closing a pane never destroys a // remote resource. Pure values here; the owner is `SurfaceCatalog`. -enum SurfaceResourceKind: String, Codable, Sendable, CaseIterable { +public enum SurfaceResourceKind: String, Codable, Sendable, CaseIterable { case terminal /// A VNC display on the machine ("display", never "screen": a cmux-tui `screen` is a /// split tree inside a workspace, a different thing). @@ -16,7 +16,7 @@ enum SurfaceResourceKind: String, Codable, Sendable, CaseIterable { /// Wire-tolerant parse: pre-rename catalogs, persisted sessions, and older CLIs say /// `screen` for a VNC display. Emit `display`, accept both. - init?(wire: String) { + public init?(wire: String) { if wire == "screen" { self = .display return @@ -24,7 +24,7 @@ enum SurfaceResourceKind: String, Codable, Sendable, CaseIterable { self.init(rawValue: wire) } - init(from decoder: any Decoder) throws { + public init(from decoder: any Decoder) throws { let raw = try decoder.singleValueContainer().decode(String.self) guard let kind = SurfaceResourceKind(wire: raw) else { throw DecodingError.dataCorrupted(DecodingError.Context( @@ -39,30 +39,30 @@ enum SurfaceResourceKind: String, Codable, Sendable, CaseIterable { /// Stable identity of a resource. `key` is the provider's own id: a local panel UUID /// string, a cmux-tui `term_…`/`browser_…` id, `display:1` for a VNC display, or /// `port:` for a forwarded port's browser. -struct SurfaceResourceID: Hashable, Codable, Sendable, CustomStringConvertible { - var machine: SurfaceMachineID - var kind: SurfaceResourceKind - var key: String +public struct SurfaceResourceID: Hashable, Codable, Sendable, CustomStringConvertible { + public var machine: SurfaceMachineID + public var kind: SurfaceResourceKind + public var key: String - var description: String { "\(machine.rawValue)/\(kind.rawValue)/\(key)" } + public var description: String { "\(machine.rawValue)/\(kind.rawValue)/\(key)" } /// Wire form `//`; keys may contain `/` (URLs), so split only twice. - var rawValue: String { description } + public var rawValue: String { description } - init(machine: SurfaceMachineID, kind: SurfaceResourceKind, key: String) { + public init(machine: SurfaceMachineID, kind: SurfaceResourceKind, key: String) { self.machine = machine self.kind = kind self.key = key } - init?(rawValue: String) { + public init?(rawValue: String) { let parts = rawValue.split(separator: "/", maxSplits: 2, omittingEmptySubsequences: false) guard parts.count == 3, let kind = SurfaceResourceKind(wire: String(parts[1])), !parts[2].isEmpty else { return nil } self.init(machine: SurfaceMachineID(rawValue: String(parts[0])), kind: kind, key: String(parts[2])) } } -enum SurfaceLifecycle: String, Codable, Sendable { +public enum SurfaceLifecycle: String, Codable, Sendable { case launching case running case exited @@ -70,11 +70,17 @@ enum SurfaceLifecycle: String, Codable, Sendable { case unavailable } -struct SurfaceAgentBadge: Hashable, Codable, Sendable { - var state: String - var source: String? +public struct SurfaceAgentBadge: Hashable, Codable, Sendable { + public var state: String + public var source: String? /// The adapter identity, separate from report provenance (`hook`, `socket`, or `plugin`). - var agent: String? = nil + public var agent: String? = nil + + public init(state: String, source: String? = nil, agent: String? = nil) { + self.state = state + self.source = source + self.agent = agent + } } /// The daemon's monotonic position for one complete remote session state. @@ -82,16 +88,16 @@ struct SurfaceAgentBadge: Hashable, Codable, Sendable { /// `revision` is encoded as a string because that is the cmux-tui wire form. The /// decoder accepts both strings and JSON numbers so a client can read snapshots /// from older daemon builds. A revision is meaningful only inside its generation. -struct CloudVMCursor: Hashable, Codable, Sendable { - var generation: String - var revision: UInt64 +public struct CloudVMCursor: Hashable, Codable, Sendable { + public var generation: String + public var revision: UInt64 - init(generation: String, revision: UInt64) { + public init(generation: String, revision: UInt64) { self.generation = generation self.revision = revision } - init?(snapshot: [String: Any]) { + public init?(snapshot: [String: Any]) { guard let cursor = snapshot["cursor"] as? [String: Any], let generation = (cursor["generation"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines), !generation.isEmpty, @@ -100,7 +106,7 @@ struct CloudVMCursor: Hashable, Codable, Sendable { self.init(generation: generation, revision: revision) } - init?(wire: [String: Any]) { + public init?(wire: [String: Any]) { guard let generation = (wire["generation"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines), !generation.isEmpty, let revision = Self.revision(wire["revision"]) @@ -115,7 +121,7 @@ struct CloudVMCursor: Hashable, Codable, Sendable { /// Returns true only when both cursors belong to the same daemon /// generation. Generations are opaque identifiers, so callers must never /// impose an ordering across them. - func isNewer(than other: CloudVMCursor?) -> Bool { + public func isNewer(than other: CloudVMCursor?) -> Bool { guard let other else { return true } return generation == other.generation && revision > other.revision } @@ -125,7 +131,7 @@ struct CloudVMCursor: Hashable, Codable, Sendable { case revision } - init(from decoder: any Decoder) throws { + public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let generation = try container.decode(String.self, forKey: .generation) if let revision = try? container.decode(UInt64.self, forKey: .revision) { @@ -139,7 +145,7 @@ struct CloudVMCursor: Hashable, Codable, Sendable { } } - func encode(to encoder: any Encoder) throws { + public func encode(to encoder: any Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(generation, forKey: .generation) try container.encode(String(revision), forKey: .revision) @@ -151,8 +157,8 @@ struct CloudVMCursor: Hashable, Codable, Sendable { /// paths. Coercing that value with intValue would turn `true`, fractions, and /// overflowing values into a different cursor or index, which can make a delta /// look contiguous when it is not. -enum CloudWireNumber { - static func unsigned(_ raw: Any?) -> UInt64? { +public enum CloudWireNumber { + public static func unsigned(_ raw: Any?) -> UInt64? { // A JSON number decodes as NSNumber, and `NSNumber(0) is Bool` is true, // so a plain `is Bool` test would reject every zero-based sequence on // the wire. Only a CFBoolean is a boolean; every other NSNumber is a @@ -171,7 +177,7 @@ enum CloudWireNumber { return nil } - static func signed(_ raw: Any?) -> Int? { + public static func signed(_ raw: Any?) -> Int? { // Same rule as `unsigned`: only a CFBoolean is a boolean. if let number = raw as? NSNumber { guard CFGetTypeID(number) != CFBooleanGetTypeID() else { return nil } @@ -187,55 +193,100 @@ enum CloudWireNumber { /// A daemon entity not yet modeled by the desktop. Its exact JSON is retained so /// an agent or a future renderer can inspect it without waiting for a schema bump. -struct CloudVMEntity: Hashable, Codable, Sendable { - var kind: String - var id: String? - var payload: Data +public struct CloudVMEntity: Hashable, Codable, Sendable { + public var kind: String + public var id: String? + public var payload: Data + + public init(kind: String, id: String? = nil, payload: Data) { + self.kind = kind + self.id = id + self.payload = payload + } } /// A typed index of the cmux-tui session graph. The raw snapshot remains the /// authority; these values are immutable indexes used by the tree and agents. -struct CloudVMWorkspaceState: Hashable, Codable, Sendable { - var id: String - var name: String - var index: Int - var focused: Bool +public struct CloudVMWorkspaceState: Hashable, Codable, Sendable { + public var id: String + public var name: String + public var index: Int + public var focused: Bool + + public init(id: String, name: String, index: Int, focused: Bool) { + self.id = id + self.name = name + self.index = index + self.focused = focused + } } -struct CloudVMScreenState: Hashable, Codable, Sendable { - var id: String - var workspaceID: String - var name: String? - var index: Int - var focused: Bool +public struct CloudVMScreenState: Hashable, Codable, Sendable { + public var id: String + public var workspaceID: String + public var name: String? + public var index: Int + public var focused: Bool /// The daemon layout document, kept opaque because its schema can evolve. - var layout: Data? + public var layout: Data? + + public init( + id: String, + workspaceID: String, + name: String? = nil, + index: Int, + focused: Bool, + layout: Data? = nil + ) { + self.id = id + self.workspaceID = workspaceID + self.name = name + self.index = index + self.focused = focused + self.layout = layout + } } -struct CloudVMPaneState: Hashable, Codable, Sendable { - var id: String - var screenID: String - var name: String? - var focused: Bool - var zoomed: Bool - var tabIDs: [String] +public struct CloudVMPaneState: Hashable, Codable, Sendable { + public var id: String + public var screenID: String + public var name: String? + public var focused: Bool + public var zoomed: Bool + public var tabIDs: [String] + + public init( + id: String, + screenID: String, + name: String? = nil, + focused: Bool, + zoomed: Bool, + tabIDs: [String] + ) { + self.id = id + self.screenID = screenID + self.name = name + self.focused = focused + self.zoomed = zoomed + self.tabIDs = tabIDs + } } /// The two valid remote tab-label states. The daemon uses an empty string to /// clear its optional label, so keep that state explicit at the app boundary /// instead of making every caller rediscover the trim-and-clear rule. -enum CloudRemoteRenameName: Hashable, Sendable { +public enum CloudRemoteRenameName: Hashable, Sendable { case named(String) case cleared - init(rawValue: String) { + public init(rawValue: String) { let normalized = rawValue.trimmingCharacters(in: .whitespacesAndNewlines) self = normalized.isEmpty ? .cleared : .named(normalized) } /// The exact value passed to `tab … rename --name`. - var wireValue: String { + public var wireValue: String { switch self { case .named(let value): return value case .cleared: return "" @@ -244,31 +295,67 @@ enum CloudRemoteRenameName: Hashable, Sendable { } -struct CloudVMTerminalState: Hashable, Codable, Sendable { - var id: String - var tabIDs: [String] - var title: String - var cwd: String? - var lifecycle: String - var cols: Int? - var rows: Int? - var running: Bool? +public struct CloudVMTerminalState: Hashable, Codable, Sendable { + public var id: String + public var tabIDs: [String] + public var title: String + public var cwd: String? + public var lifecycle: String + public var cols: Int? + public var rows: Int? + public var running: Bool? + + public init( + id: String, + tabIDs: [String], + title: String, + cwd: String? = nil, + lifecycle: String, + cols: Int? = nil, + rows: Int? = nil, + running: Bool? = nil + ) { + self.id = id + self.tabIDs = tabIDs + self.title = title + self.cwd = cwd + self.lifecycle = lifecycle + self.cols = cols + self.rows = rows + self.running = running + } } -struct CloudVMBrowserState: Hashable, Codable, Sendable { - var id: String - var tabID: String - var url: String - var title: String - var status: String +public struct CloudVMBrowserState: Hashable, Codable, Sendable { + public var id: String + public var tabID: String + public var url: String + public var title: String + public var status: String + + public init(id: String, tabID: String, url: String, title: String, status: String) { + self.id = id + self.tabID = tabID + self.url = url + self.title = title + self.status = status + } } -struct CloudVMAgentState: Hashable, Codable, Sendable { - var id: String? - var terminalID: String - var state: String - var source: String? - var agent: String? = nil +public struct CloudVMAgentState: Hashable, Codable, Sendable { + public var id: String? + public var terminalID: String + public var state: String + public var source: String? + public var agent: String? = nil + + public init(id: String? = nil, terminalID: String, state: String, source: String? = nil, agent: String? = nil) { + self.id = id + self.terminalID = terminalID + self.state = state + self.source = source + self.agent = agent + } } /// How a remote session can be synchronized. @@ -278,16 +365,21 @@ struct CloudVMAgentState: Hashable, Codable, Sendable { /// daemons publish the same graph without a cursor. Their snapshot is still useful /// for display and inspection, but it cannot prove ordering or authorize a /// revision-fenced mutation. -enum CloudVMStateSyncMode: String, Codable, Sendable { +public enum CloudVMStateSyncMode: String, Codable, Sendable { case journaled case snapshotOnly = "snapshot_only" } /// The join key for a tab's content. A terminal and a browser may use the same /// daemon-local id, so the content kind is part of the identity. -struct CloudVMTabContentKey: Hashable, Sendable { - var kind: String - var id: String +public struct CloudVMTabContentKey: Hashable, Sendable { + public var kind: String + public var id: String + + public init(kind: String, id: String) { + self.kind = kind + self.id = id + } } /// Materialized joins for one accepted daemon graph. The arrays on @@ -295,21 +387,21 @@ struct CloudVMTabContentKey: Hashable, Sendable { /// a derived lookup layer, rebuilt once at a snapshot boundary and changed only /// for entities named by a delta. It is deliberately not encoded: a cache must /// never become a second persisted source of truth. -struct CloudVMStateIndex: Sendable { - var workspacesByID: [String: CloudVMWorkspaceState] = [:] - var screensByID: [String: CloudVMScreenState] = [:] - var panesByID: [String: CloudVMPaneState] = [:] - var tabsByID: [String: CloudVMTabState] = [:] - var terminalsByID: [String: CloudVMTerminalState] = [:] - var browsersByID: [String: CloudVMBrowserState] = [:] - var agentsByID: [String: CloudVMAgentState] = [:] - var agentsByTerminalID: [String: CloudVMAgentState] = [:] - var screenIDsByWorkspaceID: [String: [String]] = [:] - var paneIDsByScreenID: [String: [String]] = [:] - var tabIDsByPaneID: [String: [String]] = [:] - var tabIDsByContent: [CloudVMTabContentKey: [String]] = [:] - - init( +public struct CloudVMStateIndex: Sendable { + public var workspacesByID: [String: CloudVMWorkspaceState] = [:] + public var screensByID: [String: CloudVMScreenState] = [:] + public var panesByID: [String: CloudVMPaneState] = [:] + public var tabsByID: [String: CloudVMTabState] = [:] + public var terminalsByID: [String: CloudVMTerminalState] = [:] + public var browsersByID: [String: CloudVMBrowserState] = [:] + public var agentsByID: [String: CloudVMAgentState] = [:] + public var agentsByTerminalID: [String: CloudVMAgentState] = [:] + public var screenIDsByWorkspaceID: [String: [String]] = [:] + public var paneIDsByScreenID: [String: [String]] = [:] + public var tabIDsByPaneID: [String: [String]] = [:] + public var tabIDsByContent: [CloudVMTabContentKey: [String]] = [:] + + public init( workspaces: [CloudVMWorkspaceState], screens: [CloudVMScreenState], panes: [CloudVMPaneState], @@ -343,54 +435,54 @@ struct CloudVMStateIndex: Sendable { } } - func workspace(id: String) -> CloudVMWorkspaceState? { + public func workspace(id: String) -> CloudVMWorkspaceState? { workspacesByID[id] } - func screen(id: String) -> CloudVMScreenState? { + public func screen(id: String) -> CloudVMScreenState? { screensByID[id] } - func pane(id: String) -> CloudVMPaneState? { + public func pane(id: String) -> CloudVMPaneState? { panesByID[id] } - func tab(id: String) -> CloudVMTabState? { + public func tab(id: String) -> CloudVMTabState? { tabsByID[id] } - func terminal(id: String) -> CloudVMTerminalState? { + public func terminal(id: String) -> CloudVMTerminalState? { terminalsByID[id] } - func browser(id: String) -> CloudVMBrowserState? { + public func browser(id: String) -> CloudVMBrowserState? { browsersByID[id] } - func agent(id: String) -> CloudVMAgentState? { + public func agent(id: String) -> CloudVMAgentState? { agentsByID[id] } - func agent(terminalID: String) -> CloudVMAgentState? { + public func agent(terminalID: String) -> CloudVMAgentState? { agentsByTerminalID[terminalID] } - func screenIDs(workspaceID: String) -> [String] { + public func screenIDs(workspaceID: String) -> [String] { orderedIDs( screenIDsByWorkspaceID[workspaceID] ?? [], index: { screensByID[$0]?.index } ) } - func paneIDs(screenID: String) -> [String] { + public func paneIDs(screenID: String) -> [String] { paneIDsByScreenID[screenID] ?? [] } - func tabIDs(paneID: String) -> [String] { + public func tabIDs(paneID: String) -> [String] { orderedTabIDs(tabIDsByPaneID[paneID] ?? []) } - func tabs(contentKind: String, contentID: String) -> [CloudVMTabState] { + public func tabs(contentKind: String, contentID: String) -> [CloudVMTabState] { let key = CloudVMTabContentKey(kind: contentKind, id: contentID) return orderedTabIDs(tabIDsByContent[key] ?? []).compactMap { tabsByID[$0] } } @@ -420,15 +512,15 @@ struct CloudVMStateIndex: Sendable { }.map { $0.element } } - mutating func upsertWorkspace(_ workspace: CloudVMWorkspaceState) { + public mutating func upsertWorkspace(_ workspace: CloudVMWorkspaceState) { workspacesByID[workspace.id] = workspace } - mutating func removeWorkspace(id: String) { + public mutating func removeWorkspace(id: String) { workspacesByID.removeValue(forKey: id) } - mutating func upsertScreen(_ screen: CloudVMScreenState) { + public mutating func upsertScreen(_ screen: CloudVMScreenState) { if let old = screensByID[screen.id], old.workspaceID != screen.workspaceID { remove(screen.id, from: &screenIDsByWorkspaceID, keyedBy: old.workspaceID) } @@ -436,12 +528,12 @@ struct CloudVMStateIndex: Sendable { appendUnique(screen.id, to: &screenIDsByWorkspaceID, keyedBy: screen.workspaceID) } - mutating func removeScreen(id: String) { + public mutating func removeScreen(id: String) { guard let old = screensByID.removeValue(forKey: id) else { return } remove(id, from: &screenIDsByWorkspaceID, keyedBy: old.workspaceID) } - mutating func upsertPane(_ pane: CloudVMPaneState) { + public mutating func upsertPane(_ pane: CloudVMPaneState) { if let old = panesByID[pane.id], old.screenID != pane.screenID { remove(pane.id, from: &paneIDsByScreenID, keyedBy: old.screenID) } @@ -449,18 +541,18 @@ struct CloudVMStateIndex: Sendable { appendUnique(pane.id, to: &paneIDsByScreenID, keyedBy: pane.screenID) } - mutating func setPaneTabIDs(_ tabIDs: [String], paneID: String) { + public mutating func setPaneTabIDs(_ tabIDs: [String], paneID: String) { guard var pane = panesByID[paneID] else { return } pane.tabIDs = tabIDs panesByID[paneID] = pane } - mutating func removePane(id: String) { + public mutating func removePane(id: String) { guard let old = panesByID.removeValue(forKey: id) else { return } remove(id, from: &paneIDsByScreenID, keyedBy: old.screenID) } - mutating func upsertTab(_ tab: CloudVMTabState) { + public mutating func upsertTab(_ tab: CloudVMTabState) { if let old = tabsByID[tab.id] { removeTabReferences(old) } @@ -468,28 +560,28 @@ struct CloudVMStateIndex: Sendable { insertTabReferences(tab) } - mutating func removeTab(id: String) { + public mutating func removeTab(id: String) { guard let old = tabsByID.removeValue(forKey: id) else { return } removeTabReferences(old) } - mutating func upsertTerminal(_ terminal: CloudVMTerminalState) { + public mutating func upsertTerminal(_ terminal: CloudVMTerminalState) { terminalsByID[terminal.id] = terminal } - mutating func removeTerminal(id: String) { + public mutating func removeTerminal(id: String) { terminalsByID.removeValue(forKey: id) } - mutating func upsertBrowser(_ browser: CloudVMBrowserState) { + public mutating func upsertBrowser(_ browser: CloudVMBrowserState) { browsersByID[browser.id] = browser } - mutating func removeBrowser(id: String) { + public mutating func removeBrowser(id: String) { browsersByID.removeValue(forKey: id) } - mutating func upsertAgent(_ agent: CloudVMAgentState) { + public mutating func upsertAgent(_ agent: CloudVMAgentState) { if let old = agentsByTerminalID[agent.terminalID] { removeAgent(old) } @@ -499,7 +591,7 @@ struct CloudVMStateIndex: Sendable { insertAgent(agent) } - mutating func removeAgent(_ agent: CloudVMAgentState) { + public mutating func removeAgent(_ agent: CloudVMAgentState) { if agentsByTerminalID[agent.terminalID]?.id == agent.id { agentsByTerminalID.removeValue(forKey: agent.terminalID) } @@ -596,7 +688,7 @@ struct CloudVMStateIndex: Sendable { /// scan and decode the whole collection. An unsupported compatibility field /// uses a bounded fallback scan so this internal API remains correct for future /// callers without pretending that arbitrary payload fields are identities. -struct CloudVMRawCollection: Hashable, Codable, Sendable { +public struct CloudVMRawCollection: Hashable, Codable, Sendable { private struct IdentityKey: Hashable, Sendable { let field: String let value: String @@ -608,9 +700,9 @@ struct CloudVMRawCollection: Hashable, Codable, Sendable { private(set) var rows: [String: Data] = [:] private var identityIndex: [IdentityKey: [String]] = [:] - init() {} + public init() {} - init(order: [String], rows: [String: Data]) { + public init(order: [String], rows: [String: Data]) { self.order = order self.rows = rows rebuildIdentityIndex() @@ -619,7 +711,7 @@ struct CloudVMRawCollection: Hashable, Codable, Sendable { /// Returns row keys for a stable identity. The direct storage key is also /// considered because a legacy row can acquire an explicit payload id while /// retaining its positional key. - func matchingRowIDs( + public func matchingRowIDs( id: String, alternateField: (name: String, value: String)? = nil ) -> [String] { @@ -644,24 +736,24 @@ struct CloudVMRawCollection: Hashable, Codable, Sendable { return matches } - func object(forRowID rowID: String) -> [String: Any]? { + public func object(forRowID rowID: String) -> [String: Any]? { guard let data = rows[rowID] else { return nil } return try? JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed]) as? [String: Any] } - mutating func insertRow(rowID: String, data: Data, object: [String: Any]) { + public mutating func insertRow(rowID: String, data: Data, object: [String: Any]) { order.append(rowID) rows[rowID] = data addIdentityEntries(for: rowID, object: object) } - mutating func replaceRow(rowID: String, data: Data, object: [String: Any]) { + public mutating func replaceRow(rowID: String, data: Data, object: [String: Any]) { removeIdentityEntries(for: rowID) rows[rowID] = data addIdentityEntries(for: rowID, object: object) } - mutating func removeRow(rowID: String, object: [String: Any]) { + public mutating func removeRow(rowID: String, object: [String: Any]) { removeIdentityEntries(for: rowID, object: object) rows.removeValue(forKey: rowID) order.removeAll { $0 == rowID } @@ -740,7 +832,7 @@ struct CloudVMRawCollection: Hashable, Codable, Sendable { case order, rows } - init(from decoder: any Decoder) throws { + public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) order = try container.decodeIfPresent([String].self, forKey: .order) ?? [] rows = try container.decodeIfPresent([String: Data].self, forKey: .rows) ?? [:] @@ -748,17 +840,17 @@ struct CloudVMRawCollection: Hashable, Codable, Sendable { rebuildIdentityIndex() } - func encode(to encoder: any Encoder) throws { + public func encode(to encoder: any Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(order, forKey: .order) try container.encode(rows, forKey: .rows) } - static func == (lhs: CloudVMRawCollection, rhs: CloudVMRawCollection) -> Bool { + public static func == (lhs: CloudVMRawCollection, rhs: CloudVMRawCollection) -> Bool { lhs.order == rhs.order && lhs.rows == rhs.rows } - func hash(into hasher: inout Hasher) { + public func hash(into hasher: inout Hasher) { hasher.combine(order) hasher.combine(rows) } @@ -768,12 +860,12 @@ struct CloudVMRawCollection: Hashable, Codable, Sendable { /// unknown top-level values share this document. Updating one row never parses /// or re-encodes unrelated rows; `object()` and `data()` are deliberate export /// boundaries that materialize the complete JSON document. -struct CloudVMStateDocument: Hashable, Codable, Sendable { +public struct CloudVMStateDocument: Hashable, Codable, Sendable { private(set) var values: [String: Data] = [:] private(set) var collections: [String: CloudVMRawCollection] = [:] private var canonicalDataCache: Data? - init(snapshot: [String: Any]) { + public init(snapshot: [String: Any]) { for (key, value) in snapshot { if let rows = value as? [[String: Any]] { var collection = CloudVMRawCollection() @@ -801,7 +893,7 @@ struct CloudVMStateDocument: Hashable, Codable, Sendable { canonicalDataCache = Self.canonicalData(snapshot) } - init?(data: Data) { + public init?(data: Data) { guard let object = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return nil } self.init(snapshot: object) // The input may be valid JSON with a different key order or whitespace. @@ -812,13 +904,13 @@ struct CloudVMStateDocument: Hashable, Codable, Sendable { } /// Materializes the complete document for export or recovery only. - func data() -> Data? { + public func data() -> Data? { if let canonicalDataCache { return canonicalDataCache } return Self.canonicalData(object()) } /// Materializes Foundation values for legacy parser and agent export paths. - func object() -> [String: Any]? { + public func object() -> [String: Any]? { var result: [String: Any] = [:] for (key, data) in values { guard let value = try? JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed]) else { return nil } @@ -838,7 +930,7 @@ struct CloudVMStateDocument: Hashable, Codable, Sendable { } /// Decodes one top-level value without materializing unrelated collections. - func value(forKey key: String) -> Any? { + public func value(forKey key: String) -> Any? { guard let data = values[key] else { return nil } return try? JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed]) } @@ -846,7 +938,7 @@ struct CloudVMStateDocument: Hashable, Codable, Sendable { /// Decodes one collection's rows without materializing the complete graph. /// The result follows the stable snapshot or storage order recorded in /// `CloudVMRawCollection.order`. Semantic placement comes from row fields. - func objects(forCollectionKey key: String) -> [[String: Any]]? { + public func objects(forCollectionKey key: String) -> [[String: Any]]? { guard let collection = collections[key] else { return nil } var result: [[String: Any]] = [] result.reserveCapacity(collection.order.count) @@ -862,14 +954,14 @@ struct CloudVMStateDocument: Hashable, Codable, Sendable { /// A delta may update only collections that were present in the accepted /// snapshot. Creating a new collection from a delta would turn an omitted /// collection into an apparently authoritative empty or partial graph. - func containsCollection(_ key: String) -> Bool { + public func containsCollection(_ key: String) -> Bool { collections[key] != nil } /// Whether this key is present as either a scalar fragment or a collection. /// Callers use this to distinguish a missing future resource from a present /// resource whose requested identity is ambiguous or absent. - func containsFragment(_ key: String) -> Bool { + public func containsFragment(_ key: String) -> Bool { values[key] != nil || collections[key] != nil } @@ -877,7 +969,7 @@ struct CloudVMStateDocument: Hashable, Codable, Sendable { /// identity returns nil instead of selecting the first row, so an agent can /// request a fresh authoritative snapshot rather than mutate an arbitrary /// remote object. - func entity(forCollectionKey key: String, id: String) -> CloudVMEntity? { + public func entity(forCollectionKey key: String, id: String) -> CloudVMEntity? { let normalizedID = Self.nonEmptyString(id) if let collection = collections[key], let normalizedID { let candidates = collection.matchingRowIDs(id: normalizedID) @@ -896,7 +988,7 @@ struct CloudVMStateDocument: Hashable, Codable, Sendable { /// an export projection, not another mutable copy of the remote graph. /// Scalar values are retained too, so a future daemon field is never lost /// only because this client does not know its shape yet. - func opaqueEntities(excluding excludedKeys: Set) -> [CloudVMEntity] { + public func opaqueEntities(excluding excludedKeys: Set) -> [CloudVMEntity] { var result: [CloudVMEntity] = [] for (key, data) in values where !excludedKeys.contains(key) { result.append(CloudVMEntity(kind: key, id: Self.entityID(from: data), payload: data)) @@ -917,7 +1009,7 @@ struct CloudVMStateDocument: Hashable, Codable, Sendable { } @discardableResult - mutating func setCursor(_ cursor: CloudVMCursor) -> Bool { + public mutating func setCursor(_ cursor: CloudVMCursor) -> Bool { // Keep cursor extensions emitted by a newer daemon. The generation and // revision are the fields this client owns; every other field remains // lossless across a local delta. @@ -946,7 +1038,7 @@ struct CloudVMStateDocument: Hashable, Codable, Sendable { /// Replaces one collection row. `alternateField` supports legacy rows whose /// stable identity is a relationship (currently agents use terminal_id). /// An existing explicit id is preserved when a legacy update omits it. - mutating func upsert( + public mutating func upsert( collectionKey: String, id: String, value: [String: Any], @@ -997,7 +1089,7 @@ struct CloudVMStateDocument: Hashable, Codable, Sendable { return true } - mutating func delete( + public mutating func delete( collectionKey: String, id: String, alternateField: (name: String, value: String)? = nil @@ -1029,7 +1121,7 @@ struct CloudVMStateDocument: Hashable, Codable, Sendable { return true } - mutating func replaceSingleton(key: String, value: [String: Any]) -> Bool { + public mutating func replaceSingleton(key: String, value: [String: Any]) -> Bool { guard let data = Self.canonicalData(value) else { return false } values[key] = data collections.removeValue(forKey: key) @@ -1037,7 +1129,7 @@ struct CloudVMStateDocument: Hashable, Codable, Sendable { return true } - mutating func removeSingleton(key: String, id: String) -> Bool { + public mutating func removeSingleton(key: String, id: String) -> Bool { guard let data = values[key], let object = try? JSONSerialization.jsonObject(with: data) as? [String: Any], Self.nonEmptyString(object["id"]) == id else { return false } @@ -1075,7 +1167,7 @@ struct CloudVMStateDocument: Hashable, Codable, Sendable { case values, collections } - init(from decoder: any Decoder) throws { + public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) values = try container.decodeIfPresent([String: Data].self, forKey: .values) ?? [:] collections = try container.decodeIfPresent([String: CloudVMRawCollection].self, forKey: .collections) ?? [:] @@ -1105,17 +1197,17 @@ struct CloudVMStateDocument: Hashable, Codable, Sendable { (try? JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed])) != nil } - func encode(to encoder: any Encoder) throws { + public func encode(to encoder: any Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(values, forKey: .values) try container.encode(collections, forKey: .collections) } - static func == (lhs: CloudVMStateDocument, rhs: CloudVMStateDocument) -> Bool { + public static func == (lhs: CloudVMStateDocument, rhs: CloudVMStateDocument) -> Bool { lhs.values == rhs.values && lhs.collections == rhs.collections } - func hash(into hasher: inout Hasher) { + public func hash(into hasher: inout Hasher) { hasher.combine(values) hasher.combine(collections) } @@ -1128,36 +1220,36 @@ struct CloudVMStateDocument: Hashable, Codable, Sendable { /// independent write sources. This gives the agent a stable graph today and an /// accretive escape hatch for new daemon resources tomorrow. A nil cursor is an /// explicit legacy snapshot-only mode, not an invalid or partially parsed graph. -struct CloudVMState: Hashable, Codable, Sendable { +public struct CloudVMState: Hashable, Codable, Sendable { private static let modeledSnapshotKeys: Set = [ "machine", "session", "cursor", "workspaces", "screens", "panes", "tabs", "terminals", "browsers", "agents", ] - var machine: SurfaceMachineID - var cursor: CloudVMCursor? + public var machine: SurfaceMachineID + public var cursor: CloudVMCursor? /// The canonical document owns both known and unknown remote fields. The /// byte form is materialized only when a caller crosses an export boundary. - var document: CloudVMStateDocument - var workspaces: [CloudVMWorkspaceState] - var screens: [CloudVMScreenState] - var panes: [CloudVMPaneState] - var tabs: [CloudVMTabState] - var terminals: [CloudVMTerminalState] - var browsers: [CloudVMBrowserState] - var agents: [CloudVMAgentState] + public var document: CloudVMStateDocument + public var workspaces: [CloudVMWorkspaceState] + public var screens: [CloudVMScreenState] + public var panes: [CloudVMPaneState] + public var tabs: [CloudVMTabState] + public var terminals: [CloudVMTerminalState] + public var browsers: [CloudVMBrowserState] + public var agents: [CloudVMAgentState] /// Derived joins are rebuilt at snapshot boundaries and updated transactionally /// with accepted deltas. They are excluded from Codable below. - var lookupIndex: CloudVMStateIndex + public var lookupIndex: CloudVMStateIndex /// Future daemon resources are always projected from `document` on read. /// Keeping this computed prevents an opaque delta from creating a second, /// stale copy of state beside the canonical fragments. - var otherEntities: [CloudVMEntity] { + public var otherEntities: [CloudVMEntity] { document.opaqueEntities(excluding: Self.modeledSnapshotKeys) } - init( + public init( machine: SurfaceMachineID, cursor: CloudVMCursor?, rawSnapshot: Data, @@ -1197,7 +1289,7 @@ struct CloudVMState: Hashable, Codable, Sendable { /// Compatibility accessor for callers that need the canonical bytes. This /// can be expensive after deltas, so hot paths must use the typed index. - var rawSnapshot: Data { + public var rawSnapshot: Data { document.data() ?? Data() } @@ -1205,7 +1297,7 @@ struct CloudVMState: Hashable, Codable, Sendable { case machine, cursor, document, rawSnapshot } - init(from decoder: any Decoder) throws { + public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let machine = try container.decode(SurfaceMachineID.self, forKey: .machine) let encodedCursor = try container.decodeIfPresent(CloudVMCursor.self, forKey: .cursor) @@ -1238,7 +1330,7 @@ struct CloudVMState: Hashable, Codable, Sendable { self = parsed } - func encode(to encoder: any Encoder) throws { + public func encode(to encoder: any Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(machine, forKey: .machine) try container.encodeIfPresent(cursor, forKey: .cursor) @@ -1248,7 +1340,7 @@ struct CloudVMState: Hashable, Codable, Sendable { // New archives contain one canonical document. The decoder keeps a // one-way rawSnapshot fallback for archives written before this model. - func hash(into hasher: inout Hasher) { + public func hash(into hasher: inout Hasher) { hasher.combine(machine) hasher.combine(cursor) hasher.combine(document) @@ -1261,13 +1353,13 @@ struct CloudVMState: Hashable, Codable, Sendable { hasher.combine(agents) } - var syncMode: CloudVMStateSyncMode { + public var syncMode: CloudVMStateSyncMode { cursor == nil ? .snapshotOnly : .journaled } - var workspaceIDs: Set { Set(workspaces.map(\.id)) } + public var workspaceIDs: Set { Set(workspaces.map(\.id)) } - func entity(kind: String, id: String) -> CloudVMEntity? { + public func entity(kind: String, id: String) -> CloudVMEntity? { let key = Self.snapshotKey(for: kind) guard key != "cursor" else { return nil } if let entity = document.entity(forCollectionKey: key, id: id) { @@ -1283,7 +1375,7 @@ struct CloudVMState: Hashable, Codable, Sendable { /// Unified read access for agents and future features. Known typed kinds /// and opaque kinds use the same plural snapshot-key vocabulary; singular /// daemon resource names are accepted as aliases. - func entities(kind: String) -> [CloudVMEntity] { + public func entities(kind: String) -> [CloudVMEntity] { let key = Self.snapshotKey(for: kind) guard key != "cursor" else { return [] } let objects: [[String: Any]] @@ -1302,7 +1394,7 @@ struct CloudVMState: Hashable, Codable, Sendable { } } - func snapshotObject() -> [String: Any]? { + public func snapshotObject() -> [String: Any]? { document.object() } @@ -1330,12 +1422,12 @@ struct CloudVMState: Hashable, Codable, Sendable { /// fields redacted. Synchronization uses the unredacted document; this /// boundary only protects the local control socket from leaking pairing or /// renderer secrets. - func agentSnapshotObject() -> [String: Any]? { + public func agentSnapshotObject() -> [String: Any]? { guard let snapshot = snapshotObject() else { return nil } return Self.redact(snapshot, context: []) as? [String: Any] } - func agentEntityObject(_ entity: CloudVMEntity) -> Any { + public func agentEntityObject(_ entity: CloudVMEntity) -> Any { guard let object = try? JSONSerialization.jsonObject(with: entity.payload, options: [.fragmentsAllowed]) else { return NSNull() } @@ -1383,25 +1475,35 @@ struct CloudVMState: Hashable, Codable, Sendable { /// Freshness of an accepted document is separate from the document itself. /// A sleeping or disconnected VM can still have useful last-known state, but /// that state must never be mistaken for a permission to mutate the VM. -enum CloudVMStateFreshness: String, Codable, Sendable { +public enum CloudVMStateFreshness: String, Codable, Sendable { case current case stale } -struct CloudVMStateObservation: Hashable, Codable, Sendable { - var freshness: CloudVMStateFreshness - var reason: String? +public struct CloudVMStateObservation: Hashable, Codable, Sendable { + public var freshness: CloudVMStateFreshness + public var reason: String? /// Transient local writes whose commit cursor is ahead of the accepted /// graph. They are exported so an agent can distinguish a provisional row /// from authoritative daemon state. This field is never persisted in the /// daemon document and disappears when the receipt is observed. - var pendingWrites: [CloudVMPendingMutation]? = nil + public var pendingWrites: [CloudVMPendingMutation]? = nil - static let current = CloudVMStateObservation(freshness: .current, reason: nil, pendingWrites: nil) + public static let current = CloudVMStateObservation(freshness: .current, reason: nil, pendingWrites: nil) - static func stale(reason: String? = nil) -> Self { + public static func stale(reason: String? = nil) -> Self { Self(freshness: .stale, reason: reason, pendingWrites: nil) } + + public init( + freshness: CloudVMStateFreshness, + reason: String? = nil, + pendingWrites: [CloudVMPendingMutation]? = nil + ) { + self.freshness = freshness + self.reason = reason + self.pendingWrites = pendingWrites + } } /// Decides whether a complete graph is allowed to cross a pending remote @@ -1409,12 +1511,12 @@ struct CloudVMStateObservation: Hashable, Codable, Sendable { /// same-generation graph before the receipt is stale. At the exact receipt /// cursor, the named value must match. A later cursor is authoritative even /// when another writer changed the value after our mutation. -enum CloudVMRemoteMutationReceiptDecision: Equatable, Sendable { +public enum CloudVMRemoteMutationReceiptDecision: Equatable, Sendable { case accept case rejectStale case rejectConflict - static func resolve( + public static func resolve( receipt: CloudVMCursor, incoming: CloudVMCursor?, targetMatches: Bool @@ -1437,11 +1539,11 @@ enum CloudVMRemoteMutationReceiptDecision: Equatable, Sendable { /// Decides whether a cursor belongs to a generation that this provider may /// still install. A generation is opaque, so the only safe old-link signal is /// that the same identifier was already accepted before another generation. -enum CloudVMGenerationAcceptanceDecision: Equatable, Sendable { +public enum CloudVMGenerationAcceptanceDecision: Equatable, Sendable { case accept case rejectStale - static func resolve( + public static func resolve( incoming: String, current: String?, accepted: Set @@ -1455,14 +1557,14 @@ enum CloudVMGenerationAcceptanceDecision: Equatable, Sendable { /// A pending creation receipt is valid only when that same refresh established /// the current daemon generation. The tab command carries a revision, not a /// generation, so an old receipt cannot be used after a failed refresh. -enum CloudVMRemoteMutationAuthority: Equatable, Sendable { +public enum CloudVMRemoteMutationAuthority: Equatable, Sendable { case currentGraph case pendingReceipt case snapshotOnly case unavailable case targetMissing - static func resolve( + public static func resolve( refreshEstablishedCurrentGraph: Bool, hasAcceptedState: Bool, targetVisible: Bool, @@ -1488,28 +1590,44 @@ enum CloudVMRemoteMutationAuthority: Equatable, Sendable { /// A local read-your-write receipt. The remote daemon remains authoritative; /// this value only tells agents why a derived catalog row can temporarily be /// ahead of the last accepted complete graph. -struct CloudVMPendingMutation: Hashable, Codable, Sendable { - enum Kind: String, Codable, Sendable { +public struct CloudVMPendingMutation: Hashable, Codable, Sendable { + public enum Kind: String, Codable, Sendable { case terminalCreate = "terminal_create" case workspaceRename = "workspace_rename" case tabRename = "tab_rename" } - var kind: Kind + public var kind: Kind /// A terminal creation has a resource id. Rename receipts use the stable /// daemon id fields below instead, so this is optional by design. - var resource: SurfaceResourceID? - var remoteWorkspaceID: String? - var remoteTabID: String? - var name: String? - var receipt: CloudVMCursor? + public var resource: SurfaceResourceID? + public var remoteWorkspaceID: String? + public var remoteTabID: String? + public var name: String? + public var receipt: CloudVMCursor? + + public init( + kind: Kind, + resource: SurfaceResourceID? = nil, + remoteWorkspaceID: String? = nil, + remoteTabID: String? = nil, + name: String? = nil, + receipt: CloudVMCursor? = nil + ) { + self.kind = kind + self.resource = resource + self.remoteWorkspaceID = remoteWorkspaceID + self.remoteTabID = remoteTabID + self.name = name + self.receipt = receipt + } } /// A snapshot repairs the document, but it does not by itself repair the live /// event feed. Keep an existing transport warning until the versioned feed is /// running again, so an agent never mistakes a point-in-time read for live sync. -enum CloudVMEventFeedRecoveryDecision { - static func shouldClearWarning( +public enum CloudVMEventFeedRecoveryDecision { + public static func shouldClearWarning( snapshotCursor: CloudVMCursor?, subscriptionResumed: Bool ) -> Bool { @@ -1519,7 +1637,7 @@ enum CloudVMEventFeedRecoveryDecision { /// The stream can carry a delta that the desktop does not understand, or it can /// end because its journal window overflowed. Both cases require a new snapshot. -enum CloudVMStateSyncDecision: Equatable, Sendable { +public enum CloudVMStateSyncDecision: Equatable, Sendable { case ignoreStale case installSnapshot case fetchSnapshot @@ -1530,7 +1648,7 @@ enum CloudVMStateSyncDecision: Equatable, Sendable { /// new daemon session and therefore accepts a snapshot even when its numeric /// revision is lower. A delta must join the exact cursor already installed; /// accepting a non-contiguous delta would silently lose an entity update. - static func forSnapshot( + public static func forSnapshot( incoming: CloudVMCursor?, current: CloudVMCursor? ) -> Self { @@ -1545,7 +1663,7 @@ enum CloudVMStateSyncDecision: Equatable, Sendable { return incoming.revision > current.revision ? .installSnapshot : .ignoreStale } - static func forDelta( + public static func forDelta( generation: String, previousRevision: UInt64, revision: UInt64, @@ -1567,79 +1685,124 @@ enum CloudVMStateSyncDecision: Equatable, Sendable { /// The cmux-tui workspace a remote resource belongs to (nil for local resources). /// Device workspaces (another Mac's sidebar) additionally carry the cwd, unread /// count, and pin state the Mac sidebar shows; cloud workspaces leave them nil. -struct SurfaceRemoteWorkspace: Hashable, Codable, Sendable { - var id: String - var name: String - var index: Int - var focused: Bool +public struct SurfaceRemoteWorkspace: Hashable, Codable, Sendable { + public var id: String + public var name: String + public var index: Int + public var focused: Bool /// The workspace's presented working directory, when the provider reports one. - var detail: String? = nil + public var detail: String? = nil /// The remote sidebar's unread badge count, when the provider reports one. - var unreadCount: Int? = nil + public var unreadCount: Int? = nil /// Whether the workspace is pinned on its machine, when the provider reports it. - var isPinned: Bool? = nil + public var isPinned: Bool? = nil + + public init( + id: String, + name: String, + index: Int, + focused: Bool, + detail: String? = nil, + unreadCount: Int? = nil, + isPinned: Bool? = nil + ) { + self.id = id + self.name = name + self.index = index + self.focused = focused + self.detail = detail + self.unreadCount = unreadCount + self.isPinned = isPinned + } } /// One view of a remote resource: a tab in one of the daemon's workspaces. A resource /// has zero or more views; closing a view never kills the resource. -struct SurfaceRemoteView: Hashable, Codable, Sendable { - var tabID: String - var workspace: SurfaceRemoteWorkspace +public struct SurfaceRemoteView: Hashable, Codable, Sendable { + public var tabID: String + public var workspace: SurfaceRemoteWorkspace /// Exact graph coordinates. They make a local pane's rename target stable even /// when the same terminal is present in several workspaces. - var screenID: String? = nil - var paneID: String? = nil - var name: String? = nil - var index: Int? = nil + public var screenID: String? = nil + public var paneID: String? = nil + public var name: String? = nil + public var index: Int? = nil /// True when this tab is the one its pane shows; the daemon flags exactly one /// tab per pane. The pane's other tabs sit behind it in its tab bar. - var focused: Bool? = nil + public var focused: Bool? = nil /// Where the tab's pane sits in the workspace's layout: the screen's index and /// the pane's depth-first position in that screen's split tree. nil when the /// snapshot carried no layout document (older daemons, focused snapshots). - var screenIndex: Int? = nil - var paneIndex: Int? = nil + public var screenIndex: Int? = nil + public var paneIndex: Int? = nil + + public init( + tabID: String, + workspace: SurfaceRemoteWorkspace, + screenID: String? = nil, + paneID: String? = nil, + name: String? = nil, + index: Int? = nil, + focused: Bool? = nil, + screenIndex: Int? = nil, + paneIndex: Int? = nil + ) { + self.tabID = tabID + self.workspace = workspace + self.screenID = screenID + self.paneID = paneID + self.name = name + self.index = index + self.focused = focused + self.screenIndex = screenIndex + self.paneIndex = paneIndex + } } /// Stable identity from the creation receipt, checked again before attachment. -struct CloudCreationAttachment: Hashable, Codable, Sendable { - let generation: String - let terminalID: String +public struct CloudCreationAttachment: Hashable, Codable, Sendable { + public let generation: String + public let terminalID: String + + public init(generation: String, terminalID: String) { + self.generation = generation + self.terminalID = terminalID + } } -struct SurfaceResource: Identifiable, Hashable, Codable, Sendable { - var id: SurfaceResourceID - var title: String +public struct SurfaceResource: Identifiable, Hashable, Codable, Sendable { + public var id: SurfaceResourceID + public var title: String /// cwd for terminals, URL for browsers, display name for screens. - var detail: String? - var lifecycle: SurfaceLifecycle - var creationAttachment: CloudCreationAttachment? = nil - var agent: SurfaceAgentBadge? + public var detail: String? + public var lifecycle: SurfaceLifecycle + public var creationAttachment: CloudCreationAttachment? = nil + public var agent: SurfaceAgentBadge? /// The workspace of the resource's first view (compat: pre-multi-view callers read /// one workspace). nil when the resource has zero views, or is local. - var remoteWorkspace: SurfaceRemoteWorkspace? + public var remoteWorkspace: SurfaceRemoteWorkspace? /// Every view of a remote resource, in the daemon's canonical tab order. nil when the /// provider does not model views (local resources, displays, port browsers); an empty /// array is a live resource with zero views (it belongs in the machine's pool). - var remoteViews: [SurfaceRemoteView]? = nil + public var remoteViews: [SurfaceRemoteView]? = nil /// For screens and port browsers: the port on the machine. - var port: Int? + public var port: Int? /// For browsers and screens: the private URL the projection loads after the /// browser Network Extension is ready. - var url: String? + public var url: String? - var machine: SurfaceMachineID { id.machine } - var kind: SurfaceResourceKind { id.kind } + public var machine: SurfaceMachineID { id.machine } + public var kind: SurfaceResourceKind { id.kind } /// How many remote views (daemon tabs) show this resource; 0 when views are not modeled. - var remoteViewCount: Int { remoteViews?.count ?? 0 } + public var remoteViewCount: Int { remoteViews?.count ?? 0 } /// A live cloud terminal no daemon tab shows: it keeps running on the machine /// and is out of every workspace's layout, so it lists only in the machine's /// Terminals group, greyed as "detached"; a click re-attaches it in a pane and /// only its kill verb ends it. Exited and unavailable records are never marked /// detached, even when stale tab ids leave an empty resolved-view list. - var isDetachedTerminal: Bool { + public var isDetachedTerminal: Bool { guard kind == .terminal, remoteViews?.isEmpty == true else { return false } switch lifecycle { case .launching, .running: @@ -1651,7 +1814,7 @@ struct SurfaceResource: Identifiable, Hashable, Codable, Sendable { /// The daemon workspaces holding at least one view, first-view order, deduped. /// Falls back to `remoteWorkspace` for providers that report a single workspace. - var remoteWorkspaces: [SurfaceRemoteWorkspace] { + public var remoteWorkspaces: [SurfaceRemoteWorkspace] { guard let remoteViews else { return remoteWorkspace.map { [$0] } ?? [] } var seen = Set() var result: [SurfaceRemoteWorkspace] = [] @@ -1660,33 +1823,71 @@ struct SurfaceResource: Identifiable, Hashable, Codable, Sendable { } return result } + + public init( + id: SurfaceResourceID, + title: String, + detail: String? = nil, + lifecycle: SurfaceLifecycle, + creationAttachment: CloudCreationAttachment? = nil, + agent: SurfaceAgentBadge? = nil, + remoteWorkspace: SurfaceRemoteWorkspace? = nil, + remoteViews: [SurfaceRemoteView]? = nil, + port: Int? = nil, + url: String? = nil + ) { + self.id = id + self.title = title + self.detail = detail + self.lifecycle = lifecycle + self.creationAttachment = creationAttachment + self.agent = agent + self.remoteWorkspace = remoteWorkspace + self.remoteViews = remoteViews + self.port = port + self.url = url + } } /// One pane showing one resource. -struct SurfaceProjection: Hashable, Codable, Sendable { - var resource: SurfaceResourceID - var workspaceID: UUID - var panelID: UUID +public struct SurfaceProjection: Hashable, Codable, Sendable { + public var resource: SurfaceResourceID + public var workspaceID: UUID + public var panelID: UUID /// The remote placement represented by this local pane, if it came from a /// cloud graph. A terminal id alone is not enough because tab names are /// placement-local. - var remoteWorkspaceID: String? = nil - var remoteTabID: String? = nil + public var remoteWorkspaceID: String? = nil + public var remoteTabID: String? = nil + + public init( + resource: SurfaceResourceID, + workspaceID: UUID, + panelID: UUID, + remoteWorkspaceID: String? = nil, + remoteTabID: String? = nil + ) { + self.resource = resource + self.workspaceID = workspaceID + self.panelID = panelID + self.remoteWorkspaceID = remoteWorkspaceID + self.remoteTabID = remoteTabID + } } -enum SurfaceSplitDirection: String, Codable, Sendable { +public enum SurfaceSplitDirection: String, Codable, Sendable { case left, right, up, down } /// Where to project. Mirrors the socket params `workspace_id` / `pane_id` / `direction` / /// `tab_index`; `.workspace` splits that workspace's focused pane to the right (or tabs /// when `placement` is `.tab`). -enum SurfaceDestination: Hashable, Sendable { +public enum SurfaceDestination: Hashable, Sendable { case workspace(id: UUID, placement: SurfacePlacement) case split(workspaceID: UUID, paneID: String, direction: SurfaceSplitDirection) case tab(workspaceID: UUID, paneID: String, index: Int?) - var workspaceID: UUID { + public var workspaceID: UUID { switch self { case .workspace(let id, _): return id case .split(let id, _, _): return id @@ -1695,38 +1896,72 @@ enum SurfaceDestination: Hashable, Sendable { } } -enum SurfacePlacement: String, Codable, Sendable { +public enum SurfacePlacement: String, Codable, Sendable { case split case tab } /// What a provider knows about its machine, for the tree header. -struct SurfaceMachineInfo: Hashable, Codable, Sendable { - var id: SurfaceMachineID - var name: String +public struct SurfaceMachineInfo: Hashable, Codable, Sendable { + public var id: SurfaceMachineID + public var name: String /// `running`, `standby`, … for cloud machines; `running` for the local Mac. - var status: String - var image: String? - var hasDesktop: Bool - var memoryMb: Int? - var diskMb: Int? - var linkState: SurfaceLinkState - var linkError: String? - var cpuPercent: Double? - var memoryUsedMb: Int? - var diskUsedMb: Int? + public var status: String + public var image: String? + public var hasDesktop: Bool + public var memoryMb: Int? + public var diskMb: Int? + public var linkState: SurfaceLinkState + public var linkError: String? + public var cpuPercent: Double? + public var memoryUsedMb: Int? + public var diskUsedMb: Int? /// Every cmux-tui workspace on the machine, in the daemon's order — including empty /// ones, which have no terminal to be derived from. nil when unknown (asleep, local). - var remoteWorkspaces: [SurfaceRemoteWorkspace]? = nil + public var remoteWorkspaces: [SurfaceRemoteWorkspace]? = nil /// The machine's address on its owner's private network (v4 preferred), /// reachable through the WireGuard tunnel. nil for the local Mac and for /// machines created before private networking. - var privateAddress: String? = nil + public var privateAddress: String? = nil /// Account presence for another Mac's app instance; nil for local and cloud machines. - var presence: SurfaceDevicePresence? = nil + public var presence: SurfaceDevicePresence? = nil + + public init( + id: SurfaceMachineID, + name: String, + status: String, + image: String? = nil, + hasDesktop: Bool, + memoryMb: Int? = nil, + diskMb: Int? = nil, + linkState: SurfaceLinkState, + linkError: String? = nil, + cpuPercent: Double? = nil, + memoryUsedMb: Int? = nil, + diskUsedMb: Int? = nil, + remoteWorkspaces: [SurfaceRemoteWorkspace]? = nil, + privateAddress: String? = nil, + presence: SurfaceDevicePresence? = nil + ) { + self.id = id + self.name = name + self.status = status + self.image = image + self.hasDesktop = hasDesktop + self.memoryMb = memoryMb + self.diskMb = diskMb + self.linkState = linkState + self.linkError = linkError + self.cpuPercent = cpuPercent + self.memoryUsedMb = memoryUsedMb + self.diskUsedMb = diskUsedMb + self.remoteWorkspaces = remoteWorkspaces + self.privateAddress = privateAddress + self.presence = presence + } } -enum SurfaceLinkState: String, Codable, Sendable { +public enum SurfaceLinkState: String, Codable, Sendable { case connected case connecting case asleep @@ -1743,28 +1978,52 @@ enum SurfaceLinkState: String, Codable, Sendable { /// `catalog`; the complete daemon graphs stay out of its high-frequency value. /// Both halves are captured in the same main-actor turn, so their cursors and /// derived resource rows always describe one accepted state. -struct SurfaceCatalogExport: Sendable { - var catalog: SurfaceCatalogSnapshot - var cloudStates: [CloudVMState] +public struct SurfaceCatalogExport: Sendable { + public var catalog: SurfaceCatalogSnapshot + public var cloudStates: [CloudVMState] /// Observation metadata is kept beside, not inside, the daemon document. /// This preserves cursor/raw-snapshot equality while making offline state /// explicit to agents. - var cloudStateObservations: [SurfaceMachineID: CloudVMStateObservation] = [:] + public var cloudStateObservations: [SurfaceMachineID: CloudVMStateObservation] = [:] /// Existing stable local owner IDs, captured beside this read's runtime projections. /// Missing owners remain unknown; these values never become resource or mutation IDs. - var projectionIdentities: [SurfaceProjection: SurfaceProjectionIdentity] = [:] + public var projectionIdentities: [SurfaceProjection: SurfaceProjectionIdentity] = [:] + + public init( + catalog: SurfaceCatalogSnapshot, + cloudStates: [CloudVMState], + cloudStateObservations: [SurfaceMachineID: CloudVMStateObservation] = [:], + projectionIdentities: [SurfaceProjection: SurfaceProjectionIdentity] = [:] + ) { + self.catalog = catalog + self.cloudStates = cloudStates + self.cloudStateObservations = cloudStateObservations + self.projectionIdentities = projectionIdentities + } } /// Persisted with the session: which resource each pane projected, so a restored pane /// re-projects a remote resource instead of becoming an anonymous shell. -struct SurfaceProjectionRecord: Hashable, Codable, Sendable { - var panelID: UUID - var resource: SurfaceResourceID - var remoteWorkspaceID: String? = nil - var remoteTabID: String? = nil +public struct SurfaceProjectionRecord: Hashable, Codable, Sendable { + public var panelID: UUID + public var resource: SurfaceResourceID + public var remoteWorkspaceID: String? = nil + public var remoteTabID: String? = nil + + public init( + panelID: UUID, + resource: SurfaceResourceID, + remoteWorkspaceID: String? = nil, + remoteTabID: String? = nil + ) { + self.panelID = panelID + self.resource = resource + self.remoteWorkspaceID = remoteWorkspaceID + self.remoteTabID = remoteTabID + } } -enum SurfaceCatalogError: Error, LocalizedError, Equatable { +public enum SurfaceCatalogError: Error, LocalizedError, Equatable { case unknownResource(SurfaceResourceID) case noProvider(SurfaceMachineID) case unavailable(SurfaceResourceID, reason: String) @@ -1778,7 +2037,7 @@ enum SurfaceCatalogError: Error, LocalizedError, Equatable { /// remote graph may now contain a deliberate partial result. case partialOperation(SurfaceResourceID, reason: String) - var errorDescription: String? { + public var errorDescription: String? { switch self { case .unknownResource(let id): return String(format: String(localized: "surfaceCatalog.error.unknownResource", defaultValue: "Unknown surface %@."), id.rawValue) diff --git a/Sources/Surfaces/SurfaceCatalogSnapshot.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceCatalogSnapshot.swift similarity index 51% rename from Sources/Surfaces/SurfaceCatalogSnapshot.swift rename to Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceCatalogSnapshot.swift index 835aa31e4e35..38a5616d917f 100644 --- a/Sources/Surfaces/SurfaceCatalogSnapshot.swift +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceCatalogSnapshot.swift @@ -2,33 +2,51 @@ import Foundation /// The catalog as one value: what the sidebar renders, what `surface.catalog` and /// `cmux vm tree --json` print. Machines are ordered local first, then by name. -struct SurfaceCatalogSnapshot: Hashable, Codable, Sendable { +public struct SurfaceCatalogSnapshot: Hashable, Codable, Sendable { /// Workspaces admitted for deletion but not yet confirmed by the daemon, /// per machine. Nil when nothing is pending, so socket readers on older /// builds keep decoding the same document. /// Pending native workspace identities, keyed by machine and daemon workspace. - var pendingWorkspaceCreations: [SurfaceMachineID: [String: UUID]]? = nil - var pendingWorkspaceDeletions: [SurfaceMachineID: Set]? = nil - var machines: [SurfaceMachineInfo] - var resources: [SurfaceResource] - var projections: [SurfaceProjection] - var staleMachineIDs: Set = [] - var displayCreationMachines: Set? = nil + public var pendingWorkspaceCreations: [SurfaceMachineID: [String: UUID]]? = nil + public var pendingWorkspaceDeletions: [SurfaceMachineID: Set]? = nil + public var machines: [SurfaceMachineInfo] + public var resources: [SurfaceResource] + public var projections: [SurfaceProjection] + public var staleMachineIDs: Set = [] + public var displayCreationMachines: Set? = nil - static let empty = SurfaceCatalogSnapshot(machines: [], resources: [], projections: []) + public static let empty = SurfaceCatalogSnapshot(machines: [], resources: [], projections: []) - func resources(on machine: SurfaceMachineID) -> [SurfaceResource] { + public func resources(on machine: SurfaceMachineID) -> [SurfaceResource] { resources.filter { $0.machine == machine } } - func projections(of resource: SurfaceResourceID) -> [SurfaceProjection] { + public func projections(of resource: SurfaceResourceID) -> [SurfaceProjection] { projections.filter { $0.resource == resource } } - func isOpen(_ resource: SurfaceResourceID) -> Bool { + public func isOpen(_ resource: SurfaceResourceID) -> Bool { projections.contains { $0.resource == resource } } + + public init( + pendingWorkspaceCreations: [SurfaceMachineID: [String: UUID]]? = nil, + pendingWorkspaceDeletions: [SurfaceMachineID: Set]? = nil, + machines: [SurfaceMachineInfo], + resources: [SurfaceResource], + projections: [SurfaceProjection], + staleMachineIDs: Set = [], + displayCreationMachines: Set? = nil + ) { + self.pendingWorkspaceCreations = pendingWorkspaceCreations + self.pendingWorkspaceDeletions = pendingWorkspaceDeletions + self.machines = machines + self.resources = resources + self.projections = projections + self.staleMachineIDs = staleMachineIDs + self.displayCreationMachines = displayCreationMachines + } } extension SurfaceCatalogSnapshot { @@ -36,7 +54,7 @@ extension SurfaceCatalogSnapshot { case pendingWorkspaceCreations, pendingWorkspaceDeletions, machines, resources, projections, staleMachineIDs, displayCreationMachines } - init(from decoder: Decoder) throws { + public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) pendingWorkspaceCreations = try values.decodeIfPresent([SurfaceMachineID: [String: UUID]].self, forKey: .pendingWorkspaceCreations) pendingWorkspaceDeletions = try values.decodeIfPresent([SurfaceMachineID: Set].self, forKey: .pendingWorkspaceDeletions) diff --git a/Sources/Surfaces/SurfaceDeviceInstanceID.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceDeviceInstanceID.swift similarity index 78% rename from Sources/Surfaces/SurfaceDeviceInstanceID.swift rename to Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceDeviceInstanceID.swift index 515c271e1a57..9ec6f86f774e 100644 --- a/Sources/Surfaces/SurfaceDeviceInstanceID.swift +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceDeviceInstanceID.swift @@ -14,25 +14,25 @@ import Foundation /// compatible with persisted sessions, `cloudTree.collapsedMachineIDs`, and the /// `surface.catalog` JSON older CLIs already read. The UUID contains no `@`, so the /// first `@` after the prefix splits identity from tag even for tags that contain one. -struct SurfaceDeviceInstanceID: Hashable, Codable, Sendable, CustomStringConvertible { - static let wirePrefix = "device:" - static let tagSeparator: Character = "@" +public struct SurfaceDeviceInstanceID: Hashable, Codable, Sendable, CustomStringConvertible { + public static let wirePrefix = "device:" + public static let tagSeparator: Character = "@" /// The registry tag of the stable channel; every other tag is a build channel or dev launch tag. - static let defaultTag = "default" + public static let defaultTag = "default" /// Canonical lowercase device UUID (``cmxCanonicalDeviceID``). - let deviceID: String + public let deviceID: String /// Normalized app-instance tag; empty input becomes ``defaultTag``. - let tag: String + public let tag: String - init(deviceID: String, tag: String) { + public init(deviceID: String, tag: String) { self.deviceID = cmxCanonicalDeviceID(deviceID.trimmingCharacters(in: .whitespacesAndNewlines)) let trimmedTag = tag.trimmingCharacters(in: .whitespacesAndNewlines) self.tag = trimmedTag.isEmpty ? Self.defaultTag : trimmedTag } /// Parses the wire form; nil for anything that is not a `device:` value. - init?(wireValue: String) { + public init?(wireValue: String) { guard wireValue.hasPrefix(Self.wirePrefix) else { return nil } let body = wireValue.dropFirst(Self.wirePrefix.count) guard let separator = body.firstIndex(of: Self.tagSeparator) else { return nil } @@ -42,15 +42,15 @@ struct SurfaceDeviceInstanceID: Hashable, Codable, Sendable, CustomStringConvert self.init(deviceID: deviceID, tag: tag) } - var wireValue: String { "\(Self.wirePrefix)\(deviceID)\(Self.tagSeparator)\(tag)" } - var description: String { wireValue } + public var wireValue: String { "\(Self.wirePrefix)\(deviceID)\(Self.tagSeparator)\(tag)" } + public var description: String { wireValue } /// Whether this instance is the stable-channel app on its device. - var isDefaultTag: Bool { tag == Self.defaultTag } + public var isDefaultTag: Bool { tag == Self.defaultTag } /// Other physical Macs are visible in the viewer's channel. Dev viewers /// additionally see stable and nightly, but never unrelated dev tags. - func isVisible(from viewer: SurfaceDeviceInstanceID) -> Bool { + public func isVisible(from viewer: SurfaceDeviceInstanceID) -> Bool { guard deviceID != viewer.deviceID else { return false } if tag == viewer.tag { return true } switch viewer.tag { @@ -62,7 +62,7 @@ struct SurfaceDeviceInstanceID: Hashable, Codable, Sendable, CustomStringConvert } /// The shared cross-app spelling used by pairing and presence code. - var appInstanceIdentity: CmxMacAppInstanceIdentity { + public var appInstanceIdentity: CmxMacAppInstanceIdentity { CmxMacAppInstanceIdentity(macDeviceID: deviceID, instanceTag: isDefaultTag ? nil : tag) } } diff --git a/Sources/Surfaces/SurfaceDevicePresence.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceDevicePresence.swift similarity index 76% rename from Sources/Surfaces/SurfaceDevicePresence.swift rename to Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceDevicePresence.swift index c1f26e983c79..6905b900a16e 100644 --- a/Sources/Surfaces/SurfaceDevicePresence.swift +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceDevicePresence.swift @@ -4,8 +4,8 @@ import Foundation /// carried on ``SurfaceMachineInfo`` so the Devices tree, `surface.catalog`, and /// `cmux vm tree --json` all render the same liveness. Nil on local and cloud /// machines, which have no presence record. -struct SurfaceDevicePresence: Hashable, Codable, Sendable { - enum State: String, Codable, Sendable { +public struct SurfaceDevicePresence: Hashable, Codable, Sendable { + public enum State: String, Codable, Sendable { case online case offline /// No presence report: the stream is down, or the device is known only @@ -16,7 +16,7 @@ struct SurfaceDevicePresence: Hashable, Codable, Sendable { /// Whether this Mac's account may drive the device. The host authorizes only /// the account that is signed in on it, so the viewer never dials (and never /// presents its bearer token to) a Mac owned by another team member. - enum AccountTrust: String, Codable, Sendable { + public enum AccountTrust: String, Codable, Sendable { /// The device is owned by the signed-in account. case sameAccount /// The device belongs to another member of the team. @@ -25,20 +25,34 @@ struct SurfaceDevicePresence: Hashable, Codable, Sendable { case unknown } - var state: State + public var state: State /// Freshest heartbeat the service reported, when it reported any. - var lastSeenAt: Date? + public var lastSeenAt: Date? /// The app-instance tag (`default` for stable builds). - var tag: String + public var tag: String /// The host app's bundle id, for build-channel labels; nil for older hosts. - var bundleID: String? - var accountTrust: AccountTrust + public var bundleID: String? + public var accountTrust: AccountTrust - var isOnline: Bool { state == .online } + public init( + state: State, + lastSeenAt: Date? = nil, + tag: String, + bundleID: String? = nil, + accountTrust: AccountTrust + ) { + self.state = state + self.lastSeenAt = lastSeenAt + self.tag = tag + self.bundleID = bundleID + self.accountTrust = accountTrust + } + + public var isOnline: Bool { state == .online } /// "DEV · issue-8001" for tagged dev builds, "Nightly" / "RC" for those /// channels, nil for a stable instance (whose name needs no qualifier). - var buildLabel: String? { + public var buildLabel: String? { let lowered = (bundleID ?? "").lowercased() let taggedName = tag != SurfaceDeviceInstanceID.defaultTag ? tag : nil let channel: String diff --git a/Sources/Surfaces/SurfaceMachineID.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceMachineID.swift similarity index 65% rename from Sources/Surfaces/SurfaceMachineID.swift rename to Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceMachineID.swift index 8ea9270dee95..4a600cdf41be 100644 --- a/Sources/Surfaces/SurfaceMachineID.swift +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceMachineID.swift @@ -2,13 +2,13 @@ import Foundation /// Where a resource lives. `.local` is this Mac; `.cloud` is a cmux Cloud machine id; /// `.device` is another Mac signed into the same account (one tagged app instance). -enum SurfaceMachineID: Hashable, Codable, Sendable, CustomStringConvertible { +public enum SurfaceMachineID: Hashable, Codable, Sendable, CustomStringConvertible { case local case cloud(String) case ssh(String) case device(SurfaceDeviceInstanceID) - var description: String { + public var description: String { switch self { case .local: return "local" case .cloud(let id): return id @@ -18,8 +18,8 @@ enum SurfaceMachineID: Hashable, Codable, Sendable, CustomStringConvertible { } /// Wire form: `"local"`, the cloud machine id, or `device:@`. - var rawValue: String { description } - var kind: String { + public var rawValue: String { description } + public var kind: String { switch self { case .local: return "local" case .cloud: return "cloud" @@ -30,7 +30,7 @@ enum SurfaceMachineID: Hashable, Codable, Sendable, CustomStringConvertible { /// `"local"` and the `device:` prefix are reserved; everything else is a cloud /// machine id, exactly as before devices existed. - init(rawValue: String) { + public init(rawValue: String) { if rawValue == "local" { self = .local } else if rawValue.hasPrefix("ssh:") { @@ -42,12 +42,12 @@ enum SurfaceMachineID: Hashable, Codable, Sendable, CustomStringConvertible { } } - var isLocal: Bool { if case .local = self { return true } else { return false } } - var cloudMachineID: String? { if case .cloud(let id) = self { return id } else { return nil } } - var deviceInstance: SurfaceDeviceInstanceID? { if case .device(let instance) = self { return instance } else { return nil } } - var isDevice: Bool { deviceInstance != nil } - var isSSH: Bool { if case .ssh = self { return true }; return false } - var tuiMachineID: String? { + public var isLocal: Bool { if case .local = self { return true } else { return false } } + public var cloudMachineID: String? { if case .cloud(let id) = self { return id } else { return nil } } + public var deviceInstance: SurfaceDeviceInstanceID? { if case .device(let instance) = self { return instance } else { return nil } } + public var isDevice: Bool { deviceInstance != nil } + public var isSSH: Bool { if case .ssh = self { return true }; return false } + public var tuiMachineID: String? { switch self { case .cloud(let id): return id case .ssh: return rawValue diff --git a/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceProjectionIdentity.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceProjectionIdentity.swift new file mode 100644 index 000000000000..bdc077da325a --- /dev/null +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceProjectionIdentity.swift @@ -0,0 +1,23 @@ +import Foundation + +/// Stable local owner identities captured for one catalog projection read. +/// +/// ``SurfaceProjectionIdentity`` joins a catalog projection to the durable surface and +/// workspace identifiers that own it. Runtime panel and workspace selectors remain in +/// ``SurfaceProjection``; this value is only the stable ownership metadata used by +/// socket snapshots and policy decisions. +public struct SurfaceProjectionIdentity: Hashable, Sendable { + /// The durable surface identifier of the local panel owner. + public let stableSurfaceID: UUID + /// The durable workspace identifier of the local workspace owner. + public let stableWorkspaceID: UUID + + /// Creates an identity from the durable local owner identifiers. + /// - Parameters: + /// - stableSurfaceID: The panel's stable surface identifier. + /// - stableWorkspaceID: The workspace's stable identifier. + public init(stableSurfaceID: UUID, stableWorkspaceID: UUID) { + self.stableSurfaceID = stableSurfaceID + self.stableWorkspaceID = stableWorkspaceID + } +} diff --git a/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceResourceID+Ports.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceResourceID+Ports.swift new file mode 100644 index 000000000000..f4f02c84564d --- /dev/null +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceResourceID+Ports.swift @@ -0,0 +1,108 @@ +import Foundation + +extension CmuxTuiSnapshotParser { + /// One local socket binding from `ss -ltn` or `netstat -ltn`. + public struct ListeningPortBinding: Hashable, Sendable { + public let port: Int + public let address: String + + /// Loopback-only listeners cannot be reached through a machine's + /// private network address. A port with any wildcard/non-loopback + /// binding remains reachable even if another process also binds loopback. + public var isLoopbackOnly: Bool { + let normalized = address + .trimmingCharacters(in: CharacterSet(charactersIn: "[]")) + .split(separator: "%", maxSplits: 1, omittingEmptySubsequences: true) + .first + .map(String.init) + .map { $0.lowercased() } ?? "" + if normalized == "localhost" || normalized == "::1" { return true } + // Linux may print an IPv4 loopback listener as an IPv4-mapped IPv6 + // address (`::ffff:127.0.0.1`). Treat every mapped 127/8 address + // as loopback before deciding that a private-address preview is + // reachable. + if let mappedIPv4 = normalized.split(separator: ":").last, + mappedIPv4.split(separator: ".").count == 4 { + let octets = mappedIPv4.split(separator: ".") + if octets.first == "127" { return true } + } + let octets = normalized.split(separator: ".") + return octets.count == 4 && octets[0] == "127" + } + + public init(port: Int, address: String) { + self.port = port + self.address = address + } + } + + /// Parses local address/port pairs while retaining the bind address for + /// providers that open services directly over a private network. + public static func listeningPortBindings(fromSocketListing text: String) -> [ListeningPortBinding] { + var byPort: [Int: Set] = [:] + for line in text.split(separator: "\n") { + let columns = line.split(whereSeparator: { $0 == " " || $0 == "\t" }) + guard columns.count >= 4 else { continue } + // `ss`: State Recv-Q Send-Q Local:Port …; `netstat`: Proto Recv-Q + // Send-Q Local:Port … . The first numeric port in the local prefix + // is the local endpoint; peer ports are intentionally ignored. + for column in columns.prefix(5) { + guard let colon = column.lastIndex(of: ":"), + let port = Int(column[column.index(after: colon)...]), + (1...65_535).contains(port) else { continue } + let address = String(column[.. [Int] { + var loopbackOnlyByPort: [Int: Bool] = [:] + for binding in listeningPortBindings(fromSocketListing: text) { + loopbackOnlyByPort[binding.port] = + (loopbackOnlyByPort[binding.port] ?? true) && binding.isLoopbackOnly + } + return loopbackOnlyByPort.keys + .filter { privateAddress == nil || loopbackOnlyByPort[$0] == false } + .sorted() + } +} + +extension SurfaceResourceID { + /// The numeric port encoded by the canonical cloud forwarded-port identity. + /// Snapshot browser views that visit localhost are normalized to this key so + /// the machine port and its workspace row share one resource identity. + public var forwardedPort: Int? { + guard kind == .browser, key.hasPrefix("port:") else { return nil } + let value = key.dropFirst("port:".count) + guard let port = Int(value), (1...65_535).contains(port) else { return nil } + guard key == SurfaceResourceID.portKey(port) else { return nil } + return port + } + + /// Whether this id is the machine-level forwarded-port resource. + public var isForwardedPort: Bool { forwardedPort != nil } +} + +extension SurfaceResourceID { + /// The key every provider uses for a machine's one VNC display (T10 makes this a list). + public static let desktopDisplayKey = "display:1" + + /// The key for the browser that shows a forwarded HTTP port. + public static func portKey(_ port: Int) -> String { "port:\(port)" } +} diff --git a/Sources/Surfaces/SurfaceResourcePlacement.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceResourcePlacement.swift similarity index 76% rename from Sources/Surfaces/SurfaceResourcePlacement.swift rename to Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceResourcePlacement.swift index 9742a5cf969a..f15c77e811f5 100644 --- a/Sources/Surfaces/SurfaceResourcePlacement.swift +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceResourcePlacement.swift @@ -4,12 +4,12 @@ import Foundation /// row. A terminal id is not enough when one terminal is shown by several tabs. /// The tab id is an opaque routing key; the current catalog resolves it to fresh /// metadata immediately before materialization. -struct SurfaceResourcePlacement: Hashable, Codable, Sendable { - let resource: SurfaceResourceID - let remoteWorkspaceID: String? - let remoteTabID: String? +public struct SurfaceResourcePlacement: Hashable, Codable, Sendable { + public let resource: SurfaceResourceID + public let remoteWorkspaceID: String? + public let remoteTabID: String? - init( + public init( resource: SurfaceResourceID, remoteView: SurfaceRemoteView? = nil, remoteWorkspaceID: String? = nil, diff --git a/Sources/Cloud/VMMachineKind.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/VMMachineKind.swift similarity index 80% rename from Sources/Cloud/VMMachineKind.swift rename to Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/VMMachineKind.swift index 131e9e2934e3..60de54b6beed 100644 --- a/Sources/Cloud/VMMachineKind.swift +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/VMMachineKind.swift @@ -9,7 +9,7 @@ import Foundation /// image id on the client broke every build whose id drifted from the web /// deploy's manifest (`vm_image_config_error`), so the id is never sent unless /// a person passes `--image` explicitly. -enum VMMachineKind: String, CaseIterable, Sendable, Equatable { +public enum VMMachineKind: String, CaseIterable, Sendable, Equatable { /// Devtools, coding agents, and a desktop with a noVNC screen. case desktop /// Shell-only machine: same devtools, no screen. @@ -23,14 +23,14 @@ enum VMMachineKind: String, CaseIterable, Sendable, Equatable { /// provider's devbox image bundled xfce + noVNC; historical devbox images /// could also be shell-only, so matching it here published a Desktop /// surface for machines with no screen. Current images report their kind. - static func inferred(fromImage image: String) -> VMMachineKind { + public static func inferred(fromImage image: String) -> VMMachineKind { let lowered = image.lowercased() return lowered.contains("xfce") || lowered.contains("vnc") ? .desktop : .base } /// The kind a backend payload describes: its explicit `kind` field when /// present and valid, otherwise inferred from the image id. - static func resolved(kind rawKind: Any?, image: Any?) -> VMMachineKind { + public static func resolved(kind rawKind: Any?, image: Any?) -> VMMachineKind { if let raw = rawKind as? String, let kind = VMMachineKind(rawValue: raw.lowercased()) { return kind } @@ -40,20 +40,20 @@ enum VMMachineKind: String, CaseIterable, Sendable, Equatable { /// The product default: every create path that does not ask for a kind /// (the New Machine sheet, bare `cmux vm new`, `vm base open` / `vm base /// reset`) gets the devbox with a screen. Base remains for historical machines. - static let defaultKind: VMMachineKind = .desktop + public static let defaultKind: VMMachineKind = .desktop /// The `cmux vm new` / `vm base open` / `vm base reset` flag that requests /// this kind. Always sent, so the create never depends on a server default. - var cliFlag: String { + public var cliFlag: String { switch self { case .desktop: return "--desktop" case .base: return "--base" } } - var hasDesktop: Bool { self == .desktop } + public var hasDesktop: Bool { self == .desktop } - var displayName: String { + public var displayName: String { switch self { case .desktop: return String(localized: "machines.kind.desktop", defaultValue: "Desktop") @@ -68,7 +68,12 @@ enum VMMachineKind: String, CaseIterable, Sendable, Equatable { /// Which image the backend will provision for a kind, as `GET /api/vm` /// reports it under `limits.imageKinds`. Display-only: the client never sends /// the image back. -struct VMImageKindOption: Equatable, Sendable { - let kind: VMMachineKind - let image: String +public struct VMImageKindOption: Equatable, Sendable { + public let kind: VMMachineKind + public let image: String + + public init(kind: VMMachineKind, image: String) { + self.kind = kind + self.image = image + } } diff --git a/Packages/macOS/CmuxSurfaceCatalogModel/Tests/CmuxSurfaceCatalogModelTests/SurfaceCatalogMetadataTests.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Tests/CmuxSurfaceCatalogModelTests/SurfaceCatalogMetadataTests.swift new file mode 100644 index 000000000000..81ee392aca0a --- /dev/null +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Tests/CmuxSurfaceCatalogModelTests/SurfaceCatalogMetadataTests.swift @@ -0,0 +1,52 @@ +import CmuxSurfaceCatalogModel +import Foundation +import Testing + +@Suite struct SurfaceCatalogMetadataTests { + @Test func pendingWorkspaceMetadataRoundTrips() throws { + let machine = SurfaceMachineID.cloud("vm-1") + let nativeWorkspaceID = UUID() + let snapshot = SurfaceCatalogSnapshot( + pendingWorkspaceCreations: [machine: ["workspace-1": nativeWorkspaceID]], + pendingWorkspaceDeletions: [machine: ["workspace-2"]], + machines: [], + resources: [], + projections: [], + staleMachineIDs: [machine] + ) + let decoded = try JSONDecoder().decode( + SurfaceCatalogSnapshot.self, + from: JSONEncoder().encode(snapshot) + ) + #expect(decoded == snapshot) + #expect(decoded.pendingWorkspaceCreations?[machine]?["workspace-1"] == nativeWorkspaceID) + } + + @Test func legacySnapshotOmitsOptionalMetadata() throws { + let data = Data(#"{"machines":[],"resources":[],"projections":[]}"#.utf8) + let snapshot = try JSONDecoder().decode(SurfaceCatalogSnapshot.self, from: data) + #expect(snapshot.pendingWorkspaceCreations == nil) + #expect(snapshot.pendingWorkspaceDeletions == nil) + #expect(snapshot.staleMachineIDs.isEmpty) + } + + @Test func exportKeepsStableOwnersSeparateFromRuntimeSelectors() { + let projection = SurfaceProjection( + resource: SurfaceResourceID(machine: .cloud("vm-1"), kind: .terminal, key: "term-1"), + workspaceID: UUID(), + panelID: UUID() + ) + let identity = SurfaceProjectionIdentity(stableSurfaceID: UUID(), stableWorkspaceID: UUID()) + let snapshot = SurfaceCatalogSnapshot(machines: [], resources: [], projections: [projection]) + let export = SurfaceCatalogExport( + catalog: snapshot, + cloudStates: [], + projectionIdentities: [projection: identity] + ) + #expect(export.catalog.projections == [projection]) + #expect(export.projectionIdentities[projection] == identity) + #expect(export.projectionIdentities[projection]?.stableSurfaceID != projection.panelID) + #expect(export.projectionIdentities[projection]?.stableWorkspaceID != projection.workspaceID) + #expect(SurfaceCatalogExport(catalog: snapshot, cloudStates: []).projectionIdentities.isEmpty) + } +} diff --git a/Packages/macOS/CmuxSurfaceCatalogModel/Tests/CmuxSurfaceCatalogModelTests/SurfaceIdentityTests.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Tests/CmuxSurfaceCatalogModelTests/SurfaceIdentityTests.swift new file mode 100644 index 000000000000..01e8343b7258 --- /dev/null +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Tests/CmuxSurfaceCatalogModelTests/SurfaceIdentityTests.swift @@ -0,0 +1,13 @@ +import Foundation +import Testing +@testable import CmuxSurfaceCatalogModel + +@Suite struct SurfaceIdentityTests { + @Test func machineIDRoundTripsThroughItsWireForm() { + #expect(SurfaceMachineID(rawValue: "local") == .local) + #expect(SurfaceMachineID(rawValue: "vm-1") == .cloud("vm-1")) + #expect(SurfaceMachineID.cloud("vm-1").rawValue == "vm-1") + #expect(SurfaceMachineID.local.isLocal) + #expect(SurfaceMachineID.cloud("vm-1").cloudMachineID == "vm-1") + } +} diff --git a/Sources/App/MacDevicesComposition.swift b/Sources/App/MacDevicesComposition.swift index 78457a37db2a..c1a7c1a2290c 100644 --- a/Sources/App/MacDevicesComposition.swift +++ b/Sources/App/MacDevicesComposition.swift @@ -1,5 +1,6 @@ import CmuxSettings import CmuxSettingsUI +import CmuxSurfaceCatalogModel import Foundation /// The executable's construction boundary for device preferences, discovery, and Settings actions. diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index 797caf4cd553..a478153f3ab6 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -7,6 +7,7 @@ import CmuxBrowser import CmuxCommandPalette import CmuxPanes import CmuxControlSocket +import CmuxSurfaceCatalogModel import CmuxWindowing import CmuxNotifications import CmuxTerminalCore diff --git a/Sources/ClosedItemHistory.swift b/Sources/ClosedItemHistory.swift index 50f2e555c796..889c2ea7c7f3 100644 --- a/Sources/ClosedItemHistory.swift +++ b/Sources/ClosedItemHistory.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Combine import Bonsplit diff --git a/Sources/Cloud/CloudDiagnosticFailure.swift b/Sources/Cloud/CloudDiagnosticFailure.swift index b392b5ab9454..3664c6f9310a 100644 --- a/Sources/Cloud/CloudDiagnosticFailure.swift +++ b/Sources/Cloud/CloudDiagnosticFailure.swift @@ -1,4 +1,5 @@ import CmuxAuthRuntime +import CmuxSurfaceCatalogModel import Foundation enum CloudDiagnosticFailure: String, Codable, Sendable, Error { diff --git a/Sources/Cloud/CloudDisplayCoordinator.swift b/Sources/Cloud/CloudDisplayCoordinator.swift index 7e54d206af96..79383776238c 100644 --- a/Sources/Cloud/CloudDisplayCoordinator.swift +++ b/Sources/Cloud/CloudDisplayCoordinator.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Observation diff --git a/Sources/Cloud/CloudGuestDisplay.swift b/Sources/Cloud/CloudGuestDisplay.swift index 3100c74c8bfd..4a87378e90f9 100644 --- a/Sources/Cloud/CloudGuestDisplay.swift +++ b/Sources/Cloud/CloudGuestDisplay.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// One guest-issued display resource with a stable slot and noVNC target. diff --git a/Sources/Cloud/CloudGuestDisplaySnapshot.swift b/Sources/Cloud/CloudGuestDisplaySnapshot.swift index 11b1b576bd51..9af1ccf7355a 100644 --- a/Sources/Cloud/CloudGuestDisplaySnapshot.swift +++ b/Sources/Cloud/CloudGuestDisplaySnapshot.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// A guest-issued catalog. Connection targets are derived from validated guest diff --git a/Sources/Cloud/CloudMachineLink.swift b/Sources/Cloud/CloudMachineLink.swift index f74ef2c334c1..4d98a5b7d327 100644 --- a/Sources/Cloud/CloudMachineLink.swift +++ b/Sources/Cloud/CloudMachineLink.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import CmuxCore diff --git a/Sources/Cloud/CloudMachineLinkManager.swift b/Sources/Cloud/CloudMachineLinkManager.swift index 5fbf49d2e46e..f35bd4849a5d 100644 --- a/Sources/Cloud/CloudMachineLinkManager.swift +++ b/Sources/Cloud/CloudMachineLinkManager.swift @@ -1,4 +1,5 @@ import CmuxFoundation +import CmuxSurfaceCatalogModel import Foundation /// The app's headless cmux-tui links, one per awake cloud machine. Links are created on diff --git a/Sources/Cloud/CloudMachineRefreshCoordinator.swift b/Sources/Cloud/CloudMachineRefreshCoordinator.swift index 492c0e041907..d54d57772377 100644 --- a/Sources/Cloud/CloudMachineRefreshCoordinator.swift +++ b/Sources/Cloud/CloudMachineRefreshCoordinator.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Owns user-requested inventory refreshes for one panel, coalescing a burst diff --git a/Sources/Cloud/CloudMachineSurfacePresentation.swift b/Sources/Cloud/CloudMachineSurfacePresentation.swift index ab6525e3604e..5d7d52bc3caf 100644 --- a/Sources/Cloud/CloudMachineSurfacePresentation.swift +++ b/Sources/Cloud/CloudMachineSurfacePresentation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Immutable surface rows available even before the terminal link supplies a graph. diff --git a/Sources/Cloud/CloudNotificationPlacement.swift b/Sources/Cloud/CloudNotificationPlacement.swift index 6b0f0d0971c4..8aa714738328 100644 --- a/Sources/Cloud/CloudNotificationPlacement.swift +++ b/Sources/Cloud/CloudNotificationPlacement.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Which local notification records a read action covers, in the local diff --git a/Sources/Cloud/CloudNotificationSync.swift b/Sources/Cloud/CloudNotificationSync.swift index b6e7cadac5c1..93e8a75fd533 100644 --- a/Sources/Cloud/CloudNotificationSync.swift +++ b/Sources/Cloud/CloudNotificationSync.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation // Cloud notifications: the VM's cmux-tui daemon is the source of truth. diff --git a/Sources/Cloud/CloudSidebarDebugLabWindow.swift b/Sources/Cloud/CloudSidebarDebugLabWindow.swift index 88c8a38f3af8..efa489c44297 100644 --- a/Sources/Cloud/CloudSidebarDebugLabWindow.swift +++ b/Sources/Cloud/CloudSidebarDebugLabWindow.swift @@ -2,6 +2,7 @@ import AppKit import CmuxAppKitSupportUI import CmuxFoundation +import CmuxSurfaceCatalogModel import SwiftUI // MARK: - Cloud sidebar spacing lab diff --git a/Sources/Cloud/CloudTerminalCreationRequest.swift b/Sources/Cloud/CloudTerminalCreationRequest.swift index c04563813e00..ce3741c1cf4e 100644 --- a/Sources/Cloud/CloudTerminalCreationRequest.swift +++ b/Sources/Cloud/CloudTerminalCreationRequest.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Retains one UI intent's daemon identity across explicit retries. diff --git a/Sources/Cloud/CloudTerminalCreationRetryResolution.swift b/Sources/Cloud/CloudTerminalCreationRetryResolution.swift index e18602d3e54d..66e3c1eda6ac 100644 --- a/Sources/Cloud/CloudTerminalCreationRetryResolution.swift +++ b/Sources/Cloud/CloudTerminalCreationRetryResolution.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// The daemon's authoritative permission for one explicit terminal-creation retry. diff --git a/Sources/Cloud/CloudTerminalNavigationCatalog.swift b/Sources/Cloud/CloudTerminalNavigationCatalog.swift index 6b242ce783f4..e2a0deab740d 100644 --- a/Sources/Cloud/CloudTerminalNavigationCatalog.swift +++ b/Sources/Cloud/CloudTerminalNavigationCatalog.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Catalog capabilities consumed by terminal navigation, without owning a catalog or its stores. diff --git a/Sources/Cloud/CloudTreeDeletionPresentation.swift b/Sources/Cloud/CloudTreeDeletionPresentation.swift index 7d16d3a09f56..06649446bdac 100644 --- a/Sources/Cloud/CloudTreeDeletionPresentation.swift +++ b/Sources/Cloud/CloudTreeDeletionPresentation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Per-outline selection/expansion recovery. Resource truth remains in the catalog; diff --git a/Sources/Cloud/CloudTreeDeviceRow.swift b/Sources/Cloud/CloudTreeDeviceRow.swift index 03956233b91e..6a1a78fd5554 100644 --- a/Sources/Cloud/CloudTreeDeviceRow.swift +++ b/Sources/Cloud/CloudTreeDeviceRow.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Another Mac's header row in the Cloud-style outline: the same slot a cloud diff --git a/Sources/Cloud/CloudTreeMachineResources.swift b/Sources/Cloud/CloudTreeMachineResources.swift index d34cbbfea8a3..c7cdee9c525d 100644 --- a/Sources/Cloud/CloudTreeMachineResources.swift +++ b/Sources/Cloud/CloudTreeMachineResources.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CloudTreeNode.Kind { diff --git a/Sources/Cloud/CloudTreeNode.swift b/Sources/Cloud/CloudTreeNode.swift index 146265ae9dc4..e2ee59d06fbc 100644 --- a/Sources/Cloud/CloudTreeNode.swift +++ b/Sources/Cloud/CloudTreeNode.swift @@ -1,5 +1,6 @@ import CmuxCore import CmuxFoundation +import CmuxSurfaceCatalogModel import Foundation /// One row of the Cloud outline, built from the surface catalog: this Mac or a /// cloud machine, a pool ("Terminals", "Displays"), a group header, a workspace diff --git a/Sources/Cloud/CloudTreeNodeActions+WorkspaceLifecycle.swift b/Sources/Cloud/CloudTreeNodeActions+WorkspaceLifecycle.swift index c1e2579cc4b1..f5aec1e1df4d 100644 --- a/Sources/Cloud/CloudTreeNodeActions+WorkspaceLifecycle.swift +++ b/Sources/Cloud/CloudTreeNodeActions+WorkspaceLifecycle.swift @@ -1,4 +1,5 @@ import AppKit +import CmuxSurfaceCatalogModel import Foundation extension CloudTreeNodeActions { diff --git a/Sources/Cloud/CloudTreeNodeActions.swift b/Sources/Cloud/CloudTreeNodeActions.swift index 38ad107f260f..db7b3134f4ec 100644 --- a/Sources/Cloud/CloudTreeNodeActions.swift +++ b/Sources/Cloud/CloudTreeNodeActions.swift @@ -1,4 +1,5 @@ import AppKit +import CmuxSurfaceCatalogModel import Foundation /// Closure bundle handed to Cloud outline rows for the nodes below a machine. struct CloudTreeNodeActions { diff --git a/Sources/Cloud/CloudTreeNodeBuilder+Devices.swift b/Sources/Cloud/CloudTreeNodeBuilder+Devices.swift index 3c5a1db4139f..a3a78c2f81fb 100644 --- a/Sources/Cloud/CloudTreeNodeBuilder+Devices.swift +++ b/Sources/Cloud/CloudTreeNodeBuilder+Devices.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Device rows for the Cloud-style outline: every `.device` machine the diff --git a/Sources/Cloud/CloudTreeNodeBuilder+Ports.swift b/Sources/Cloud/CloudTreeNodeBuilder+Ports.swift index d3999b7b73a6..11aca21900ac 100644 --- a/Sources/Cloud/CloudTreeNodeBuilder+Ports.swift +++ b/Sources/Cloud/CloudTreeNodeBuilder+Ports.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CloudTreeNodeBuilder { diff --git a/Sources/Cloud/CloudTreeOutlineView+Devices.swift b/Sources/Cloud/CloudTreeOutlineView+Devices.swift index 30f3397e9fe3..71f6daf11fe2 100644 --- a/Sources/Cloud/CloudTreeOutlineView+Devices.swift +++ b/Sources/Cloud/CloudTreeOutlineView+Devices.swift @@ -1,4 +1,5 @@ import AppKit +import CmuxSurfaceCatalogModel import Foundation /// One request to expand and select a row, identified by token so SwiftUI diff --git a/Sources/Cloud/CloudTreeOutlineView+Displays.swift b/Sources/Cloud/CloudTreeOutlineView+Displays.swift index f92e5f249fe8..99f6c3235630 100644 --- a/Sources/Cloud/CloudTreeOutlineView+Displays.swift +++ b/Sources/Cloud/CloudTreeOutlineView+Displays.swift @@ -1,4 +1,5 @@ import AppKit +import CmuxSurfaceCatalogModel extension CloudTreeOutlineView.Coordinator { func displayMenuItems(machine: SurfaceMachineID, canCreate: Bool) -> [NSMenuItem] { diff --git a/Sources/Cloud/CloudTreeOutlineView+MachineMenu.swift b/Sources/Cloud/CloudTreeOutlineView+MachineMenu.swift index e0f60e21eb1f..86b7ecc1127f 100644 --- a/Sources/Cloud/CloudTreeOutlineView+MachineMenu.swift +++ b/Sources/Cloud/CloudTreeOutlineView+MachineMenu.swift @@ -1,4 +1,5 @@ import AppKit +import CmuxSurfaceCatalogModel extension CloudTreeOutlineView.Coordinator { func machineMenuItems(_ machine: MachineSnapshot) -> [NSMenuItem] { diff --git a/Sources/Cloud/CloudTreeOutlineView.swift b/Sources/Cloud/CloudTreeOutlineView.swift index 22d38ae5239e..fa26abfe4a36 100644 --- a/Sources/Cloud/CloudTreeOutlineView.swift +++ b/Sources/Cloud/CloudTreeOutlineView.swift @@ -2,6 +2,7 @@ import AppKit import Bonsplit import CmuxAppKitSupportUI import CmuxFoundation +import CmuxSurfaceCatalogModel import SwiftUI /// The Cloud catalog outline: local workspaces, then machine workspaces and resources. Rows are pure /// display (`CloudTreeRowContentView`); the coordinator owns selection, diff --git a/Sources/Cloud/CloudTreeRemoteWorkspaces.swift b/Sources/Cloud/CloudTreeRemoteWorkspaces.swift index ffcaaa7ec02e..c72a8b00f207 100644 --- a/Sources/Cloud/CloudTreeRemoteWorkspaces.swift +++ b/Sources/Cloud/CloudTreeRemoteWorkspaces.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// What one cmux-tui workspace on a cloud machine holds, in the order the diff --git a/Sources/Cloud/CloudTreeRowContentView.swift b/Sources/Cloud/CloudTreeRowContentView.swift index c969c29ed2dc..91fa8bb5b057 100644 --- a/Sources/Cloud/CloudTreeRowContentView.swift +++ b/Sources/Cloud/CloudTreeRowContentView.swift @@ -1,4 +1,5 @@ import CmuxFoundation +import CmuxSurfaceCatalogModel import SwiftUI enum CloudTreeIconPalette { static let workspace = Color.blue diff --git a/Sources/Cloud/CloudTreeStyleGalleryWindow.swift b/Sources/Cloud/CloudTreeStyleGalleryWindow.swift index b5b04b3f8bae..6d6ec0fdf6f2 100644 --- a/Sources/Cloud/CloudTreeStyleGalleryWindow.swift +++ b/Sources/Cloud/CloudTreeStyleGalleryWindow.swift @@ -1,5 +1,6 @@ #if DEBUG import AppKit +import CmuxSurfaceCatalogModel import SwiftUI /// Debug-only Cloud tree style picker (Debug → Debug Windows → Cloud Tree Style diff --git a/Sources/Cloud/CloudTreeTerminalNavigationCoordinator.swift b/Sources/Cloud/CloudTreeTerminalNavigationCoordinator.swift index 89b689ec10bf..b640786bc3c6 100644 --- a/Sources/Cloud/CloudTreeTerminalNavigationCoordinator.swift +++ b/Sources/Cloud/CloudTreeTerminalNavigationCoordinator.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Routes an ordinary Cloud tree terminal activation to its parent workspace. diff --git a/Sources/Cloud/CloudTuiCommandLine+Placement.swift b/Sources/Cloud/CloudTuiCommandLine+Placement.swift index 5c43e04ab197..02568c533590 100644 --- a/Sources/Cloud/CloudTuiCommandLine+Placement.swift +++ b/Sources/Cloud/CloudTuiCommandLine+Placement.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CloudTuiCommandLine { diff --git a/Sources/Cloud/CloudTuiCommandLine.swift b/Sources/Cloud/CloudTuiCommandLine.swift index b106ef3ff499..bb89b6464341 100644 --- a/Sources/Cloud/CloudTuiCommandLine.swift +++ b/Sources/Cloud/CloudTuiCommandLine.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// The exact argv the app hands the cmux-tui client for each cloud-tree operation. diff --git a/Sources/Cloud/CloudTuiManualMirrorSession.swift b/Sources/Cloud/CloudTuiManualMirrorSession.swift index 432e32574a75..cd7cf8df53fc 100644 --- a/Sources/Cloud/CloudTuiManualMirrorSession.swift +++ b/Sources/Cloud/CloudTuiManualMirrorSession.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import CmuxTerminal import CmuxCore import CmuxCloudImagePaste diff --git a/Sources/Cloud/CloudTuiPersistentRequestBuilder.swift b/Sources/Cloud/CloudTuiPersistentRequestBuilder.swift index 5dc76b1d67ec..8cb2937f8219 100644 --- a/Sources/Cloud/CloudTuiPersistentRequestBuilder.swift +++ b/Sources/Cloud/CloudTuiPersistentRequestBuilder.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Immutable wire request. Application code uses resource operations directly; diff --git a/Sources/Cloud/CloudTuiPersistentResourceConnection.swift b/Sources/Cloud/CloudTuiPersistentResourceConnection.swift index 9cbee9792c17..ec998243772f 100644 --- a/Sources/Cloud/CloudTuiPersistentResourceConnection.swift +++ b/Sources/Cloud/CloudTuiPersistentResourceConnection.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import CoreFoundation import Foundation diff --git a/Sources/Cloud/CloudTuiTerminalProjectionTarget.swift b/Sources/Cloud/CloudTuiTerminalProjectionTarget.swift deleted file mode 100644 index 1ba5cb20ca55..000000000000 --- a/Sources/Cloud/CloudTuiTerminalProjectionTarget.swift +++ /dev/null @@ -1,11 +0,0 @@ -/// A concrete remote pane location accepted by cmux-tui's `terminal.project` command. -/// -/// The public terminal resource can outlive every tab that displays it. When that happens, -/// the native macOS mirror first creates one remote view in an existing pane, then resolves -/// the daemon-local surface id for the byte attachment. -struct CloudTuiTerminalProjectionTarget: Equatable, Sendable { - let workspaceID: String - let screenID: String - let paneID: String - let index: Int -} diff --git a/Sources/Cloud/MachineCreateOperation.swift b/Sources/Cloud/MachineCreateOperation.swift index cdac0027e695..0a72d3e44983 100644 --- a/Sources/Cloud/MachineCreateOperation.swift +++ b/Sources/Cloud/MachineCreateOperation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import CmuxCloudMachines diff --git a/Sources/Cloud/MachineCreateRequest.swift b/Sources/Cloud/MachineCreateRequest.swift index c73581662eb7..ce1842ade264 100644 --- a/Sources/Cloud/MachineCreateRequest.swift +++ b/Sources/Cloud/MachineCreateRequest.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import CmuxCloudMachines diff --git a/Sources/Cloud/MachineSnapshotBuilder.swift b/Sources/Cloud/MachineSnapshotBuilder.swift index bbe599e7e99f..ed839a729717 100644 --- a/Sources/Cloud/MachineSnapshotBuilder.swift +++ b/Sources/Cloud/MachineSnapshotBuilder.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation enum MachineSnapshotBuilder { diff --git a/Sources/Cloud/MachinesPanelView.swift b/Sources/Cloud/MachinesPanelView.swift index c367eb3b0ea8..51d140e66175 100644 --- a/Sources/Cloud/MachinesPanelView.swift +++ b/Sources/Cloud/MachinesPanelView.swift @@ -1,6 +1,7 @@ import AppKit import CmuxCloudMachines import CmuxSettings +import CmuxSurfaceCatalogModel import SwiftUI /// Right-sidebar Machines tab: the user's cloud machine fleet as a Finder-like diff --git a/Sources/Cloud/MachinesPanelViewModel.swift b/Sources/Cloud/MachinesPanelViewModel.swift index 706d1595fa78..e88a62268e99 100644 --- a/Sources/Cloud/MachinesPanelViewModel.swift +++ b/Sources/Cloud/MachinesPanelViewModel.swift @@ -1,4 +1,5 @@ import CmuxCloudMachines +import CmuxSurfaceCatalogModel import Foundation import SwiftUI extension Notification.Name { diff --git a/Sources/Cloud/NewMachineModel.swift b/Sources/Cloud/NewMachineModel.swift index 1819b45385dc..159d659833af 100644 --- a/Sources/Cloud/NewMachineModel.swift +++ b/Sources/Cloud/NewMachineModel.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Observation diff --git a/Sources/Cloud/PortForward/CloudBrowserAccessState.swift b/Sources/Cloud/PortForward/CloudBrowserAccessState.swift index ce8231ca4d70..e381518ba70f 100644 --- a/Sources/Cloud/PortForward/CloudBrowserAccessState.swift +++ b/Sources/Cloud/PortForward/CloudBrowserAccessState.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import CmuxCore import CmuxFoundation diff --git a/Sources/Cloud/PortForward/CloudPortRoutePlan.swift b/Sources/Cloud/PortForward/CloudPortRoutePlan.swift index b1f40df385d1..cb4b56d58bf8 100644 --- a/Sources/Cloud/PortForward/CloudPortRoutePlan.swift +++ b/Sources/Cloud/PortForward/CloudPortRoutePlan.swift @@ -1,4 +1,5 @@ import CmuxFoundation +import CmuxSurfaceCatalogModel import Foundation /// Browser opens target the VM's private address. The provider may carry that diff --git a/Sources/Cloud/Sidebar/CloudSidebarNotificationCoordinator.swift b/Sources/Cloud/Sidebar/CloudSidebarNotificationCoordinator.swift index 1e30b94b64eb..382ed44c109a 100644 --- a/Sources/Cloud/Sidebar/CloudSidebarNotificationCoordinator.swift +++ b/Sources/Cloud/Sidebar/CloudSidebarNotificationCoordinator.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Coalesces admitted notifications until the next main-actor turn. Each machine diff --git a/Sources/Cloud/Sidebar/CloudSidebarOrganizationStore.swift b/Sources/Cloud/Sidebar/CloudSidebarOrganizationStore.swift index ac6e82774bfb..2ab5f955a57f 100644 --- a/Sources/Cloud/Sidebar/CloudSidebarOrganizationStore.swift +++ b/Sources/Cloud/Sidebar/CloudSidebarOrganizationStore.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Observation diff --git a/Sources/Cloud/Sidebar/SurfaceCatalog+SidebarOrganization.swift b/Sources/Cloud/Sidebar/SurfaceCatalog+SidebarOrganization.swift index 1d403bebed68..f70abf2c6cd5 100644 --- a/Sources/Cloud/Sidebar/SurfaceCatalog+SidebarOrganization.swift +++ b/Sources/Cloud/Sidebar/SurfaceCatalog+SidebarOrganization.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalog { diff --git a/Sources/Cloud/Sidebar/SurfaceResource+CatalogOrdering.swift b/Sources/Cloud/Sidebar/SurfaceResource+CatalogOrdering.swift index d87f6e3cb740..c72200e1506b 100644 --- a/Sources/Cloud/Sidebar/SurfaceResource+CatalogOrdering.swift +++ b/Sources/Cloud/Sidebar/SurfaceResource+CatalogOrdering.swift @@ -1,3 +1,5 @@ +import CmuxSurfaceCatalogModel + extension SurfaceResource { /// Stable catalog order shared by whole-fleet and machine-scoped snapshots. func catalogPrecedes(_ other: SurfaceResource) -> Bool { diff --git a/Sources/Cloud/Sidebar/TerminalController+CloudSidebar.swift b/Sources/Cloud/Sidebar/TerminalController+CloudSidebar.swift index 116ebf266126..32747a4770bd 100644 --- a/Sources/Cloud/Sidebar/TerminalController+CloudSidebar.swift +++ b/Sources/Cloud/Sidebar/TerminalController+CloudSidebar.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension TerminalController { diff --git a/Sources/Cloud/SurfaceResourceDragPayload.swift b/Sources/Cloud/SurfaceResourceDragPayload.swift index 7578a7a8afb8..e9b679b6ed43 100644 --- a/Sources/Cloud/SurfaceResourceDragPayload.swift +++ b/Sources/Cloud/SurfaceResourceDragPayload.swift @@ -1,4 +1,5 @@ import Bonsplit +import CmuxSurfaceCatalogModel import Foundation /// Registers a Cloud tree row as the same live capability Bonsplit tab drags diff --git a/Sources/Cloud/VMCapabilities.swift b/Sources/Cloud/VMCapabilities.swift index 2956b442aaf1..00a5b225b31b 100644 --- a/Sources/Cloud/VMCapabilities.swift +++ b/Sources/Cloud/VMCapabilities.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// What a machine's provider can do; the app offers only verbs that can succeed diff --git a/Sources/Cloud/VMClient.swift b/Sources/Cloud/VMClient.swift index 907caa837612..648ceea97922 100644 --- a/Sources/Cloud/VMClient.swift +++ b/Sources/Cloud/VMClient.swift @@ -1,5 +1,6 @@ import CmuxAuthRuntime import CMUXMobileCore +import CmuxSurfaceCatalogModel import Foundation extension URLError.Code { diff --git a/Sources/Cloud/VMClientSocketCommands.swift b/Sources/Cloud/VMClientSocketCommands.swift index 8513ab1eb716..3f5e085c1207 100644 --- a/Sources/Cloud/VMClientSocketCommands.swift +++ b/Sources/Cloud/VMClientSocketCommands.swift @@ -1,5 +1,6 @@ import CmuxControlSocket import CmuxSettings +import CmuxSurfaceCatalogModel import Foundation extension TerminalController { nonisolated func socketWorkerCloudVMResponse( diff --git a/Sources/CloudTerminalCreationCoordinator.swift b/Sources/CloudTerminalCreationCoordinator.swift index 53b51a20ad8d..96e9946586a2 100644 --- a/Sources/CloudTerminalCreationCoordinator.swift +++ b/Sources/CloudTerminalCreationCoordinator.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import os diff --git a/Sources/CommandPalette/CurrentWorkPalettePresentation.swift b/Sources/CommandPalette/CurrentWorkPalettePresentation.swift index 9178074d7d05..15830c8ae433 100644 --- a/Sources/CommandPalette/CurrentWorkPalettePresentation.swift +++ b/Sources/CommandPalette/CurrentWorkPalettePresentation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Pure presentation of the same bounded values returned by `current.list`. diff --git a/Sources/Devices/AppDelegate+NewDeviceWorkspace.swift b/Sources/Devices/AppDelegate+NewDeviceWorkspace.swift index fb10cbb9a4a1..a5229c6a596e 100644 --- a/Sources/Devices/AppDelegate+NewDeviceWorkspace.swift +++ b/Sources/Devices/AppDelegate+NewDeviceWorkspace.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension AppDelegate { diff --git a/Sources/Devices/DeviceDirectory.swift b/Sources/Devices/DeviceDirectory.swift index 5420e827ac6d..9c0bf80b5855 100644 --- a/Sources/Devices/DeviceDirectory.swift +++ b/Sources/Devices/DeviceDirectory.swift @@ -1,6 +1,7 @@ import CMUXMobileCore import CmuxAuthRuntime import CmuxIrohTransport +import CmuxSurfaceCatalogModel import Foundation import Observation import OSLog diff --git a/Sources/Devices/DeviceDirectoryIdentityIndex.swift b/Sources/Devices/DeviceDirectoryIdentityIndex.swift index 371b6fb4d01f..cc8487bee166 100644 --- a/Sources/Devices/DeviceDirectoryIdentityIndex.swift +++ b/Sources/Devices/DeviceDirectoryIdentityIndex.swift @@ -1,4 +1,5 @@ import CMUXMobileCore +import CmuxSurfaceCatalogModel import Foundation /// Joins the two identities one Mac app instance is known by. diff --git a/Sources/Devices/DeviceDirectoryMerge.swift b/Sources/Devices/DeviceDirectoryMerge.swift index aa7201c896e5..2a717e067140 100644 --- a/Sources/Devices/DeviceDirectoryMerge.swift +++ b/Sources/Devices/DeviceDirectoryMerge.swift @@ -1,5 +1,6 @@ import CMUXMobileCore import CmuxIrohTransport +import CmuxSurfaceCatalogModel import Foundation /// The pure merge behind the Devices directory: the pairing store's saved diff --git a/Sources/Devices/DeviceDirectoryRecord.swift b/Sources/Devices/DeviceDirectoryRecord.swift index 0ade714f6189..37633cadc403 100644 --- a/Sources/Devices/DeviceDirectoryRecord.swift +++ b/Sources/Devices/DeviceDirectoryRecord.swift @@ -1,4 +1,5 @@ import CMUXMobileCore +import CmuxSurfaceCatalogModel import Foundation /// What the account's presence service currently says about a device. diff --git a/Sources/Devices/DeviceIrxClient.swift b/Sources/Devices/DeviceIrxClient.swift index 4deed9f4c635..b71097a2bbd3 100644 --- a/Sources/Devices/DeviceIrxClient.swift +++ b/Sources/Devices/DeviceIrxClient.swift @@ -1,6 +1,7 @@ import CMUXMobileCore import CmuxIrohTransport import CmuxIrxTransport +import CmuxSurfaceCatalogModel import Foundation /// Owns outgoing control sessions while borrowing the Mac's single registered endpoint. diff --git a/Sources/Devices/DeviceIrxPeerTransportProvider.swift b/Sources/Devices/DeviceIrxPeerTransportProvider.swift index b744a8b9dae5..664e6c7e9527 100644 --- a/Sources/Devices/DeviceIrxPeerTransportProvider.swift +++ b/Sources/Devices/DeviceIrxPeerTransportProvider.swift @@ -1,5 +1,6 @@ import CMUXMobileCore import CmuxIrohTransport +import CmuxSurfaceCatalogModel /// Carries the intended physical Mac and build through the deferred factory. struct DeviceIrxPeerTransportProvider: CmxIrohDeferredTransportProviding { diff --git a/Sources/Devices/DeviceLink.swift b/Sources/Devices/DeviceLink.swift index fee299171a61..6020f0b79015 100644 --- a/Sources/Devices/DeviceLink.swift +++ b/Sources/Devices/DeviceLink.swift @@ -1,6 +1,7 @@ import CMUXMobileCore import CmuxCore import CmuxMobileRPC +import CmuxSurfaceCatalogModel import Foundation import OSLog diff --git a/Sources/Devices/DeviceLinkAuthorization.swift b/Sources/Devices/DeviceLinkAuthorization.swift index 5e506cabfd96..504f451406e7 100644 --- a/Sources/Devices/DeviceLinkAuthorization.swift +++ b/Sources/Devices/DeviceLinkAuthorization.swift @@ -1,5 +1,6 @@ import CMUXMobileCore import CmuxMobileRPC +import CmuxSurfaceCatalogModel import Foundation /// A Mac the person paired in Settings › Computers, as the directory lists it: diff --git a/Sources/Devices/DeviceLinkRuntime.swift b/Sources/Devices/DeviceLinkRuntime.swift index 4238fcb274db..cad60b9cf198 100644 --- a/Sources/Devices/DeviceLinkRuntime.swift +++ b/Sources/Devices/DeviceLinkRuntime.swift @@ -3,6 +3,7 @@ import CmuxAuthRuntime import CmuxIrohTransport import CmuxMobileRPC import CmuxMobileTransport +import CmuxSurfaceCatalogModel import Foundation /// The Mac's `MobileSyncRuntime`: what the shared RPC client needs from the diff --git a/Sources/Devices/DevicePresenceFrame.swift b/Sources/Devices/DevicePresenceFrame.swift index affa4e9b2922..228e072ffb0b 100644 --- a/Sources/Devices/DevicePresenceFrame.swift +++ b/Sources/Devices/DevicePresenceFrame.swift @@ -1,4 +1,5 @@ import CMUXMobileCore +import CmuxSurfaceCatalogModel import Foundation /// One running cmux app instance on a device, as the presence service reports diff --git a/Sources/Devices/DeviceRegistryDirectoryClient.swift b/Sources/Devices/DeviceRegistryDirectoryClient.swift index 084213c4e6b5..aba72ef0094b 100644 --- a/Sources/Devices/DeviceRegistryDirectoryClient.swift +++ b/Sources/Devices/DeviceRegistryDirectoryClient.swift @@ -1,5 +1,6 @@ import CMUXMobileCore import CmuxAuthRuntime +import CmuxSurfaceCatalogModel import Foundation /// Reads the team's durable device registry (`GET /api/devices`), the source diff --git a/Sources/Devices/DeviceRouteSelector.swift b/Sources/Devices/DeviceRouteSelector.swift index 0679a00c8271..e2f60dbbc1eb 100644 --- a/Sources/Devices/DeviceRouteSelector.swift +++ b/Sources/Devices/DeviceRouteSelector.swift @@ -1,4 +1,5 @@ import CMUXMobileCore +import CmuxSurfaceCatalogModel import Foundation /// Picks which of a device's attach routes this Mac dials, in the host's diff --git a/Sources/Devices/DeviceSurfaceProvider+Mutations.swift b/Sources/Devices/DeviceSurfaceProvider+Mutations.swift index d5b35b53f729..fe4f812db194 100644 --- a/Sources/Devices/DeviceSurfaceProvider+Mutations.swift +++ b/Sources/Devices/DeviceSurfaceProvider+Mutations.swift @@ -1,4 +1,5 @@ import CMUXMobileCore +import CmuxSurfaceCatalogModel import Foundation /// The mutation half of ``DeviceSurfaceProvider``: every verb the Cloud tree, diff --git a/Sources/Devices/DeviceSurfaceProvider+TerminalLayout.swift b/Sources/Devices/DeviceSurfaceProvider+TerminalLayout.swift index 9feac447908d..6a39075b474b 100644 --- a/Sources/Devices/DeviceSurfaceProvider+TerminalLayout.swift +++ b/Sources/Devices/DeviceSurfaceProvider+TerminalLayout.swift @@ -1,4 +1,5 @@ import CmuxCore +import CmuxSurfaceCatalogModel import Foundation extension DeviceSurfaceProvider: SurfaceLayoutTerminalCreating { diff --git a/Sources/Devices/DeviceSurfaceProvider.swift b/Sources/Devices/DeviceSurfaceProvider.swift index cbaa0d734aeb..15039379f97f 100644 --- a/Sources/Devices/DeviceSurfaceProvider.swift +++ b/Sources/Devices/DeviceSurfaceProvider.swift @@ -1,5 +1,6 @@ import CMUXMobileCore import CmuxCore +import CmuxSurfaceCatalogModel import CmuxTerminal import Foundation diff --git a/Sources/Devices/DeviceSurfaceProviderRegistry.swift b/Sources/Devices/DeviceSurfaceProviderRegistry.swift index 15c84d36575f..901f2901ee23 100644 --- a/Sources/Devices/DeviceSurfaceProviderRegistry.swift +++ b/Sources/Devices/DeviceSurfaceProviderRegistry.swift @@ -1,6 +1,7 @@ import CmuxAuthRuntime import CmuxFoundation import CmuxSettings +import CmuxSurfaceCatalogModel import Foundation import Observation diff --git a/Sources/Devices/DeviceWorkspaceCreationCoordinator.swift b/Sources/Devices/DeviceWorkspaceCreationCoordinator.swift index 509dee2a3418..ca23995b0741 100644 --- a/Sources/Devices/DeviceWorkspaceCreationCoordinator.swift +++ b/Sources/Devices/DeviceWorkspaceCreationCoordinator.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Shares device workspace creation between synchronous shortcut and menu actions. diff --git a/Sources/Devices/DeviceWorkspaceLayoutCoordinator.swift b/Sources/Devices/DeviceWorkspaceLayoutCoordinator.swift index 6dd867fc0502..6f3cef0769c3 100644 --- a/Sources/Devices/DeviceWorkspaceLayoutCoordinator.swift +++ b/Sources/Devices/DeviceWorkspaceLayoutCoordinator.swift @@ -1,4 +1,5 @@ import CmuxCore +import CmuxSurfaceCatalogModel import Foundation /// Orders local layout intents and projects the owning Mac's accepted snapshots. diff --git a/Sources/Devices/DeviceWorkspaceLayoutHost.swift b/Sources/Devices/DeviceWorkspaceLayoutHost.swift index d4e607e2f9e4..46a2f094b35b 100644 --- a/Sources/Devices/DeviceWorkspaceLayoutHost.swift +++ b/Sources/Devices/DeviceWorkspaceLayoutHost.swift @@ -1,4 +1,5 @@ import CmuxCore +import CmuxSurfaceCatalogModel import Foundation /// Owns Mac layout revisions and applies authenticated, workspace-scoped edits. diff --git a/Sources/Devices/DeviceWorkspaceProjection+Layout.swift b/Sources/Devices/DeviceWorkspaceProjection+Layout.swift index 5788887456f5..584948e53ea3 100644 --- a/Sources/Devices/DeviceWorkspaceProjection+Layout.swift +++ b/Sources/Devices/DeviceWorkspaceProjection+Layout.swift @@ -1,5 +1,6 @@ import CMUXMobileCore import CmuxCore +import CmuxSurfaceCatalogModel import Foundation extension DeviceWorkspaceProjection { diff --git a/Sources/Devices/DeviceWorkspaceProjection.swift b/Sources/Devices/DeviceWorkspaceProjection.swift index c2c16205ed63..9e4fbc5338f6 100644 --- a/Sources/Devices/DeviceWorkspaceProjection.swift +++ b/Sources/Devices/DeviceWorkspaceProjection.swift @@ -1,5 +1,6 @@ import CMUXMobileCore import CmuxCore +import CmuxSurfaceCatalogModel import Foundation /// Maps another Mac's synced workspace records onto the surface catalog: one diff --git a/Sources/Devices/DevicesPanelViewModel.swift b/Sources/Devices/DevicesPanelViewModel.swift index 83db602f182e..7d090edda6f5 100644 --- a/Sources/Devices/DevicesPanelViewModel.swift +++ b/Sources/Devices/DevicesPanelViewModel.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Observation diff --git a/Sources/Devices/DevicesPreferencesModel.swift b/Sources/Devices/DevicesPreferencesModel.swift index 3162e4919020..5397b165c47e 100644 --- a/Sources/Devices/DevicesPreferencesModel.swift +++ b/Sources/Devices/DevicesPreferencesModel.swift @@ -1,4 +1,5 @@ import CmuxSettings +import CmuxSurfaceCatalogModel import Foundation import Observation diff --git a/Sources/Devices/SurfaceCatalog+DeviceNames.swift b/Sources/Devices/SurfaceCatalog+DeviceNames.swift index c4a53a01c7dd..d96e4cfb6b03 100644 --- a/Sources/Devices/SurfaceCatalog+DeviceNames.swift +++ b/Sources/Devices/SurfaceCatalog+DeviceNames.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalog { diff --git a/Sources/Devices/Workspace+DeviceLayout.swift b/Sources/Devices/Workspace+DeviceLayout.swift index 7cb33dbbd2c6..97143ea3e46a 100644 --- a/Sources/Devices/Workspace+DeviceLayout.swift +++ b/Sources/Devices/Workspace+DeviceLayout.swift @@ -1,5 +1,6 @@ import Bonsplit import CmuxCore +import CmuxSurfaceCatalogModel import Foundation extension Workspace { diff --git a/Sources/Devices/Workspace+DeviceMachineContext.swift b/Sources/Devices/Workspace+DeviceMachineContext.swift index 00bbce22913a..7885f6813b37 100644 --- a/Sources/Devices/Workspace+DeviceMachineContext.swift +++ b/Sources/Devices/Workspace+DeviceMachineContext.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension Workspace { diff --git a/Sources/DockSplitStore+BrowserActions.swift b/Sources/DockSplitStore+BrowserActions.swift index 55ddcdacd732..9793ded73d5a 100644 --- a/Sources/DockSplitStore+BrowserActions.swift +++ b/Sources/DockSplitStore+BrowserActions.swift @@ -1,5 +1,6 @@ import AppKit import Bonsplit +import CmuxSurfaceCatalogModel import Foundation extension DockSplitStore { diff --git a/Sources/Hive/HiveComputersService.swift b/Sources/Hive/HiveComputersService.swift index 7524e14461fd..65e25644642c 100644 --- a/Sources/Hive/HiveComputersService.swift +++ b/Sources/Hive/HiveComputersService.swift @@ -2,6 +2,7 @@ import CMUXMobileCore import CmuxAuthRuntime import CmuxHive import CmuxSettingsUI +import CmuxSurfaceCatalogModel import Foundation import Observation diff --git a/Sources/Panels/BrowserPanel+CloudConnection.swift b/Sources/Panels/BrowserPanel+CloudConnection.swift index d01297363b3b..a02d78a21979 100644 --- a/Sources/Panels/BrowserPanel+CloudConnection.swift +++ b/Sources/Panels/BrowserPanel+CloudConnection.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import WebKit diff --git a/Sources/Panels/PanelStableSurfaceIdentity.swift b/Sources/Panels/PanelStableSurfaceIdentity.swift index 733bd1c0f505..9dc2899dd1d4 100644 --- a/Sources/Panels/PanelStableSurfaceIdentity.swift +++ b/Sources/Panels/PanelStableSurfaceIdentity.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Boxed restart-stable surface identity for durable deep links. diff --git a/Sources/RemoteTui/RemoteTuiMachine.swift b/Sources/RemoteTui/RemoteTuiMachine.swift index ff9525cbabdb..f947b9ecbf57 100644 --- a/Sources/RemoteTui/RemoteTuiMachine.swift +++ b/Sources/RemoteTui/RemoteTuiMachine.swift @@ -1,4 +1,5 @@ import CmuxCore +import CmuxSurfaceCatalogModel import Foundation /// Hosting metadata for the shared terminal provider. SSH never requires a Cloud account. diff --git a/Sources/RemoteTui/SSHTuiWorkspaceCoordinator.swift b/Sources/RemoteTui/SSHTuiWorkspaceCoordinator.swift index c749e85c3b2e..7d5e3c824079 100644 --- a/Sources/RemoteTui/SSHTuiWorkspaceCoordinator.swift +++ b/Sources/RemoteTui/SSHTuiWorkspaceCoordinator.swift @@ -1,4 +1,5 @@ import CmuxCore +import CmuxSurfaceCatalogModel import Foundation /// Composes SSH carriers with the same terminal graph and native projections as Cloud. diff --git a/Sources/RemoteTui/TerminalController+SSHTui.swift b/Sources/RemoteTui/TerminalController+SSHTui.swift index 13b85f368cd1..d911ce574e87 100644 --- a/Sources/RemoteTui/TerminalController+SSHTui.swift +++ b/Sources/RemoteTui/TerminalController+SSHTui.swift @@ -1,5 +1,6 @@ import CmuxCore import CmuxFoundation +import CmuxSurfaceCatalogModel import Foundation extension TerminalController { diff --git a/Sources/RemoteTui/TerminalController+SSHTuiSessions.swift b/Sources/RemoteTui/TerminalController+SSHTuiSessions.swift index 2f6efd947d1d..e760ef8ec2cc 100644 --- a/Sources/RemoteTui/TerminalController+SSHTuiSessions.swift +++ b/Sources/RemoteTui/TerminalController+SSHTuiSessions.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension TerminalController { diff --git a/Sources/RemoteTui/Workspace+SSHTuiStatus.swift b/Sources/RemoteTui/Workspace+SSHTuiStatus.swift index e9cfff1dffad..dc53e3f057fe 100644 --- a/Sources/RemoteTui/Workspace+SSHTuiStatus.swift +++ b/Sources/RemoteTui/Workspace+SSHTuiStatus.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import CmuxCore diff --git a/Sources/SessionBrowserPanelSnapshot.swift b/Sources/SessionBrowserPanelSnapshot.swift index b2952fb6bf53..6102985da3c0 100644 --- a/Sources/SessionBrowserPanelSnapshot.swift +++ b/Sources/SessionBrowserPanelSnapshot.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import CmuxBrowser diff --git a/Sources/SessionPersistence.swift b/Sources/SessionPersistence.swift index ec54812743cd..9683bf81b950 100644 --- a/Sources/SessionPersistence.swift +++ b/Sources/SessionPersistence.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import CoreGraphics import CmuxBrowser import CmuxCore diff --git a/Sources/Surfaces/AppDelegate+SurfaceOwnership.swift b/Sources/Surfaces/AppDelegate+SurfaceOwnership.swift index 2d1f14d040a3..c5330f11a139 100644 --- a/Sources/Surfaces/AppDelegate+SurfaceOwnership.swift +++ b/Sources/Surfaces/AppDelegate+SurfaceOwnership.swift @@ -1,4 +1,5 @@ import Bonsplit +import CmuxSurfaceCatalogModel import Foundation extension AppDelegate { diff --git a/Sources/Surfaces/CloudAgentNameContext.swift b/Sources/Surfaces/CloudAgentNameContext.swift index f9683d283b9d..2dfd9c83e882 100644 --- a/Sources/Surfaces/CloudAgentNameContext.swift +++ b/Sources/Surfaces/CloudAgentNameContext.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// A callback's read precondition, captured before an agent computes its title. diff --git a/Sources/Surfaces/CloudMachineLoadingReservation.swift b/Sources/Surfaces/CloudMachineLoadingReservation.swift index e89eb3f6f5f5..57c1fb6552d2 100644 --- a/Sources/Surfaces/CloudMachineLoadingReservation.swift +++ b/Sources/Surfaces/CloudMachineLoadingReservation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// An immutable claim on one creating pane, retained only by its attachment task. diff --git a/Sources/Surfaces/CloudPaneCreationFailure.swift b/Sources/Surfaces/CloudPaneCreationFailure.swift index 4a85cac82939..98f19678ae15 100644 --- a/Sources/Surfaces/CloudPaneCreationFailure.swift +++ b/Sources/Surfaces/CloudPaneCreationFailure.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// The latest cloud terminal creation failure shown by its owning workspace. diff --git a/Sources/Surfaces/CloudPaneCreationFailureStore.swift b/Sources/Surfaces/CloudPaneCreationFailureStore.swift index c42909f2dfac..e2b24bacab66 100644 --- a/Sources/Surfaces/CloudPaneCreationFailureStore.swift +++ b/Sources/Surfaces/CloudPaneCreationFailureStore.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Observation diff --git a/Sources/Surfaces/CloudPlacementCoordinator.swift b/Sources/Surfaces/CloudPlacementCoordinator.swift index df7afd15216a..be6b5c7ecd2f 100644 --- a/Sources/Surfaces/CloudPlacementCoordinator.swift +++ b/Sources/Surfaces/CloudPlacementCoordinator.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Observation diff --git a/Sources/Surfaces/CloudRenameCoordinator.swift b/Sources/Surfaces/CloudRenameCoordinator.swift index 851e75c8abf9..5af221b97e14 100644 --- a/Sources/Surfaces/CloudRenameCoordinator.swift +++ b/Sources/Surfaces/CloudRenameCoordinator.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Remote rename requests are process-wide because one daemon workspace or tab can be diff --git a/Sources/Surfaces/CloudTerminalAttachmentResolver.swift b/Sources/Surfaces/CloudTerminalAttachmentResolver.swift index 1d33c614b2ba..d71ec1129cfe 100644 --- a/Sources/Surfaces/CloudTerminalAttachmentResolver.swift +++ b/Sources/Surfaces/CloudTerminalAttachmentResolver.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Maps a public `term_…` id to the daemon-local numeric surface a byte diff --git a/Sources/Surfaces/CloudTerminalCreationReceipt.swift b/Sources/Surfaces/CloudTerminalCreationReceipt.swift index f260a27e0a4b..6e9d414d9f57 100644 --- a/Sources/Surfaces/CloudTerminalCreationReceipt.swift +++ b/Sources/Surfaces/CloudTerminalCreationReceipt.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// The authoritative remote result of one reserved pane, shared with child creates. diff --git a/Sources/Surfaces/CloudTerminalLayoutCreation.swift b/Sources/Surfaces/CloudTerminalLayoutCreation.swift index 74b7e3d39a81..d9a21fae2139 100644 --- a/Sources/Surfaces/CloudTerminalLayoutCreation.swift +++ b/Sources/Surfaces/CloudTerminalLayoutCreation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Creates one terminal beside an exact daemon tab from a fresh operation snapshot. diff --git a/Sources/Surfaces/CloudTerminalLayoutCreationResult.swift b/Sources/Surfaces/CloudTerminalLayoutCreationResult.swift index cf78b93f9dbd..3a8d83740042 100644 --- a/Sources/Surfaces/CloudTerminalLayoutCreationResult.swift +++ b/Sources/Surfaces/CloudTerminalLayoutCreationResult.swift @@ -1,3 +1,5 @@ +import CmuxSurfaceCatalogModel + /// The daemon's creation receipt and the source snapshot's workspace identity. struct CloudTerminalLayoutCreationResult: Sendable { let created: CmuxTuiSnapshotParser.CreatedTerminalPath diff --git a/Sources/Surfaces/CloudTerminalPaneClosure.swift b/Sources/Surfaces/CloudTerminalPaneClosure.swift index e904899b1b6f..0fe023a41e01 100644 --- a/Sources/Surfaces/CloudTerminalPaneClosure.swift +++ b/Sources/Surfaces/CloudTerminalPaneClosure.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Decides which cloud attach panes must close after a graph publication. diff --git a/Sources/Surfaces/CloudTerminalPaneReservation.swift b/Sources/Surfaces/CloudTerminalPaneReservation.swift index 9a2967300363..bd3578c70536 100644 --- a/Sources/Surfaces/CloudTerminalPaneReservation.swift +++ b/Sources/Surfaces/CloudTerminalPaneReservation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import CmuxTerminal import Foundation diff --git a/Sources/Surfaces/CloudTerminalSourcePlacement.swift b/Sources/Surfaces/CloudTerminalSourcePlacement.swift index b6ea5ae1fade..52896e3004df 100644 --- a/Sources/Surfaces/CloudTerminalSourcePlacement.swift +++ b/Sources/Surfaces/CloudTerminalSourcePlacement.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Immutable Cloud identity captured at a terminal-create boundary. diff --git a/Sources/Surfaces/CloudVMTabState.swift b/Sources/Surfaces/CloudVMTabState.swift deleted file mode 100644 index 4081cd29edbe..000000000000 --- a/Sources/Surfaces/CloudVMTabState.swift +++ /dev/null @@ -1,13 +0,0 @@ -import Foundation - -/// One accepted daemon tab, including the durable owner and revision of its name. -struct CloudVMTabState: Hashable, Codable, Sendable { - var id: String - var paneID: String - var name: String? - var index: Int - var focused: Bool - var contentKind: String - var contentID: String - var nameAuthority: CloudTabNameAuthority? = nil -} diff --git a/Sources/Surfaces/CloudWorkspaceBindingReconciliation.swift b/Sources/Surfaces/CloudWorkspaceBindingReconciliation.swift index f2de4ff99434..902e4d1256da 100644 --- a/Sources/Surfaces/CloudWorkspaceBindingReconciliation.swift +++ b/Sources/Surfaces/CloudWorkspaceBindingReconciliation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CloudWorkspaceRenameService { diff --git a/Sources/Surfaces/CloudWorkspaceCreationCoordinator.swift b/Sources/Surfaces/CloudWorkspaceCreationCoordinator.swift index 0e978daf6fe5..1009a679d9ba 100644 --- a/Sources/Surfaces/CloudWorkspaceCreationCoordinator.swift +++ b/Sources/Surfaces/CloudWorkspaceCreationCoordinator.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Owns the shared pending workspace projection; both sidebars consume its receipt identity. diff --git a/Sources/Surfaces/CloudWorkspaceCreationHost.swift b/Sources/Surfaces/CloudWorkspaceCreationHost.swift index b3cbd77010d0..3659e6495b88 100644 --- a/Sources/Surfaces/CloudWorkspaceCreationHost.swift +++ b/Sources/Surfaces/CloudWorkspaceCreationHost.swift @@ -1,5 +1,6 @@ import AppKit import Bonsplit +import CmuxSurfaceCatalogModel import CmuxTerminal import Foundation diff --git a/Sources/Surfaces/CloudWorkspaceCreationOperation.swift b/Sources/Surfaces/CloudWorkspaceCreationOperation.swift index 93c0c364ba7e..0d9e2b075a9c 100644 --- a/Sources/Surfaces/CloudWorkspaceCreationOperation.swift +++ b/Sources/Surfaces/CloudWorkspaceCreationOperation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// One request owns its receipt and native reservation until the graph adopts them. diff --git a/Sources/Surfaces/CloudWorkspaceDeletionLedger.swift b/Sources/Surfaces/CloudWorkspaceDeletionLedger.swift index 1c906908d3e8..9369014ff2d6 100644 --- a/Sources/Surfaces/CloudWorkspaceDeletionLedger.swift +++ b/Sources/Surfaces/CloudWorkspaceDeletionLedger.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Observation diff --git a/Sources/Surfaces/CloudWorkspaceLayoutTranslator.swift b/Sources/Surfaces/CloudWorkspaceLayoutTranslator.swift index 102ca9586497..5c5aaf2b7cbe 100644 --- a/Sources/Surfaces/CloudWorkspaceLayoutTranslator.swift +++ b/Sources/Surfaces/CloudWorkspaceLayoutTranslator.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// The geometry a machine workspace should open with on this Mac: the daemon screen's diff --git a/Sources/Surfaces/CloudWorkspaceProjectionCoordinator.swift b/Sources/Surfaces/CloudWorkspaceProjectionCoordinator.swift index ce740afff35e..a7b93394147d 100644 --- a/Sources/Surfaces/CloudWorkspaceProjectionCoordinator.swift +++ b/Sources/Surfaces/CloudWorkspaceProjectionCoordinator.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Materializes the accepted Cloud graph into bound native workspaces. The graph diff --git a/Sources/Surfaces/CloudWorkspaceProjectionEnvironment.swift b/Sources/Surfaces/CloudWorkspaceProjectionEnvironment.swift index 3f61bdde57e8..41ba42789c50 100644 --- a/Sources/Surfaces/CloudWorkspaceProjectionEnvironment.swift +++ b/Sources/Surfaces/CloudWorkspaceProjectionEnvironment.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Native projection operations supplied by the app, or an isolated test host. diff --git a/Sources/Surfaces/CloudWorkspaceProjectionPlan.swift b/Sources/Surfaces/CloudWorkspaceProjectionPlan.swift index 13f56447e529..0e037a586f09 100644 --- a/Sources/Surfaces/CloudWorkspaceProjectionPlan.swift +++ b/Sources/Surfaces/CloudWorkspaceProjectionPlan.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Exact daemon tab identities determine membership; a shared terminal process diff --git a/Sources/Surfaces/CloudWorkspaceRenameService+Admission.swift b/Sources/Surfaces/CloudWorkspaceRenameService+Admission.swift index ae72ec221c2e..c0c4e24b1ca1 100644 --- a/Sources/Surfaces/CloudWorkspaceRenameService+Admission.swift +++ b/Sources/Surfaces/CloudWorkspaceRenameService+Admission.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CloudWorkspaceRenameService { diff --git a/Sources/Surfaces/CloudWorkspaceRenameService+Reconciliation.swift b/Sources/Surfaces/CloudWorkspaceRenameService+Reconciliation.swift index ddeff295b641..78daa191d0a1 100644 --- a/Sources/Surfaces/CloudWorkspaceRenameService+Reconciliation.swift +++ b/Sources/Surfaces/CloudWorkspaceRenameService+Reconciliation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CloudWorkspaceRenameService { diff --git a/Sources/Surfaces/CmuxTuiSnapshotParser+Displays.swift b/Sources/Surfaces/CmuxTuiSnapshotParser+Displays.swift index 3c67465f243a..e1b5b7a81d13 100644 --- a/Sources/Surfaces/CmuxTuiSnapshotParser+Displays.swift +++ b/Sources/Surfaces/CmuxTuiSnapshotParser+Displays.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CmuxTuiSnapshotParser { diff --git a/Sources/Surfaces/CmuxTuiSnapshotParser+Placement.swift b/Sources/Surfaces/CmuxTuiSnapshotParser+Placement.swift index ce3bfeed9c0f..e9fd378bfda6 100644 --- a/Sources/Surfaces/CmuxTuiSnapshotParser+Placement.swift +++ b/Sources/Surfaces/CmuxTuiSnapshotParser+Placement.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CmuxTuiSnapshotParser { diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+AgentNaming.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+AgentNaming.swift index a6e9e2906499..e570607e366e 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+AgentNaming.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+AgentNaming.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CmuxTuiSurfaceProvider: SurfaceAgentNaming { diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+CloseTerminal.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+CloseTerminal.swift index fa970fbb68bd..95a2ab872baf 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+CloseTerminal.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+CloseTerminal.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CmuxTuiSurfaceProvider { diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+Displays.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+Displays.swift index ad5cfebfbe62..f7a923661c45 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+Displays.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+Displays.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import CmuxFoundation diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+Environment.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+Environment.swift index 27ea0b2099d9..7f246c723f3f 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+Environment.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+Environment.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CmuxTuiSurfaceProvider { diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+FileDelivery.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+FileDelivery.swift index a664a223d8c1..25ad6886069b 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+FileDelivery.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+FileDelivery.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CmuxTuiSurfaceProvider { diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+GuestURL.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+GuestURL.swift index b9a93aaf6a91..2a52232a9981 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+GuestURL.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+GuestURL.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CmuxTuiSurfaceProvider { diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+Hosting.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+Hosting.swift index f71ce12a35ca..8aa0bb9565f9 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+Hosting.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+Hosting.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import CmuxSettings diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+LayoutCreation.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+LayoutCreation.swift index ea065b784d65..423c05a44196 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+LayoutCreation.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+LayoutCreation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CmuxTuiSurfaceProvider: SurfaceLayoutTerminalCreating { diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+ManualMirror.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+ManualMirror.swift index 08d4992ec9cf..17b5dd9ec09a 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+ManualMirror.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+ManualMirror.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import CmuxTerminal import CmuxRemoteSession import Foundation diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+Notifications.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+Notifications.swift index 0f35dd1a365f..d79ef1b90da5 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+Notifications.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+Notifications.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CmuxTuiSurfaceProvider { diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+PendingCreationRecovery.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+PendingCreationRecovery.swift index 69e1a9ee941a..8ab2a18497a5 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+PendingCreationRecovery.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+PendingCreationRecovery.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Read-your-write overlays for Cloud terminal creation and rename receipts. diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+PlacementSync.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+PlacementSync.swift index f2f1b19191a4..63e6bdaf2bc6 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+PlacementSync.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+PlacementSync.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Revision-fenced placement edits, retried once with fresh destination coordinates. diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+PortForward.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+PortForward.swift index 5535f73665a3..6349d4f6d04e 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+PortForward.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+PortForward.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CmuxTuiSurfaceProvider { diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+Refresh.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+Refresh.swift index 755ed8326271..4cab8dc983e5 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+Refresh.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+Refresh.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CmuxTuiSurfaceProvider { diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+TabRename.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+TabRename.swift index b4be8053cd9d..ab311b27503e 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+TabRename.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+TabRename.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Placement rename writes share the provider revision/receipt fence for user and agent calls. diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalCreation.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalCreation.swift index 6dd7e94a4494..2862974d23e3 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalCreation.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalCreation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation @MainActor diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalIO.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalIO.swift index 649b837c4f4c..84b25a77a28a 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalIO.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalIO.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension CmuxTuiSurfaceProvider { diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalPrimitives.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalPrimitives.swift index 43a71c77fbe9..9069a473ac73 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalPrimitives.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalPrimitives.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation @MainActor diff --git a/Sources/Surfaces/CmuxTuiSurfaceProviderRegistry.swift b/Sources/Surfaces/CmuxTuiSurfaceProviderRegistry.swift index ca7f64c18671..dbaa729b5428 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProviderRegistry.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProviderRegistry.swift @@ -1,5 +1,6 @@ import CmuxFoundation import CmuxSettings +import CmuxSurfaceCatalogModel import Foundation /// Owns one ``CmuxTuiSurfaceProvider`` per cloud machine and keeps the catalog's machine diff --git a/Sources/Surfaces/CmuxTuiSurfaceProviders.swift b/Sources/Surfaces/CmuxTuiSurfaceProviders.swift index 8b896ce01cd8..ab9628d9f7f4 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProviders.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProviders.swift @@ -1,6 +1,7 @@ import CmuxCore import CmuxFoundation import CmuxSettings +import CmuxSurfaceCatalogModel import Foundation /// One cloud machine's resources: its cmux-tui terminals (over the headless link), its /// noVNC screen, and its forwarded ports. Terminals live in the machine's cmux-tui diff --git a/Sources/Surfaces/CurrentWorkInput.swift b/Sources/Surfaces/CurrentWorkInput.swift index 6b1e475768eb..b36123cbe548 100644 --- a/Sources/Surfaces/CurrentWorkInput.swift +++ b/Sources/Surfaces/CurrentWorkInput.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Immutable capture for the pure current-work reduction; never persisted as a graph. diff --git a/Sources/Surfaces/CurrentWorkQueryService.swift b/Sources/Surfaces/CurrentWorkQueryService.swift index fe069fcdf945..be108b9c03cd 100644 --- a/Sources/Surfaces/CurrentWorkQueryService.swift +++ b/Sources/Surfaces/CurrentWorkQueryService.swift @@ -1,5 +1,6 @@ import CmuxAgentChat import CmuxNotifications +import CmuxSurfaceCatalogModel import Foundation /// Captures current owners in one actor turn. Reading never refreshes or discovers work. diff --git a/Sources/Surfaces/CurrentWorkSnapshot.swift b/Sources/Surfaces/CurrentWorkSnapshot.swift index 4f01d6dcb161..a2c77618a463 100644 --- a/Sources/Surfaces/CurrentWorkSnapshot.swift +++ b/Sources/Surfaces/CurrentWorkSnapshot.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// A bounded, read-only projection of existing work owners for CLI and Find Work. diff --git a/Sources/Surfaces/DockSplitStore+SurfaceOwnership.swift b/Sources/Surfaces/DockSplitStore+SurfaceOwnership.swift index 64ae8d7c0edb..08eac763b633 100644 --- a/Sources/Surfaces/DockSplitStore+SurfaceOwnership.swift +++ b/Sources/Surfaces/DockSplitStore+SurfaceOwnership.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension DockSplitStore { diff --git a/Sources/Surfaces/LocalSurfaceProvider.swift b/Sources/Surfaces/LocalSurfaceProvider.swift index 5a5417738e65..e1498ab6cca1 100644 --- a/Sources/Surfaces/LocalSurfaceProvider.swift +++ b/Sources/Surfaces/LocalSurfaceProvider.swift @@ -1,5 +1,6 @@ import AppKit import Bonsplit +import CmuxSurfaceCatalogModel import Foundation /// This Mac as a surface provider. diff --git a/Sources/Surfaces/PendingRemoteCreation.swift b/Sources/Surfaces/PendingRemoteCreation.swift index 610c04f820e2..216f8ff347a9 100644 --- a/Sources/Surfaces/PendingRemoteCreation.swift +++ b/Sources/Surfaces/PendingRemoteCreation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation @MainActor diff --git a/Sources/Surfaces/SurfaceCatalog+AgentIcons.swift b/Sources/Surfaces/SurfaceCatalog+AgentIcons.swift index 06042e101069..e5f01415cd6d 100644 --- a/Sources/Surfaces/SurfaceCatalog+AgentIcons.swift +++ b/Sources/Surfaces/SurfaceCatalog+AgentIcons.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalog { diff --git a/Sources/Surfaces/SurfaceCatalog+CloudBinding.swift b/Sources/Surfaces/SurfaceCatalog+CloudBinding.swift index 41c2695bb981..f0a6c973734c 100644 --- a/Sources/Surfaces/SurfaceCatalog+CloudBinding.swift +++ b/Sources/Surfaces/SurfaceCatalog+CloudBinding.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalog { diff --git a/Sources/Surfaces/SurfaceCatalog+CloudDirectoryMetadata.swift b/Sources/Surfaces/SurfaceCatalog+CloudDirectoryMetadata.swift index 22375f291445..9eb6c1b36685 100644 --- a/Sources/Surfaces/SurfaceCatalog+CloudDirectoryMetadata.swift +++ b/Sources/Surfaces/SurfaceCatalog+CloudDirectoryMetadata.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalog { diff --git a/Sources/Surfaces/SurfaceCatalog+CloudPorts.swift b/Sources/Surfaces/SurfaceCatalog+CloudPorts.swift index 1048149dc7e9..d5c293ea5095 100644 --- a/Sources/Surfaces/SurfaceCatalog+CloudPorts.swift +++ b/Sources/Surfaces/SurfaceCatalog+CloudPorts.swift @@ -1,100 +1,7 @@ import CmuxFoundation +import CmuxSurfaceCatalogModel import Foundation -extension CmuxTuiSnapshotParser { - /// One local socket binding from `ss -ltn` or `netstat -ltn`. - struct ListeningPortBinding: Hashable, Sendable { - let port: Int - let address: String - - /// Loopback-only listeners cannot be reached through a machine's - /// private network address. A port with any wildcard/non-loopback - /// binding remains reachable even if another process also binds loopback. - var isLoopbackOnly: Bool { - let normalized = address - .trimmingCharacters(in: CharacterSet(charactersIn: "[]")) - .split(separator: "%", maxSplits: 1, omittingEmptySubsequences: true) - .first - .map(String.init) - .map { $0.lowercased() } ?? "" - if normalized == "localhost" || normalized == "::1" { return true } - // Linux may print an IPv4 loopback listener as an IPv4-mapped IPv6 - // address (`::ffff:127.0.0.1`). Treat every mapped 127/8 address - // as loopback before deciding that a private-address preview is - // reachable. - if let mappedIPv4 = normalized.split(separator: ":").last, - mappedIPv4.split(separator: ".").count == 4 { - let octets = mappedIPv4.split(separator: ".") - if octets.first == "127" { return true } - } - let octets = normalized.split(separator: ".") - return octets.count == 4 && octets[0] == "127" - } - } - - /// Parses local address/port pairs while retaining the bind address for - /// providers that open services directly over a private network. - static func listeningPortBindings(fromSocketListing text: String) -> [ListeningPortBinding] { - var byPort: [Int: Set] = [:] - for line in text.split(separator: "\n") { - let columns = line.split(whereSeparator: { $0 == " " || $0 == "\t" }) - guard columns.count >= 4 else { continue } - // `ss`: State Recv-Q Send-Q Local:Port …; `netstat`: Proto Recv-Q - // Send-Q Local:Port … . The first numeric port in the local prefix - // is the local endpoint; peer ports are intentionally ignored. - for column in columns.prefix(5) { - guard let colon = column.lastIndex(of: ":"), - let port = Int(column[column.index(after: colon)...]), - (1...65_535).contains(port) else { continue } - let address = String(column[.. [Int] { - var loopbackOnlyByPort: [Int: Bool] = [:] - for binding in listeningPortBindings(fromSocketListing: text) { - loopbackOnlyByPort[binding.port] = - (loopbackOnlyByPort[binding.port] ?? true) && binding.isLoopbackOnly - } - return loopbackOnlyByPort.keys - .filter { privateAddress == nil || loopbackOnlyByPort[$0] == false } - .sorted() - } -} - -extension SurfaceResourceID { - /// The numeric port encoded by the canonical cloud forwarded-port identity. - /// Snapshot browser views that visit localhost are normalized to this key so - /// the machine port and its workspace row share one resource identity. - var forwardedPort: Int? { - guard kind == .browser, key.hasPrefix("port:") else { return nil } - let value = key.dropFirst("port:".count) - guard let port = Int(value), (1...65_535).contains(port) else { return nil } - guard key == SurfaceResourceID.portKey(port) else { return nil } - return port - } - - /// Whether this id is the machine-level forwarded-port resource. - var isForwardedPort: Bool { forwardedPort != nil } -} - extension SurfaceCatalog { /// Localized destination error shared by the sidebar and socket open paths. nonisolated static func portDestinationUnavailableMessage(machine: SurfaceMachineID) -> String { diff --git a/Sources/Surfaces/SurfaceCatalog+CloudRenameSubmission.swift b/Sources/Surfaces/SurfaceCatalog+CloudRenameSubmission.swift index 6c7be4b4d782..df5c287c9d64 100644 --- a/Sources/Surfaces/SurfaceCatalog+CloudRenameSubmission.swift +++ b/Sources/Surfaces/SurfaceCatalog+CloudRenameSubmission.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalog { diff --git a/Sources/Surfaces/SurfaceCatalog+CloudTerminalNavigation.swift b/Sources/Surfaces/SurfaceCatalog+CloudTerminalNavigation.swift index 93d5a601a788..ae3a468bf507 100644 --- a/Sources/Surfaces/SurfaceCatalog+CloudTerminalNavigation.swift +++ b/Sources/Surfaces/SurfaceCatalog+CloudTerminalNavigation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalog: CloudTerminalNavigationCatalog { diff --git a/Sources/Surfaces/SurfaceCatalog+CloudWorkspaceProjection.swift b/Sources/Surfaces/SurfaceCatalog+CloudWorkspaceProjection.swift index 96deb8033494..5fc52fe9684c 100644 --- a/Sources/Surfaces/SurfaceCatalog+CloudWorkspaceProjection.swift +++ b/Sources/Surfaces/SurfaceCatalog+CloudWorkspaceProjection.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalog { diff --git a/Sources/Surfaces/SurfaceCatalog+Displays.swift b/Sources/Surfaces/SurfaceCatalog+Displays.swift index 291934f97611..ade796073e72 100644 --- a/Sources/Surfaces/SurfaceCatalog+Displays.swift +++ b/Sources/Surfaces/SurfaceCatalog+Displays.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalog { diff --git a/Sources/Surfaces/SurfaceCatalog+Groups.swift b/Sources/Surfaces/SurfaceCatalog+Groups.swift index 25d259df86c4..81d460981c11 100644 --- a/Sources/Surfaces/SurfaceCatalog+Groups.swift +++ b/Sources/Surfaces/SurfaceCatalog+Groups.swift @@ -1,4 +1,5 @@ import CmuxCore +import CmuxSurfaceCatalogModel import Foundation /// A collection of resources that travels as one drag or one "open all": a cmux-tui diff --git a/Sources/Surfaces/SurfaceCatalog+NameAuthority.swift b/Sources/Surfaces/SurfaceCatalog+NameAuthority.swift index 8660d2c61d95..923ecf264f9b 100644 --- a/Sources/Surfaces/SurfaceCatalog+NameAuthority.swift +++ b/Sources/Surfaces/SurfaceCatalog+NameAuthority.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalog { diff --git a/Sources/Surfaces/SurfaceCatalog+Ownership.swift b/Sources/Surfaces/SurfaceCatalog+Ownership.swift index 971f9c7cea83..cbba19614607 100644 --- a/Sources/Surfaces/SurfaceCatalog+Ownership.swift +++ b/Sources/Surfaces/SurfaceCatalog+Ownership.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalog { diff --git a/Sources/Surfaces/SurfaceCatalog+ProjectionQueries.swift b/Sources/Surfaces/SurfaceCatalog+ProjectionQueries.swift index eab4485ff2f8..da814f9ab191 100644 --- a/Sources/Surfaces/SurfaceCatalog+ProjectionQueries.swift +++ b/Sources/Surfaces/SurfaceCatalog+ProjectionQueries.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalog { diff --git a/Sources/Surfaces/SurfaceCatalog+Snapshot.swift b/Sources/Surfaces/SurfaceCatalog+Snapshot.swift index 003f78241f0a..98cc9059bc23 100644 --- a/Sources/Surfaces/SurfaceCatalog+Snapshot.swift +++ b/Sources/Surfaces/SurfaceCatalog+Snapshot.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalog { diff --git a/Sources/Surfaces/SurfaceCatalog+WorkspaceDeletion.swift b/Sources/Surfaces/SurfaceCatalog+WorkspaceDeletion.swift index 1873483668cc..2b32c17a18bd 100644 --- a/Sources/Surfaces/SurfaceCatalog+WorkspaceDeletion.swift +++ b/Sources/Surfaces/SurfaceCatalog+WorkspaceDeletion.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalog { diff --git a/Sources/Surfaces/SurfaceCatalog+WorkspaceMembership.swift b/Sources/Surfaces/SurfaceCatalog+WorkspaceMembership.swift index f7b734f7480b..f7d3cc8c499e 100644 --- a/Sources/Surfaces/SurfaceCatalog+WorkspaceMembership.swift +++ b/Sources/Surfaces/SurfaceCatalog+WorkspaceMembership.swift @@ -1,4 +1,5 @@ import CmuxCore +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalog { diff --git a/Sources/Surfaces/SurfaceCatalog.swift b/Sources/Surfaces/SurfaceCatalog.swift index 1481e13582a8..4e516809b523 100644 --- a/Sources/Surfaces/SurfaceCatalog.swift +++ b/Sources/Surfaces/SurfaceCatalog.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Observation /// The single owner of surface identities and projections on this Mac. diff --git a/Sources/Surfaces/SurfaceCatalogQueryService.swift b/Sources/Surfaces/SurfaceCatalogQueryService.swift index ef2da325f02b..ac316d4bf9ae 100644 --- a/Sources/Surfaces/SurfaceCatalogQueryService.swift +++ b/Sources/Surfaces/SurfaceCatalogQueryService.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// How much work a catalog read may do before exporting. diff --git a/Sources/Surfaces/SurfaceCatalogSnapshot+DeviceVisibility.swift b/Sources/Surfaces/SurfaceCatalogSnapshot+DeviceVisibility.swift index 5f4526087bac..351e49879bad 100644 --- a/Sources/Surfaces/SurfaceCatalogSnapshot+DeviceVisibility.swift +++ b/Sources/Surfaces/SurfaceCatalogSnapshot+DeviceVisibility.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceCatalogSnapshot { diff --git a/Sources/Surfaces/SurfaceLayoutTerminalCreating.swift b/Sources/Surfaces/SurfaceLayoutTerminalCreating.swift index b4354bb4756c..283bb33164a6 100644 --- a/Sources/Surfaces/SurfaceLayoutTerminalCreating.swift +++ b/Sources/Surfaces/SurfaceLayoutTerminalCreating.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// A Cloud provider can author a native new-tab/split intent in the daemon's diff --git a/Sources/Surfaces/SurfaceOwnershipPolicy.swift b/Sources/Surfaces/SurfaceOwnershipPolicy.swift index ca7cf9dbe76d..2db76483b24f 100644 --- a/Sources/Surfaces/SurfaceOwnershipPolicy.swift +++ b/Sources/Surfaces/SurfaceOwnershipPolicy.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// A destination's ownership rule, independent of UI, drag payloads, and I/O. diff --git a/Sources/Surfaces/SurfacePaneFactory+CloudManualMirror.swift b/Sources/Surfaces/SurfacePaneFactory+CloudManualMirror.swift index 8ce294a9c4e9..eceae3b429ce 100644 --- a/Sources/Surfaces/SurfacePaneFactory+CloudManualMirror.swift +++ b/Sources/Surfaces/SurfacePaneFactory+CloudManualMirror.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import CmuxTerminal import GhosttyKit import Foundation diff --git a/Sources/Surfaces/SurfacePaneFactory+ProjectionLayout.swift b/Sources/Surfaces/SurfacePaneFactory+ProjectionLayout.swift index 47b7c0c62c00..cd9f3e80318c 100644 --- a/Sources/Surfaces/SurfacePaneFactory+ProjectionLayout.swift +++ b/Sources/Surfaces/SurfacePaneFactory+ProjectionLayout.swift @@ -1,5 +1,6 @@ import AppKit import Bonsplit +import CmuxSurfaceCatalogModel import Foundation extension SurfacePaneFactory { diff --git a/Sources/Surfaces/SurfacePaneFactory.swift b/Sources/Surfaces/SurfacePaneFactory.swift index 8c81ae3915db..8bd4f827b362 100644 --- a/Sources/Surfaces/SurfacePaneFactory.swift +++ b/Sources/Surfaces/SurfacePaneFactory.swift @@ -1,5 +1,6 @@ import Bonsplit import CmuxControlSocket +import CmuxSurfaceCatalogModel import Foundation import WebKit diff --git a/Sources/Surfaces/SurfacePlacementSyncing.swift b/Sources/Surfaces/SurfacePlacementSyncing.swift index e6a61734f362..f9e317bbae5a 100644 --- a/Sources/Surfaces/SurfacePlacementSyncing.swift +++ b/Sources/Surfaces/SurfacePlacementSyncing.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// The machine-layout mutations the sync service asks a provider for. Only providers diff --git a/Sources/Surfaces/SurfaceProjection+WorkspaceMembership.swift b/Sources/Surfaces/SurfaceProjection+WorkspaceMembership.swift index b4880a034b6a..31aea6b83011 100644 --- a/Sources/Surfaces/SurfaceProjection+WorkspaceMembership.swift +++ b/Sources/Surfaces/SurfaceProjection+WorkspaceMembership.swift @@ -1,3 +1,5 @@ +import CmuxSurfaceCatalogModel + extension SurfaceProjection { /// Desktop and port previews created on this Mac have no daemon tab. var isLocalWorkspaceView: Bool { diff --git a/Sources/Surfaces/SurfaceProjectionIdentity+Workspace.swift b/Sources/Surfaces/SurfaceProjectionIdentity+Workspace.swift index 120a415ed3e9..d685a1e3db4e 100644 --- a/Sources/Surfaces/SurfaceProjectionIdentity+Workspace.swift +++ b/Sources/Surfaces/SurfaceProjectionIdentity+Workspace.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceProjectionIdentity { diff --git a/Sources/Surfaces/SurfaceProjectionIdentity.swift b/Sources/Surfaces/SurfaceProjectionIdentity.swift deleted file mode 100644 index 8c8b641cef12..000000000000 --- a/Sources/Surfaces/SurfaceProjectionIdentity.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Foundation - -/// Persisted identities of the local owners displaying a resource, captured for one read. -/// Cloud resource identity and runtime projection selectors retain their existing meanings. -struct SurfaceProjectionIdentity: Hashable, Sendable { - let stableSurfaceID: UUID - let stableWorkspaceID: UUID - -} diff --git a/Sources/Surfaces/SurfaceProjectionMaterialization+Key.swift b/Sources/Surfaces/SurfaceProjectionMaterialization+Key.swift index 9d33ed429650..5f277690adea 100644 --- a/Sources/Surfaces/SurfaceProjectionMaterialization+Key.swift +++ b/Sources/Surfaces/SurfaceProjectionMaterialization+Key.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceProjectionMaterialization { diff --git a/Sources/Surfaces/SurfaceProjectionMaterialization.swift b/Sources/Surfaces/SurfaceProjectionMaterialization.swift index 53d4a4452cae..e025c6ff368d 100644 --- a/Sources/Surfaces/SurfaceProjectionMaterialization.swift +++ b/Sources/Surfaces/SurfaceProjectionMaterialization.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// One provider operation that is currently creating a projection for a resource. diff --git a/Sources/Surfaces/SurfaceProjectionRestoreStore.swift b/Sources/Surfaces/SurfaceProjectionRestoreStore.swift index 6006fd4e4a05..57c9c9e92646 100644 --- a/Sources/Surfaces/SurfaceProjectionRestoreStore.swift +++ b/Sources/Surfaces/SurfaceProjectionRestoreStore.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Owns remote projections that were restored before their provider published a resource. diff --git a/Sources/Surfaces/SurfaceProvider+MaterializationValidation.swift b/Sources/Surfaces/SurfaceProvider+MaterializationValidation.swift index d144674e5bfa..cd54995393d6 100644 --- a/Sources/Surfaces/SurfaceProvider+MaterializationValidation.swift +++ b/Sources/Surfaces/SurfaceProvider+MaterializationValidation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceProvider { diff --git a/Sources/Surfaces/SurfaceProvider.swift b/Sources/Surfaces/SurfaceProvider.swift index 8e7b42781f14..677552b6181d 100644 --- a/Sources/Surfaces/SurfaceProvider.swift +++ b/Sources/Surfaces/SurfaceProvider.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// A provider owns the resources of one machine and knows how to put one on screen. diff --git a/Sources/Surfaces/SurfaceRemotePlacement.swift b/Sources/Surfaces/SurfaceRemotePlacement.swift index c2ccaeee2eb9..935639c8a5fa 100644 --- a/Sources/Surfaces/SurfaceRemotePlacement.swift +++ b/Sources/Surfaces/SurfaceRemotePlacement.swift @@ -1,3 +1,5 @@ +import CmuxSurfaceCatalogModel + /// The confirmed daemon tab behind a local pane. It is independent of local geometry. struct SurfaceRemotePlacement: Equatable, Sendable { let workspaceID: String diff --git a/Sources/Surfaces/SurfaceResource+AgentIcon.swift b/Sources/Surfaces/SurfaceResource+AgentIcon.swift index cbeb35d08c98..2f75a86d3197 100644 --- a/Sources/Surfaces/SurfaceResource+AgentIcon.swift +++ b/Sources/Surfaces/SurfaceResource+AgentIcon.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension SurfaceResource { diff --git a/Sources/Surfaces/SurfaceResource+CloudTitle.swift b/Sources/Surfaces/SurfaceResource+CloudTitle.swift index 59fca3297fd5..d8150f406c9c 100644 --- a/Sources/Surfaces/SurfaceResource+CloudTitle.swift +++ b/Sources/Surfaces/SurfaceResource+CloudTitle.swift @@ -1,4 +1,5 @@ import CmuxCore +import CmuxSurfaceCatalogModel import Foundation extension SurfaceResource { diff --git a/Sources/Surfaces/SurfaceSocketCommands.swift b/Sources/Surfaces/SurfaceSocketCommands.swift index 8ff641fae823..1aca04022fee 100644 --- a/Sources/Surfaces/SurfaceSocketCommands.swift +++ b/Sources/Surfaces/SurfaceSocketCommands.swift @@ -1,5 +1,6 @@ import CmuxControlSocket import CmuxSettings +import CmuxSurfaceCatalogModel import Foundation // The socket face of the surface catalog: `surface.catalog`, `surface.project`, @@ -1375,11 +1376,3 @@ extension TerminalController { return array.compactMap { surfaceString($0) } } } - -extension SurfaceResourceID { - /// The key every provider uses for a machine's one VNC display (T10 makes this a list). - static let desktopDisplayKey = "display:1" - - /// The key for the browser that shows a forwarded HTTP port. - static func portKey(_ port: Int) -> String { "port:\(port)" } -} diff --git a/Sources/Surfaces/SurfaceWorkspaceCreationReceipt.swift b/Sources/Surfaces/SurfaceWorkspaceCreationReceipt.swift index 3d770146a05f..ea369d1aa882 100644 --- a/Sources/Surfaces/SurfaceWorkspaceCreationReceipt.swift +++ b/Sources/Surfaces/SurfaceWorkspaceCreationReceipt.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// A committed workspace and its optional starter, available before a graph refresh. diff --git a/Sources/Surfaces/TerminalController+CloudTerminalCreation.swift b/Sources/Surfaces/TerminalController+CloudTerminalCreation.swift index 0bfea06f8df4..2f22003e114f 100644 --- a/Sources/Surfaces/TerminalController+CloudTerminalCreation.swift +++ b/Sources/Surfaces/TerminalController+CloudTerminalCreation.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Keeps a socket create and its requested native projection in one local intent. diff --git a/Sources/Surfaces/TerminalController+CurrentWork.swift b/Sources/Surfaces/TerminalController+CurrentWork.swift index a5f71169a13a..8bc63d1dd19e 100644 --- a/Sources/Surfaces/TerminalController+CurrentWork.swift +++ b/Sources/Surfaces/TerminalController+CurrentWork.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension TerminalController { diff --git a/Sources/Surfaces/Workspace+BrowserDuplication.swift b/Sources/Surfaces/Workspace+BrowserDuplication.swift index 5801bbb0b35a..ad88fbd040ee 100644 --- a/Sources/Surfaces/Workspace+BrowserDuplication.swift +++ b/Sources/Surfaces/Workspace+BrowserDuplication.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// Browser view duplication retains resource provenance before navigation. diff --git a/Sources/Surfaces/Workspace+CloudDisplayOwnership.swift b/Sources/Surfaces/Workspace+CloudDisplayOwnership.swift index 694419121bbe..83253c3b0782 100644 --- a/Sources/Surfaces/Workspace+CloudDisplayOwnership.swift +++ b/Sources/Surfaces/Workspace+CloudDisplayOwnership.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension Workspace { diff --git a/Sources/Surfaces/Workspace+CloudDragSplitRouting.swift b/Sources/Surfaces/Workspace+CloudDragSplitRouting.swift index a9b89d4fb181..3d14fff27f57 100644 --- a/Sources/Surfaces/Workspace+CloudDragSplitRouting.swift +++ b/Sources/Surfaces/Workspace+CloudDragSplitRouting.swift @@ -1,4 +1,5 @@ import Bonsplit +import CmuxSurfaceCatalogModel import CmuxWorkspaces import Foundation import GhosttyKit diff --git a/Sources/Surfaces/Workspace+CloudLayoutProjection.swift b/Sources/Surfaces/Workspace+CloudLayoutProjection.swift index de3fb9de56f9..6274168b5065 100644 --- a/Sources/Surfaces/Workspace+CloudLayoutProjection.swift +++ b/Sources/Surfaces/Workspace+CloudLayoutProjection.swift @@ -1,4 +1,5 @@ import Bonsplit +import CmuxSurfaceCatalogModel import Foundation @MainActor diff --git a/Sources/Surfaces/Workspace+CloudMachineLoading.swift b/Sources/Surfaces/Workspace+CloudMachineLoading.swift index f766dd3a0e3c..d8d880444fc4 100644 --- a/Sources/Surfaces/Workspace+CloudMachineLoading.swift +++ b/Sources/Surfaces/Workspace+CloudMachineLoading.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import CmuxWorkspaces diff --git a/Sources/Surfaces/Workspace+CloudManualMirror.swift b/Sources/Surfaces/Workspace+CloudManualMirror.swift index 3f9ed7d431c7..847a45764a30 100644 --- a/Sources/Surfaces/Workspace+CloudManualMirror.swift +++ b/Sources/Surfaces/Workspace+CloudManualMirror.swift @@ -1,5 +1,6 @@ import AppKit import Bonsplit +import CmuxSurfaceCatalogModel import CmuxTerminal import CmuxWorkspaces import Foundation diff --git a/Sources/Surfaces/Workspace+CloudPaneRouting.swift b/Sources/Surfaces/Workspace+CloudPaneRouting.swift index 4804fc3cb1f6..33694cc0f6a7 100644 --- a/Sources/Surfaces/Workspace+CloudPaneRouting.swift +++ b/Sources/Surfaces/Workspace+CloudPaneRouting.swift @@ -1,5 +1,6 @@ import AppKit import Bonsplit +import CmuxSurfaceCatalogModel import Foundation /// Identifies one remote workspace placement for a local projection. diff --git a/Sources/Surfaces/Workspace+CloudPlacementFailure.swift b/Sources/Surfaces/Workspace+CloudPlacementFailure.swift index 4d418a14f299..45c526027703 100644 --- a/Sources/Surfaces/Workspace+CloudPlacementFailure.swift +++ b/Sources/Surfaces/Workspace+CloudPlacementFailure.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation extension Workspace { diff --git a/Sources/Surfaces/Workspace+CloudTerminalCreation.swift b/Sources/Surfaces/Workspace+CloudTerminalCreation.swift index a0f4e6b90160..8f2500a9dbec 100644 --- a/Sources/Surfaces/Workspace+CloudTerminalCreation.swift +++ b/Sources/Surfaces/Workspace+CloudTerminalCreation.swift @@ -1,5 +1,6 @@ import AppKit import Bonsplit +import CmuxSurfaceCatalogModel import CmuxWorkspaces import Foundation diff --git a/Sources/Surfaces/Workspace+CloudTerminalReservation.swift b/Sources/Surfaces/Workspace+CloudTerminalReservation.swift index ec8d0f52c648..ec8742b0ec51 100644 --- a/Sources/Surfaces/Workspace+CloudTerminalReservation.swift +++ b/Sources/Surfaces/Workspace+CloudTerminalReservation.swift @@ -1,5 +1,6 @@ import Bonsplit import CmuxRemoteSession +import CmuxSurfaceCatalogModel import CmuxTerminal import Foundation diff --git a/Sources/Surfaces/Workspace+SurfaceCatalog.swift b/Sources/Surfaces/Workspace+SurfaceCatalog.swift index 79a9a73930bb..e5dd692c3c01 100644 --- a/Sources/Surfaces/Workspace+SurfaceCatalog.swift +++ b/Sources/Surfaces/Workspace+SurfaceCatalog.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation /// The workspace's side of the surface catalog: the single seams through which a pane diff --git a/Sources/Surfaces/Workspace+SurfaceOwnership.swift b/Sources/Surfaces/Workspace+SurfaceOwnership.swift index e0ab90eb19e8..bfc2d915f998 100644 --- a/Sources/Surfaces/Workspace+SurfaceOwnership.swift +++ b/Sources/Surfaces/Workspace+SurfaceOwnership.swift @@ -1,5 +1,6 @@ import AppKit import Bonsplit +import CmuxSurfaceCatalogModel import Foundation extension Workspace { diff --git a/Sources/TerminalController.swift b/Sources/TerminalController.swift index 818fd99a7cd8..fa22c16872da 100644 --- a/Sources/TerminalController.swift +++ b/Sources/TerminalController.swift @@ -10,6 +10,7 @@ import CmuxFoundation import CmuxPanes import CmuxRemoteDaemon import CmuxRemoteWorkspace +import CmuxSurfaceCatalogModel import CmuxTerminal import CmuxSettings import CmuxSwiftRenderUI diff --git a/Sources/Workspace+DetachedSurfaceTransfer.swift b/Sources/Workspace+DetachedSurfaceTransfer.swift index c01dda9fe585..f9a7c2d87cfd 100644 --- a/Sources/Workspace+DetachedSurfaceTransfer.swift +++ b/Sources/Workspace+DetachedSurfaceTransfer.swift @@ -1,4 +1,5 @@ import CmuxFoundation +import CmuxSurfaceCatalogModel import Foundation import CmuxCore import CmuxWorkspaces diff --git a/Sources/Workspace+ForkConversationContextMenu.swift b/Sources/Workspace+ForkConversationContextMenu.swift index 3bcc50cdc684..719edad1af39 100644 --- a/Sources/Workspace+ForkConversationContextMenu.swift +++ b/Sources/Workspace+ForkConversationContextMenu.swift @@ -1,6 +1,7 @@ import Bonsplit import CmuxCore import CmuxSettings +import CmuxSurfaceCatalogModel import Foundation extension Workspace { diff --git a/Sources/Workspace+TerminalLinkOpening.swift b/Sources/Workspace+TerminalLinkOpening.swift index 6d0bd93e5b18..ee1632d03f14 100644 --- a/Sources/Workspace+TerminalLinkOpening.swift +++ b/Sources/Workspace+TerminalLinkOpening.swift @@ -1,5 +1,6 @@ import CmuxCore import CmuxPanes +import CmuxSurfaceCatalogModel import Foundation extension Workspace: TerminalLinkOpenContainer { diff --git a/Sources/Workspace.swift b/Sources/Workspace.swift index 7bd4a3f6d68a..7c5fee61d710 100644 --- a/Sources/Workspace.swift +++ b/Sources/Workspace.swift @@ -1,6 +1,7 @@ import CmuxAppKitSupportUI import CMUXMobileCore import CmuxFoundation +import CmuxSurfaceCatalogModel import Foundation import CmuxCore import CmuxRemoteDaemon diff --git a/Sources/WorkspaceCloudBindingState.swift b/Sources/WorkspaceCloudBindingState.swift index 97b88cc60e20..73df62096d0b 100644 --- a/Sources/WorkspaceCloudBindingState.swift +++ b/Sources/WorkspaceCloudBindingState.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Observation diff --git a/Sources/WorkspaceSurfaceResourceDrop.swift b/Sources/WorkspaceSurfaceResourceDrop.swift index 8dd75262e25f..d0d238bc929f 100644 --- a/Sources/WorkspaceSurfaceResourceDrop.swift +++ b/Sources/WorkspaceSurfaceResourceDrop.swift @@ -1,4 +1,5 @@ import Bonsplit +import CmuxSurfaceCatalogModel import Foundation extension SurfaceDestination { diff --git a/cmux.xcodeproj/project.pbxproj b/cmux.xcodeproj/project.pbxproj index b33ce20c51fe..054f1542803e 100644 --- a/cmux.xcodeproj/project.pbxproj +++ b/cmux.xcodeproj/project.pbxproj @@ -925,8 +925,6 @@ 09FBC463786149D49E73B297 /* CloudSurfaceDropGateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A23E881DEAE40649BC8FFCF /* CloudSurfaceDropGateView.swift */; }; 5CB4AC1D92064236B5C2DDF1 /* CloudSurfaceMoveOwnershipTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF212BA811D54BB986A5E27F /* CloudSurfaceMoveOwnershipTests.swift */; }; 6D2669CD5E4944C2B8C0F49C /* CloudSurfaceOwnershipTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DAE66C6AF74046B5545B47 /* CloudSurfaceOwnershipTests.swift */; }; - E2B852A520D64882866D700C /* CloudTabNameAuthority.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD1C27A86AE4440C848A1B61 /* CloudTabNameAuthority.swift */; }; - 1B425E2EA8D5475B94ED1BC1 /* CloudTabNameSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF9BB61CBFC4479997B23A3A /* CloudTabNameSource.swift */; }; 13019C000000000000000002 /* CloudTeamScopeObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13019C000000000000000001 /* CloudTeamScopeObserver.swift */; }; CCBC23BCB453E9A38F16DB2D /* CloudTelemetryClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF35B9235AD817EE95281459 /* CloudTelemetryClient.swift */; }; A38052AB06B7E0B46A215689 /* CloudTelemetrySending.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EBCFB7E8CD8163F74525C53 /* CloudTelemetrySending.swift */; }; @@ -1072,7 +1070,6 @@ C11324430000000000000001 /* CloudTuiRemoteColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11324440000000000000001 /* CloudTuiRemoteColors.swift */; }; C11324710000000000000001 /* CloudTuiResolvedSurface.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11324720000000000000001 /* CloudTuiResolvedSurface.swift */; }; C11324910000000000000001 /* CloudTuiSurfaceIDResolution.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11324920000000000000001 /* CloudTuiSurfaceIDResolution.swift */; }; - C11325A10000000000000001 /* CloudTuiTerminalProjectionTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11325A20000000000000001 /* CloudTuiTerminalProjectionTarget.swift */; }; 0F2A151D749994FC57D403CA /* CloudTunnelActivationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E9D51274EC32D701633288E /* CloudTunnelActivationObserver.swift */; }; 430BEAB3F1329807647B9796 /* CloudTunnelAdmission.swift in Sources */ = {isa = PBXBuildFile; fileRef = F92F1CACC68CA3F67E73A822 /* CloudTunnelAdmission.swift */; }; 7A0CE1000000000000000308 /* CloudTunnelAppConsumers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0CE1000000000000000307 /* CloudTunnelAppConsumers.swift */; }; @@ -1121,9 +1118,7 @@ C7F29A7CD7C6410F87BB8536 /* CloudVMLoadingPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0581DFBCDBEE482A8D4CDB6C /* CloudVMLoadingPanelView.swift */; }; C75740010000000000000001 /* CloudVMMenuItemMetricsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75740010000000000000002 /* CloudVMMenuItemMetricsTests.swift */; }; 800100000000000000000304 /* CloudVMPanelAuthState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 800100000000000000000303 /* CloudVMPanelAuthState.swift */; }; - 2F48EE37A14B4D76A8013B83 /* CloudVMState+SnapshotComparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18D73C012B643DBADE8B7B5 /* CloudVMState+SnapshotComparison.swift */; }; 2D7F228001C24ABD865AFD66 /* CloudVMStateSnapshotComparisonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EFB9C9E7B5D4F39873871A1 /* CloudVMStateSnapshotComparisonTests.swift */; }; - E34036E08961479A9D9BF649 /* CloudVMTabState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 024F0948012A442588DAAB27 /* CloudVMTabState.swift */; }; C12093000000000000000002 /* CloudVPNExplicitActivationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C12093000000000000000001 /* CloudVPNExplicitActivationTests.swift */; }; 319338AE832D4D34BCA1B995 /* CloudWireGuardHub+Production.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32EF99FEA7414C6589A1DFF2 /* CloudWireGuardHub+Production.swift */; }; 999B711473384AA9593B1936 /* CloudWireGuardHub.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F40FDCEAFAA24EB9A175A2 /* CloudWireGuardHub.swift */; }; @@ -1512,6 +1507,9 @@ D96970000000000000000003 /* CmuxSudoBroker in Frameworks */ = {isa = PBXBuildFile; productRef = D96973000000000000000001 /* CmuxSudoBroker */; }; D96970000000000000000004 /* CmuxSudoBrokerUI in Frameworks */ = {isa = PBXBuildFile; productRef = D96973000000000000000002 /* CmuxSudoBrokerUI */; }; D96970000000000000000005 /* CmuxSudoBrokerUI in Frameworks */ = {isa = PBXBuildFile; productRef = D96973000000000000000002 /* CmuxSudoBrokerUI */; }; + 5CA7A1060000000000000004 /* CmuxSurfaceCatalogModel in Frameworks */ = {isa = PBXBuildFile; productRef = 5CA7A1060000000000000003 /* CmuxSurfaceCatalogModel */; }; + 5CA7A1060000000000000005 /* CmuxSurfaceCatalogModel in Frameworks */ = {isa = PBXBuildFile; productRef = 5CA7A1060000000000000002 /* CmuxSurfaceCatalogModel */; }; + 5CA7A1060000000000000006 /* CmuxSurfaceCatalogModel in Frameworks */ = {isa = PBXBuildFile; productRef = 5CA7A1060000000000000002 /* CmuxSurfaceCatalogModel */; }; C0DE43000000000000000003 /* CmuxSurfaceTabBarBuiltInAction+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DE43000000000000000004 /* CmuxSurfaceTabBarBuiltInAction+Codable.swift */; }; C10D00030000000000000003 /* CmuxSurfaceTabBarBuiltInAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10D00040000000000000004 /* CmuxSurfaceTabBarBuiltInAction.swift */; }; C5A1FED000000000000000C1 /* CmuxSwiftRender in Frameworks */ = {isa = PBXBuildFile; productRef = C5A1FED000000000000000C3 /* CmuxSwiftRender */; }; @@ -1558,7 +1556,6 @@ D9F800000000000000000003 /* CmuxTuiRemoteRouting.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9F800000000000000000002 /* CmuxTuiRemoteRouting.swift */; }; 3CBCDC9381A949A7A6A239D0 /* CmuxTuiSnapshotParser+Displays.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B54F1BEB0C84A15BAA2A261 /* CmuxTuiSnapshotParser+Displays.swift */; }; 622E567166E1DC2C614E4E7F /* CmuxTuiSnapshotParser+Placement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3349CDE9CB71FA353EA004D3 /* CmuxTuiSnapshotParser+Placement.swift */; }; - 3B2BDBC03043E0C3CC4A308E /* CmuxTuiSnapshotParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C28D6AEDE6B00F5C633ED2A8 /* CmuxTuiSnapshotParser.swift */; }; 3312CA2585324CF9B4CA9D9A /* CmuxTuiSurfaceProvider+AgentNaming.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3528E32A29B49E99C636F0B /* CmuxTuiSurfaceProvider+AgentNaming.swift */; }; 81A1621623F83B61D4C5FFA9 /* CmuxTuiSurfaceProvider+AttachmentRecovery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D8BE7D3234A1F44E2A1C52B /* CmuxTuiSurfaceProvider+AttachmentRecovery.swift */; }; 5633045E23E44075B22C08CA /* CmuxTuiSurfaceProvider+CloseTerminal.swift in Sources */ = {isa = PBXBuildFile; fileRef = E655249E82FC412B9EC2CD21 /* CmuxTuiSurfaceProvider+CloseTerminal.swift */; }; @@ -3378,17 +3375,12 @@ C12575000000000000000036 /* SurfaceCatalog+WorkspaceDeletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C12575000000000000000037 /* SurfaceCatalog+WorkspaceDeletion.swift */; }; 00B9FC4EDB2E47F6819B01C4 /* SurfaceCatalog+WorkspaceMembership.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B8876F3CE1A40FB83FF5463 /* SurfaceCatalog+WorkspaceMembership.swift */; }; F7F3BED81BD5FE96D52055C0 /* SurfaceCatalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA63D377742D3A6A0BAB499B /* SurfaceCatalog.swift */; }; - 31B62CAEA63AA5B0F737F227 /* SurfaceCatalogModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66CA02CE5FE3E9DC871AD810 /* SurfaceCatalogModel.swift */; }; D01100800000000000000002 /* SurfaceCatalogQueryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01100800000000000000001 /* SurfaceCatalogQueryService.swift */; }; D01100800000000000000004 /* SurfaceCatalogQueryServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01100800000000000000003 /* SurfaceCatalogQueryServiceTests.swift */; }; D22034B96FFC2D5F31B4311B /* SurfaceCatalogSnapshot+DeviceVisibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E14A2CEDB3FA23862A733F6 /* SurfaceCatalogSnapshot+DeviceVisibility.swift */; }; - 201889FAA2BD4E1CB5094958 /* SurfaceCatalogSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520449EF5FBA4DCE8423DA80 /* SurfaceCatalogSnapshot.swift */; }; 5873A6BE37C34CC1082864E2 /* SurfaceCatalogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 877BC9A2B697FEF2957397FA /* SurfaceCatalogTests.swift */; }; - DEC1CE00000000000000003A /* SurfaceDeviceInstanceID.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC1CE000000000000000039 /* SurfaceDeviceInstanceID.swift */; }; - DEC1CE00000000000000003C /* SurfaceDevicePresence.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC1CE00000000000000003B /* SurfaceDevicePresence.swift */; }; 32A5901C769249B692FA1104 /* SurfaceDropFeedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = E192919EFE65466C84C0DD68 /* SurfaceDropFeedback.swift */; }; F26476EEB1E440498FD2A4F5 /* SurfaceLayoutTerminalCreating.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672C19994B1744318706972D /* SurfaceLayoutTerminalCreating.swift */; }; - DB9B5DF2EB6F87829782A6E5 /* SurfaceMachineID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90C987FF6EE94B4E842EC733 /* SurfaceMachineID.swift */; }; DEC1CE00000000000000003E /* SurfaceMachineIDDeviceEncodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC1CE00000000000000003D /* SurfaceMachineIDDeviceEncodingTests.swift */; }; 917D12A30EB4417BBE5E9E6A /* SurfaceOwnershipPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7A47B5093E24AAD8DE28313 /* SurfaceOwnershipPolicy.swift */; }; C11323110000000000000001 /* SurfacePaneFactory+CloudManualMirror.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11323120000000000000001 /* SurfacePaneFactory+CloudManualMirror.swift */; }; @@ -3401,7 +3393,6 @@ 027446BA1E1B4E69B2DCDF8A /* SurfaceProjection+WorkspaceMembership.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DF4946E3C7A4AEEBD2E8933 /* SurfaceProjection+WorkspaceMembership.swift */; }; 0F4B1E526E23283B5CA1FAAA /* SurfaceProjectionEndReason.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA8DF56A13E861D787CA4AE /* SurfaceProjectionEndReason.swift */; }; D01324400000000000000006 /* SurfaceProjectionIdentity+Workspace.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01324400000000000000005 /* SurfaceProjectionIdentity+Workspace.swift */; }; - D01324400000000000000002 /* SurfaceProjectionIdentity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01324400000000000000001 /* SurfaceProjectionIdentity.swift */; }; D01324400000000000000004 /* SurfaceProjectionIdentityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01324400000000000000003 /* SurfaceProjectionIdentityTests.swift */; }; A9C10D000000000000000003 /* SurfaceProjectionMaterialization+Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C10D000000000000000004 /* SurfaceProjectionMaterialization+Key.swift */; }; A9C10D000000000000000002 /* SurfaceProjectionMaterialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C10D000000000000000001 /* SurfaceProjectionMaterialization.swift */; }; @@ -3416,7 +3407,6 @@ F96CAE8A14774122A5F8613B /* SurfaceResourceDragPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48A1307BCCDB493F49179E0B /* SurfaceResourceDragPayload.swift */; }; 3E66E7A1B2B320EC850BB174 /* SurfaceResourceDragRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 627B5FB396A1BF22407E8B45 /* SurfaceResourceDragRegistry.swift */; }; 12D94A2FDF834F9495F22AEE /* SurfaceResourceGroup+CloudNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EBE986DDD446A694D6EA07 /* SurfaceResourceGroup+CloudNavigation.swift */; }; - 891B4C987DA84C9B975AD832 /* SurfaceResourcePlacement.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65E70E1AE6E42F890ADD7EF /* SurfaceResourcePlacement.swift */; }; 842300000000000000000007 /* SurfaceResumeAgentBindingGenerationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842300000000000000000008 /* SurfaceResumeAgentBindingGenerationTests.swift */; }; F6572032A1B2C3D4E5F60718 /* SurfaceResumeAgentHookDowngradeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6572033A1B2C3D4E5F60718 /* SurfaceResumeAgentHookDowngradeTests.swift */; }; 883700000000000000000003 /* SurfaceResumeApprovalLookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 883700000000000000000004 /* SurfaceResumeApprovalLookup.swift */; }; @@ -3952,8 +3942,6 @@ 93AF7E8EC956FE883E252322 /* VMClientTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 831145877779546B628FCAFC /* VMClientTelemetry.swift */; }; 6EE115A36CAD9082FBC4593E /* VMClientTelemetryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F8A0358C8DD0A7D998A86E9 /* VMClientTelemetryTests.swift */; }; C37800000000000000000003 /* VMDefaultCloudCommandTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C37800000000000000000004 /* VMDefaultCloudCommandTests.swift */; }; - DCFD2C3A4F04C00C62720EE5 /* VMMachineKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EECEC1A2D3CFC13D4343F46 /* VMMachineKind.swift */; }; - F1C699902D31503EE3D492DF /* VMMachineKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EECEC1A2D3CFC13D4343F46 /* VMMachineKind.swift */; }; B81607322100000000000002 /* VMMachineTerminalResolution.swift in Sources */ = {isa = PBXBuildFile; fileRef = B81607322100000000000004 /* VMMachineTerminalResolution.swift */; }; B81607322100000000000003 /* VMMachineTerminalResolution.swift in Sources */ = {isa = PBXBuildFile; fileRef = B81607322100000000000004 /* VMMachineTerminalResolution.swift */; }; CADD34A6A84246019F1EB5DA /* VMReflectionResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DB85E9217B428E8E41F463 /* VMReflectionResult.swift */; }; @@ -5263,8 +5251,6 @@ 3A23E881DEAE40649BC8FFCF /* CloudSurfaceDropGateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudSurfaceDropGateView.swift; sourceTree = ""; }; DF212BA811D54BB986A5E27F /* CloudSurfaceMoveOwnershipTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudSurfaceMoveOwnershipTests.swift; sourceTree = ""; }; D4DAE66C6AF74046B5545B47 /* CloudSurfaceOwnershipTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudSurfaceOwnershipTests.swift; sourceTree = ""; }; - BD1C27A86AE4440C848A1B61 /* CloudTabNameAuthority.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CloudTabNameAuthority.swift"; sourceTree = ""; }; - AF9BB61CBFC4479997B23A3A /* CloudTabNameSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CloudTabNameSource.swift"; sourceTree = ""; }; 13019C000000000000000001 /* CloudTeamScopeObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CloudTeamScopeObserver.swift"; sourceTree = ""; }; FF35B9235AD817EE95281459 /* CloudTelemetryClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CloudTelemetryClient.swift"; sourceTree = ""; }; 3EBCFB7E8CD8163F74525C53 /* CloudTelemetrySending.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CloudTelemetrySending.swift"; sourceTree = ""; }; @@ -5410,7 +5396,6 @@ C11324440000000000000001 /* CloudTuiRemoteColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiRemoteColors.swift; sourceTree = ""; }; C11324720000000000000001 /* CloudTuiResolvedSurface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiResolvedSurface.swift; sourceTree = ""; }; C11324920000000000000001 /* CloudTuiSurfaceIDResolution.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiSurfaceIDResolution.swift; sourceTree = ""; }; - C11325A20000000000000001 /* CloudTuiTerminalProjectionTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiTerminalProjectionTarget.swift; sourceTree = ""; }; 8E9D51274EC32D701633288E /* CloudTunnelActivationObserver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudTunnelActivationObserver.swift; sourceTree = ""; }; F92F1CACC68CA3F67E73A822 /* CloudTunnelAdmission.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudTunnelAdmission.swift; sourceTree = ""; }; 7A0CE1000000000000000307 /* CloudTunnelAppConsumers.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudTunnelAppConsumers.swift; sourceTree = ""; }; @@ -5456,9 +5441,7 @@ 0581DFBCDBEE482A8D4CDB6C /* CloudVMLoadingPanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Panels/CloudVMLoadingPanelView.swift; sourceTree = ""; }; C75740010000000000000002 /* CloudVMMenuItemMetricsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudVMMenuItemMetricsTests.swift; sourceTree = ""; }; 800100000000000000000303 /* CloudVMPanelAuthState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudVMPanelAuthState.swift; sourceTree = ""; }; - F18D73C012B643DBADE8B7B5 /* CloudVMState+SnapshotComparison.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CloudVMState+SnapshotComparison.swift"; sourceTree = ""; }; 7EFB9C9E7B5D4F39873871A1 /* CloudVMStateSnapshotComparisonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudVMStateSnapshotComparisonTests.swift; sourceTree = ""; }; - 024F0948012A442588DAAB27 /* CloudVMTabState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CloudVMTabState.swift"; sourceTree = ""; }; C12093000000000000000001 /* CloudVPNExplicitActivationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudVPNExplicitActivationTests.swift; sourceTree = ""; }; 32EF99FEA7414C6589A1DFF2 /* CloudWireGuardHub+Production.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "CloudWireGuardHub+Production.swift"; sourceTree = ""; }; D8F40FDCEAFAA24EB9A175A2 /* CloudWireGuardHub.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudWireGuardHub.swift; sourceTree = ""; }; @@ -5791,7 +5774,6 @@ D9F800000000000000000002 /* CmuxTuiRemoteRouting.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CmuxTuiRemoteRouting.swift; sourceTree = ""; }; 1B54F1BEB0C84A15BAA2A261 /* CmuxTuiSnapshotParser+Displays.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CmuxTuiSnapshotParser+Displays.swift"; sourceTree = ""; }; 3349CDE9CB71FA353EA004D3 /* CmuxTuiSnapshotParser+Placement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CmuxTuiSnapshotParser+Placement.swift"; sourceTree = ""; }; - C28D6AEDE6B00F5C633ED2A8 /* CmuxTuiSnapshotParser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CmuxTuiSnapshotParser.swift; sourceTree = ""; }; E3528E32A29B49E99C636F0B /* CmuxTuiSurfaceProvider+AgentNaming.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CmuxTuiSurfaceProvider+AgentNaming.swift"; sourceTree = ""; }; 4D8BE7D3234A1F44E2A1C52B /* CmuxTuiSurfaceProvider+AttachmentRecovery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CmuxTuiSurfaceProvider+AttachmentRecovery.swift"; sourceTree = ""; }; E655249E82FC412B9EC2CD21 /* CmuxTuiSurfaceProvider+CloseTerminal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CmuxTuiSurfaceProvider+CloseTerminal.swift"; sourceTree = ""; }; @@ -7584,17 +7566,12 @@ C12575000000000000000037 /* SurfaceCatalog+WorkspaceDeletion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SurfaceCatalog+WorkspaceDeletion.swift"; sourceTree = ""; }; 0B8876F3CE1A40FB83FF5463 /* SurfaceCatalog+WorkspaceMembership.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "SurfaceCatalog+WorkspaceMembership.swift"; sourceTree = ""; }; EA63D377742D3A6A0BAB499B /* SurfaceCatalog.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SurfaceCatalog.swift; sourceTree = ""; }; - 66CA02CE5FE3E9DC871AD810 /* SurfaceCatalogModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SurfaceCatalogModel.swift; sourceTree = ""; }; D01100800000000000000001 /* SurfaceCatalogQueryService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceCatalogQueryService.swift; sourceTree = ""; }; D01100800000000000000003 /* SurfaceCatalogQueryServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceCatalogQueryServiceTests.swift; sourceTree = ""; }; 3E14A2CEDB3FA23862A733F6 /* SurfaceCatalogSnapshot+DeviceVisibility.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "Sources/Surfaces/SurfaceCatalogSnapshot+DeviceVisibility.swift"; sourceTree = ""; }; - 520449EF5FBA4DCE8423DA80 /* SurfaceCatalogSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SurfaceCatalogSnapshot.swift"; sourceTree = ""; }; 877BC9A2B697FEF2957397FA /* SurfaceCatalogTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SurfaceCatalogTests.swift; sourceTree = ""; }; - DEC1CE000000000000000039 /* SurfaceDeviceInstanceID.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SurfaceDeviceInstanceID.swift; sourceTree = ""; }; - DEC1CE00000000000000003B /* SurfaceDevicePresence.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SurfaceDevicePresence.swift; sourceTree = ""; }; E192919EFE65466C84C0DD68 /* SurfaceDropFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceDropFeedback.swift; sourceTree = ""; }; 672C19994B1744318706972D /* SurfaceLayoutTerminalCreating.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SurfaceLayoutTerminalCreating.swift"; sourceTree = ""; }; - 90C987FF6EE94B4E842EC733 /* SurfaceMachineID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Surfaces/SurfaceMachineID.swift"; sourceTree = ""; }; DEC1CE00000000000000003D /* SurfaceMachineIDDeviceEncodingTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SurfaceMachineIDDeviceEncodingTests.swift; sourceTree = ""; }; B7A47B5093E24AAD8DE28313 /* SurfaceOwnershipPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceOwnershipPolicy.swift; sourceTree = ""; }; C11323120000000000000001 /* SurfacePaneFactory+CloudManualMirror.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SurfacePaneFactory+CloudManualMirror.swift"; sourceTree = ""; }; @@ -7607,7 +7584,6 @@ 7DF4946E3C7A4AEEBD2E8933 /* SurfaceProjection+WorkspaceMembership.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "SurfaceProjection+WorkspaceMembership.swift"; sourceTree = ""; }; ECA8DF56A13E861D787CA4AE /* SurfaceProjectionEndReason.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SurfaceProjectionEndReason.swift"; sourceTree = ""; }; D01324400000000000000005 /* SurfaceProjectionIdentity+Workspace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SurfaceProjectionIdentity+Workspace.swift"; sourceTree = ""; }; - D01324400000000000000001 /* SurfaceProjectionIdentity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceProjectionIdentity.swift; sourceTree = ""; }; D01324400000000000000003 /* SurfaceProjectionIdentityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceProjectionIdentityTests.swift; sourceTree = ""; }; A9C10D000000000000000004 /* SurfaceProjectionMaterialization+Key.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "SurfaceProjectionMaterialization+Key.swift"; sourceTree = ""; }; A9C10D000000000000000001 /* SurfaceProjectionMaterialization.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SurfaceProjectionMaterialization.swift; sourceTree = ""; }; @@ -7622,7 +7598,6 @@ 48A1307BCCDB493F49179E0B /* SurfaceResourceDragPayload.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SurfaceResourceDragPayload.swift; sourceTree = ""; }; 627B5FB396A1BF22407E8B45 /* SurfaceResourceDragRegistry.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SurfaceResourceDragRegistry.swift; sourceTree = ""; }; 54EBE986DDD446A694D6EA07 /* SurfaceResourceGroup+CloudNavigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SurfaceResourceGroup+CloudNavigation.swift"; sourceTree = ""; }; - D65E70E1AE6E42F890ADD7EF /* SurfaceResourcePlacement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceResourcePlacement.swift; sourceTree = ""; }; 842300000000000000000008 /* SurfaceResumeAgentBindingGenerationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceResumeAgentBindingGenerationTests.swift; sourceTree = ""; }; F6572033A1B2C3D4E5F60718 /* SurfaceResumeAgentHookDowngradeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceResumeAgentHookDowngradeTests.swift; sourceTree = ""; }; 883700000000000000000004 /* SurfaceResumeApprovalLookup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceResumeApprovalLookup.swift; sourceTree = ""; }; @@ -8158,7 +8133,6 @@ 831145877779546B628FCAFC /* VMClientTelemetry.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = VMClientTelemetry.swift; sourceTree = ""; }; 8F8A0358C8DD0A7D998A86E9 /* VMClientTelemetryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMClientTelemetryTests.swift; sourceTree = ""; }; C37800000000000000000004 /* VMDefaultCloudCommandTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMDefaultCloudCommandTests.swift; sourceTree = ""; }; - 6EECEC1A2D3CFC13D4343F46 /* VMMachineKind.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = VMMachineKind.swift; sourceTree = ""; }; B81607322100000000000004 /* VMMachineTerminalResolution.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMMachineTerminalResolution.swift; sourceTree = ""; }; 01DB85E9217B428E8E41F463 /* VMReflectionResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMReflectionResult.swift; sourceTree = ""; }; BA2691D26BF6676FF32FBC54 /* VMRemoteWorkspaceResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMRemoteWorkspaceResolver.swift; sourceTree = ""; }; @@ -8522,6 +8496,7 @@ C51A700000000000000000C3 /* CmuxSimulatorWorker in Frameworks */, D96970000000000000000001 /* CmuxSudoBroker in Frameworks */, D96970000000000000000004 /* CmuxSudoBrokerUI in Frameworks */, + 5CA7A1060000000000000005 /* CmuxSurfaceCatalogModel in Frameworks */, C5A1FED000000000000000C1 /* CmuxSwiftRender in Frameworks */, C5A1FED100000000000000D1 /* CmuxSwiftRenderUI in Frameworks */, C01E00AA00000000000000A3 /* CmuxSyntaxHighlighting in Frameworks */, @@ -8565,6 +8540,7 @@ C5A1FED200000000000000E2 /* CmuxSidebarInterpreterClient in Frameworks */, C51A700000000000000000A5 /* CmuxSimulator in Frameworks */, D96970000000000000000002 /* CmuxSudoBroker in Frameworks */, + 5CA7A1060000000000000006 /* CmuxSurfaceCatalogModel in Frameworks */, C5A1FED000000000000000C2 /* CmuxSwiftRender in Frameworks */, C5A1FED100000000000000D2 /* CmuxSwiftRenderUI in Frameworks */, C85390090000000000000001 /* CmuxTerminalCore in Frameworks */, @@ -8612,6 +8588,7 @@ C0DE4A000000000000000005 /* CmuxSidebarProviderKit in Frameworks */, D96970000000000000000003 /* CmuxSudoBroker in Frameworks */, D96970000000000000000005 /* CmuxSudoBrokerUI in Frameworks */, + 5CA7A1060000000000000004 /* CmuxSurfaceCatalogModel in Frameworks */, C01E00AA00000000000000A4 /* CmuxSyntaxHighlighting in Frameworks */, 730600040000000000000001 /* CmuxWindowing in Frameworks */, 1A0B0C0D0E0F101112132214 /* IrohLib in Frameworks */, @@ -8935,9 +8912,6 @@ B6775F6F996E44AB577BF9A5 /* SurfaceCatalog+ProjectionQueries.swift */, 019F9382B99C46AAAEB9C91C /* SurfaceAgentNaming.swift */, E3528E32A29B49E99C636F0B /* CmuxTuiSurfaceProvider+AgentNaming.swift */, - 024F0948012A442588DAAB27 /* CloudVMTabState.swift */, - BD1C27A86AE4440C848A1B61 /* CloudTabNameAuthority.swift */, - AF9BB61CBFC4479997B23A3A /* CloudTabNameSource.swift */, 70C2D3786C88493F9C30B299 /* CloudAgentNameContext.swift */, D12439060000000000000002 /* CloudWorkspaceBindingReconciliation.swift */, 1660E446B91349C08725BFFF /* CloudWorkspaceProjectionPlan.swift */, @@ -8977,9 +8951,7 @@ F11347000000000000000002 /* SurfaceCatalog+CloudPorts.swift */, A09B853868B54438A5B14F26 /* CloudProviderRefreshCoordinator.swift */, 7289F02703E9435CBA1C598B /* CmuxTuiSurfaceProvider+Refresh.swift */, - F18D73C012B643DBADE8B7B5 /* CloudVMState+SnapshotComparison.swift */, D01100800000000000000001 /* SurfaceCatalogQueryService.swift */, - D01324400000000000000001 /* SurfaceProjectionIdentity.swift */, FD26BDF22AEF02E4171683F8 /* TerminalController+CurrentWork.swift */, D6E67CB4D64F9BB44F1E5794 /* AppDelegate+CurrentWork.swift */, 9E83BB2716EBCA8C2F0A93DA /* CurrentWorkQueryService.swift */, @@ -8990,7 +8962,6 @@ 278BEFB3583AED9627966DFF /* SurfaceSocketCommands.swift */, 47BBA1A8703756FDDFD53CF3 /* Workspace+SurfaceCatalog.swift */, F3C1A7B40000000000000006 /* CloudWorkspaceRenameService+Directory.swift */, - 520449EF5FBA4DCE8423DA80 /* SurfaceCatalogSnapshot.swift */, 7B2248ED1422420F90E0D71D /* SurfaceCatalog+CloudDirectoryMetadata.swift */, C5797FE9CD70513E4FFAC6EB /* Workspace+CloudPaneRouting.swift */, 39B27028EBC44AB1A897A8D8 /* CmuxTuiSurfaceProvider+TabRename.swift */, @@ -9008,7 +8979,6 @@ 63F5BDFB110A8A96C9CA0A75 /* CmuxTuiSurfaceProviders.swift */, D9F800000000000000000002 /* CmuxTuiRemoteRouting.swift */, 1FE7223A65E72C46927A4F02 /* SurfacePaneFactory.swift */, - C28D6AEDE6B00F5C633ED2A8 /* CmuxTuiSnapshotParser.swift */, EA63D377742D3A6A0BAB499B /* SurfaceCatalog.swift */, A9B841AA21EA4B58B34D228B /* SurfaceCatalog+CloudTerminalNavigation.swift */, F11347000000000000000008 /* SurfaceCatalog+CloudBinding.swift */, @@ -9020,7 +8990,6 @@ 05CFF7107D1F48BC805549F3 /* Workspace+BrowserDuplication.swift */, 14F78AF3938A4C58A82EA25C /* Workspace+CloudDisplayOwnership.swift */, 45B5556BE218466AB7FBA814 /* SurfaceTransferRejection.swift */, - D65E70E1AE6E42F890ADD7EF /* SurfaceResourcePlacement.swift */, B7A47B5093E24AAD8DE28313 /* SurfaceOwnershipPolicy.swift */, C0CD79814B2F48938BC881A7 /* SurfaceTransferOrigin.swift */, E192919EFE65466C84C0DD68 /* SurfaceDropFeedback.swift */, @@ -9029,7 +8998,6 @@ 3A23E881DEAE40649BC8FFCF /* CloudSurfaceDropGateView.swift */, D9DF6ACE0DBF4ACE823A6CE8 /* CloudSurfaceDropGate.swift */, 113DE273128A45B791B46B00 /* AppDelegate+SurfaceOwnership.swift */, - 66CA02CE5FE3E9DC871AD810 /* SurfaceCatalogModel.swift */, A9C10D000000000000000001 /* SurfaceProjectionMaterialization.swift */, A9C10D000000000000000004 /* SurfaceProjectionMaterialization+Key.swift */, 3B78E9F9B9C233418960CB37 /* SurfacePortEndpointCache.swift */, @@ -9037,8 +9005,6 @@ 7A0CE1000000000000000727 /* CmuxTuiSurfaceProvider+PortForward.swift */, C12469AA0000000000000028 /* Workspace+CloudTerminalLoading.swift */, C12469AA0000000000000016 /* CmuxTuiSurfaceProvider+CreationRecovery.swift */, - DEC1CE000000000000000039 /* SurfaceDeviceInstanceID.swift */, - DEC1CE00000000000000003B /* SurfaceDevicePresence.swift */, EF71108A70863A2942D129D2 /* CmuxTuiSurfaceProvider+Notifications.swift */, B51835ECCEF581663B12E497 /* CmuxTuiSurfaceProvider+GuestURL.swift */, @@ -9051,7 +9017,6 @@ isa = PBXGroup; children = ( C12636A00000000000000003 /* CloudReadRecoveryObserver.swift */, - C11325A20000000000000001 /* CloudTuiTerminalProjectionTarget.swift */, C11324120000000000000001 /* CloudTuiManualIOCommand.swift */, C11324320000000000000001 /* CloudTuiManualIODescriptorLease.swift */, C11324420000000000000001 /* CloudTuiManualIOFrame.swift */, @@ -9096,7 +9061,6 @@ A12677000000000000000002 /* MachineCreateCoordinator+Selection.swift */, FA7AB7C9143F6869E3ED0E95 /* MachineCreateOperation.swift */, 5DE6021305BAAE21622C5DFD /* MachineCreateRequest.swift */, - 6EECEC1A2D3CFC13D4343F46 /* VMMachineKind.swift */, 9CEC6EF35B71AE59FA45BA69 /* VMClient.swift */, FA1253800000000000000002 /* VMStats.swift */, 69879F55D8ABC32BD1538457 /* VMResourceStatsStore+Entry.swift */, @@ -11152,7 +11116,6 @@ E8F5DB43CBC1B7DF31B7A0C9 /* RemoteTmuxSSHTransport.swift */, 938D1CC0FD9AA3B4F32498BD /* Workspace+SSHTuiStatus.swift */, 3215707A3AC7512495F685AA /* DetectedSSHSession+Workspace.swift */, - 90C987FF6EE94B4E842EC733 /* SurfaceMachineID.swift */, F97A8A59948A6E78A1170AA3 /* CmuxTuiSurfaceProvider+Hosting.swift */, A5B66C7DD8A6A89CAF4027DA /* WorkspaceCloudVMBinding.swift */, C48B23C47B58CA29EAC5FAC6 /* TerminalController+SSHWorkspaceResolution.swift */, @@ -12918,6 +12881,7 @@ ); name = cmux; packageProductDependencies = ( + 5CA7A1060000000000000002 /* CmuxSurfaceCatalogModel */, 79E4513469FB44C7A3690415 /* CmuxCloudMachines */, A5001231 /* Sparkle */, A5001271 /* PostHog */, @@ -13001,6 +12965,7 @@ ); name = "cmux-cli"; packageProductDependencies = ( + 5CA7A1060000000000000002 /* CmuxSurfaceCatalogModel */, A5B00002A1B2C3D4E5F60718 /* CMUXAgentLaunch */, A77A0002A1B2C3D4E5F60001 /* CmuxAgentJournal */, C750100000000000000000A2 /* CmuxControlSocket */, @@ -13071,6 +13036,7 @@ ); name = cmuxTests; packageProductDependencies = ( + 5CA7A1060000000000000003 /* CmuxSurfaceCatalogModel */, 79E4513469FB44C7A3690415 /* CmuxCloudMachines */, 29813FE5A6CBC1019289A251 /* CMUXAuthCore */, 800100000000000000000202 /* CmuxHive */, @@ -13174,6 +13140,7 @@ ); mainGroup = A5001040; packageReferences = ( + 5CA7A1060000000000000001 /* XCLocalSwiftPackageReference "CmuxSurfaceCatalogModel" */, 3915DFE5FE794CDF87D746C9 /* XCLocalSwiftPackageReference "CmuxCloudMachines" */, A5001232 /* XCRemoteSwiftPackageReference "Sparkle" */, A5001252 /* XCRemoteSwiftPackageReference "sentry-cocoa" */, @@ -14057,8 +14024,6 @@ 375E0D1FDA464DC4A05359E1 /* CloudSidebarRowSnapshot.swift in Sources */, D318853B007D48AD9532F83F /* CloudSurfaceDropGate.swift in Sources */, 09FBC463786149D49E73B297 /* CloudSurfaceDropGateView.swift in Sources */, - E2B852A520D64882866D700C /* CloudTabNameAuthority.swift in Sources */, - 1B425E2EA8D5475B94ED1BC1 /* CloudTabNameSource.swift in Sources */, 13019C000000000000000002 /* CloudTeamScopeObserver.swift in Sources */, CCBC23BCB453E9A38F16DB2D /* CloudTelemetryClient.swift in Sources */, A38052AB06B7E0B46A215689 /* CloudTelemetrySending.swift in Sources */, @@ -14174,7 +14139,6 @@ C11324430000000000000001 /* CloudTuiRemoteColors.swift in Sources */, C11324710000000000000001 /* CloudTuiResolvedSurface.swift in Sources */, C11324910000000000000001 /* CloudTuiSurfaceIDResolution.swift in Sources */, - C11325A10000000000000001 /* CloudTuiTerminalProjectionTarget.swift in Sources */, 0F2A151D749994FC57D403CA /* CloudTunnelActivationObserver.swift in Sources */, 430BEAB3F1329807647B9796 /* CloudTunnelAdmission.swift in Sources */, 7A0CE1000000000000000308 /* CloudTunnelAppConsumers.swift in Sources */, @@ -14207,8 +14171,6 @@ C7F29A7CD7C6410F87BB8537 /* CloudVMLoadingPanel.swift in Sources */, C7F29A7CD7C6410F87BB8536 /* CloudVMLoadingPanelView.swift in Sources */, 800100000000000000000304 /* CloudVMPanelAuthState.swift in Sources */, - 2F48EE37A14B4D76A8013B83 /* CloudVMState+SnapshotComparison.swift in Sources */, - E34036E08961479A9D9BF649 /* CloudVMTabState.swift in Sources */, 319338AE832D4D34BCA1B995 /* CloudWireGuardHub+Production.swift in Sources */, 999B711473384AA9593B1936 /* CloudWireGuardHub.swift in Sources */, 7A0CE1000000000000000710 /* CloudWireGuardHubDialer.swift in Sources */, @@ -14319,7 +14281,6 @@ D9F800000000000000000001 /* CmuxTuiRemoteRouting.swift in Sources */, 3CBCDC9381A949A7A6A239D0 /* CmuxTuiSnapshotParser+Displays.swift in Sources */, 622E567166E1DC2C614E4E7F /* CmuxTuiSnapshotParser+Placement.swift in Sources */, - 3B2BDBC03043E0C3CC4A308E /* CmuxTuiSnapshotParser.swift in Sources */, 3312CA2585324CF9B4CA9D9A /* CmuxTuiSurfaceProvider+AgentNaming.swift in Sources */, 81A1621623F83B61D4C5FFA9 /* CmuxTuiSurfaceProvider+AttachmentRecovery.swift in Sources */, 5633045E23E44075B22C08CA /* CmuxTuiSurfaceProvider+CloseTerminal.swift in Sources */, @@ -15542,15 +15503,10 @@ C12575000000000000000036 /* SurfaceCatalog+WorkspaceDeletion.swift in Sources */, 00B9FC4EDB2E47F6819B01C4 /* SurfaceCatalog+WorkspaceMembership.swift in Sources */, F7F3BED81BD5FE96D52055C0 /* SurfaceCatalog.swift in Sources */, - 31B62CAEA63AA5B0F737F227 /* SurfaceCatalogModel.swift in Sources */, D01100800000000000000002 /* SurfaceCatalogQueryService.swift in Sources */, D22034B96FFC2D5F31B4311B /* SurfaceCatalogSnapshot+DeviceVisibility.swift in Sources */, - 201889FAA2BD4E1CB5094958 /* SurfaceCatalogSnapshot.swift in Sources */, - DEC1CE00000000000000003A /* SurfaceDeviceInstanceID.swift in Sources */, - DEC1CE00000000000000003C /* SurfaceDevicePresence.swift in Sources */, 32A5901C769249B692FA1104 /* SurfaceDropFeedback.swift in Sources */, F26476EEB1E440498FD2A4F5 /* SurfaceLayoutTerminalCreating.swift in Sources */, - DB9B5DF2EB6F87829782A6E5 /* SurfaceMachineID.swift in Sources */, 917D12A30EB4417BBE5E9E6A /* SurfaceOwnershipPolicy.swift in Sources */, C11323110000000000000001 /* SurfacePaneFactory+CloudManualMirror.swift in Sources */, D3D6AC9CC41C0052D47883CD /* SurfacePaneFactory+ProjectionLayout.swift in Sources */, @@ -15561,7 +15517,6 @@ 027446BA1E1B4E69B2DCDF8A /* SurfaceProjection+WorkspaceMembership.swift in Sources */, 0F4B1E526E23283B5CA1FAAA /* SurfaceProjectionEndReason.swift in Sources */, D01324400000000000000006 /* SurfaceProjectionIdentity+Workspace.swift in Sources */, - D01324400000000000000002 /* SurfaceProjectionIdentity.swift in Sources */, A9C10D000000000000000003 /* SurfaceProjectionMaterialization+Key.swift in Sources */, A9C10D000000000000000002 /* SurfaceProjectionMaterialization.swift in Sources */, 932123D0170AF0015A0D5B11 /* SurfaceProjectionMutationObserving.swift in Sources */, @@ -15575,7 +15530,6 @@ F96CAE8A14774122A5F8613B /* SurfaceResourceDragPayload.swift in Sources */, 3E66E7A1B2B320EC850BB174 /* SurfaceResourceDragRegistry.swift in Sources */, 12D94A2FDF834F9495F22AEE /* SurfaceResourceGroup+CloudNavigation.swift in Sources */, - 891B4C987DA84C9B975AD832 /* SurfaceResourcePlacement.swift in Sources */, 883700000000000000000003 /* SurfaceResumeApprovalLookup.swift in Sources */, 875900000000000000000005 /* SurfaceResumeApprovalSigningSecretCache.swift in Sources */, 883700000000000000000001 /* SurfaceResumeApprovalSigningSecretResolution.swift in Sources */, @@ -15993,7 +15947,6 @@ 7A0CE1000000000000000044 /* VMClientSocketCommands+Tunnel.swift in Sources */, ACE4FE2A8F1CC6C0B6A673AB /* VMClientSocketCommands.swift in Sources */, 93AF7E8EC956FE883E252322 /* VMClientTelemetry.swift in Sources */, - F1C699902D31503EE3D492DF /* VMMachineKind.swift in Sources */, CADD34A6A84246019F1EB5DA /* VMReflectionResult.swift in Sources */, 142CC5D4A966D7EF32E2889F /* VMResourceStatsStore+Entry.swift in Sources */, 141CC77C279D12D2CBAC100D /* VMResourceStatsStore+Request.swift in Sources */, @@ -16441,7 +16394,6 @@ 9A6800000000000000000002 /* SSHPTYTerminalReadinessReport.swift in Sources */, D0F101000000000000000003 /* TerminalRawMode.swift in Sources */, 9009A0029009A0029009A001 /* TmuxCompatLaunchContextError.swift in Sources */, - DCFD2C3A4F04C00C62720EE5 /* VMMachineKind.swift in Sources */, B81607322100000000000002 /* VMMachineTerminalResolution.swift in Sources */, 65F8FEC592CFA22BD1F4FA29 /* VMRemoteWorkspaceResolver.swift in Sources */, A6AC720A0000000000000001 /* WorkspaceLoadingArguments.swift in Sources */, @@ -18127,6 +18079,10 @@ /* End XCConfigurationList section */ /* Begin XCLocalSwiftPackageReference section */ + 5CA7A1060000000000000001 /* XCLocalSwiftPackageReference "CmuxSurfaceCatalogModel" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Packages/macOS/CmuxSurfaceCatalogModel; + }; D96972000000000000000001 /* XCLocalSwiftPackageReference "CmuxSudoBroker" */ = { isa = XCLocalSwiftPackageReference; relativePath = Packages/macOS/CmuxSudoBroker; @@ -18422,6 +18378,16 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 5CA7A1060000000000000002 /* CmuxSurfaceCatalogModel */ = { + isa = XCSwiftPackageProductDependency; + package = 5CA7A1060000000000000001 /* XCLocalSwiftPackageReference "CmuxSurfaceCatalogModel" */; + productName = CmuxSurfaceCatalogModel; + }; + 5CA7A1060000000000000003 /* CmuxSurfaceCatalogModel */ = { + isa = XCSwiftPackageProductDependency; + package = 5CA7A1060000000000000001 /* XCLocalSwiftPackageReference "CmuxSurfaceCatalogModel" */; + productName = CmuxSurfaceCatalogModel; + }; D96973000000000000000001 /* CmuxSudoBroker */ = { isa = XCSwiftPackageProductDependency; package = D96972000000000000000001 /* XCLocalSwiftPackageReference "CmuxSudoBroker" */; diff --git a/cmux.xcworkspace/contents.xcworkspacedata b/cmux.xcworkspace/contents.xcworkspacedata index 267b30860b6e..d23ef2df6347 100644 --- a/cmux.xcworkspace/contents.xcworkspacedata +++ b/cmux.xcworkspace/contents.xcworkspacedata @@ -237,6 +237,9 @@ + + diff --git a/cmuxTests/CloudCatalogQueryTestProvider.swift b/cmuxTests/CloudCatalogQueryTestProvider.swift index c0b46017933d..1a84168e9ad8 100644 --- a/cmuxTests/CloudCatalogQueryTestProvider.swift +++ b/cmuxTests/CloudCatalogQueryTestProvider.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudClosedPanelRestoreTests.swift b/cmuxTests/CloudClosedPanelRestoreTests.swift index 2ce702dc9d45..82e49de460bf 100644 --- a/cmuxTests/CloudClosedPanelRestoreTests.swift +++ b/cmuxTests/CloudClosedPanelRestoreTests.swift @@ -1,5 +1,6 @@ import Bonsplit import CmuxSettings +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudDesktopAccessTests.swift b/cmuxTests/CloudDesktopAccessTests.swift index e0b8d28940d5..dc67a5a02415 100644 --- a/cmuxTests/CloudDesktopAccessTests.swift +++ b/cmuxTests/CloudDesktopAccessTests.swift @@ -1,4 +1,5 @@ import CmuxSettings +import CmuxSurfaceCatalogModel import Foundation import Testing import WebKit diff --git a/cmuxTests/CloudDesktopOpenFixture.swift b/cmuxTests/CloudDesktopOpenFixture.swift index ef2f57dd90a7..b56a8bdde3f6 100644 --- a/cmuxTests/CloudDesktopOpenFixture.swift +++ b/cmuxTests/CloudDesktopOpenFixture.swift @@ -1,5 +1,6 @@ import AppKit import Bonsplit +import CmuxSurfaceCatalogModel import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudDesktopOpenTestProvider.swift b/cmuxTests/CloudDesktopOpenTestProvider.swift index 29a2344ae831..018147f76a70 100644 --- a/cmuxTests/CloudDesktopOpenTestProvider.swift +++ b/cmuxTests/CloudDesktopOpenTestProvider.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudDirectoryLifecycleTests.swift b/cmuxTests/CloudDirectoryLifecycleTests.swift index f1cb38241d9a..3856b6aa72f9 100644 --- a/cmuxTests/CloudDirectoryLifecycleTests.swift +++ b/cmuxTests/CloudDirectoryLifecycleTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudDirectoryTestFixture.swift b/cmuxTests/CloudDirectoryTestFixture.swift index 2cb4fbf3eb7c..9abf1ccdfd21 100644 --- a/cmuxTests/CloudDirectoryTestFixture.swift +++ b/cmuxTests/CloudDirectoryTestFixture.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudDisplayCatalogTests.swift b/cmuxTests/CloudDisplayCatalogTests.swift index fd24e04a561c..33144aab8b23 100644 --- a/cmuxTests/CloudDisplayCatalogTests.swift +++ b/cmuxTests/CloudDisplayCatalogTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudGuestURLRoutingTests.swift b/cmuxTests/CloudGuestURLRoutingTests.swift index cb7fdd37ff13..7d72630106ca 100644 --- a/cmuxTests/CloudGuestURLRoutingTests.swift +++ b/cmuxTests/CloudGuestURLRoutingTests.swift @@ -1,4 +1,5 @@ import AppKit +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudInitialWorkspaceNamingTests.swift b/cmuxTests/CloudInitialWorkspaceNamingTests.swift index ef7280a59f37..76b7306a18ff 100644 --- a/cmuxTests/CloudInitialWorkspaceNamingTests.swift +++ b/cmuxTests/CloudInitialWorkspaceNamingTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudMachineNotificationEventTests.swift b/cmuxTests/CloudMachineNotificationEventTests.swift index 0fb5dcb0e47e..f70b5da90588 100644 --- a/cmuxTests/CloudMachineNotificationEventTests.swift +++ b/cmuxTests/CloudMachineNotificationEventTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudMachineOrderingFixture.swift b/cmuxTests/CloudMachineOrderingFixture.swift index 1d4d3b94d33c..b1b19aced810 100644 --- a/cmuxTests/CloudMachineOrderingFixture.swift +++ b/cmuxTests/CloudMachineOrderingFixture.swift @@ -1,5 +1,6 @@ import AppKit import CmuxCloudMachines +import CmuxSurfaceCatalogModel import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudMachineWorkspaceAdoptionTests.swift b/cmuxTests/CloudMachineWorkspaceAdoptionTests.swift index af277f3679c0..6d4e31697cb0 100644 --- a/cmuxTests/CloudMachineWorkspaceAdoptionTests.swift +++ b/cmuxTests/CloudMachineWorkspaceAdoptionTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudMachineWorkspaceTestProvider.swift b/cmuxTests/CloudMachineWorkspaceTestProvider.swift index d00d44a832b3..52b97d303506 100644 --- a/cmuxTests/CloudMachineWorkspaceTestProvider.swift +++ b/cmuxTests/CloudMachineWorkspaceTestProvider.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudManualMirrorTransportTests.swift b/cmuxTests/CloudManualMirrorTransportTests.swift index e3446c570b48..f0d2de9843af 100644 --- a/cmuxTests/CloudManualMirrorTransportTests.swift +++ b/cmuxTests/CloudManualMirrorTransportTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudNameAuthorityFixture.swift b/cmuxTests/CloudNameAuthorityFixture.swift index b3591b0a6f40..01956f9df47c 100644 --- a/cmuxTests/CloudNameAuthorityFixture.swift +++ b/cmuxTests/CloudNameAuthorityFixture.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudNameAuthorityTestProvider.swift b/cmuxTests/CloudNameAuthorityTestProvider.swift index 6cb26570f34a..5cb2852b8051 100644 --- a/cmuxTests/CloudNameAuthorityTestProvider.swift +++ b/cmuxTests/CloudNameAuthorityTestProvider.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudNameAuthorityTests.swift b/cmuxTests/CloudNameAuthorityTests.swift index f4bf09575a7c..9ae8ccf60095 100644 --- a/cmuxTests/CloudNameAuthorityTests.swift +++ b/cmuxTests/CloudNameAuthorityTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudNativeLayoutProjectionTests.swift b/cmuxTests/CloudNativeLayoutProjectionTests.swift index 8d6b6ddb02f9..0e722923e056 100644 --- a/cmuxTests/CloudNativeLayoutProjectionTests.swift +++ b/cmuxTests/CloudNativeLayoutProjectionTests.swift @@ -1,4 +1,5 @@ import CmuxCore +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudNotificationDismissParityHarness.swift b/cmuxTests/CloudNotificationDismissParityHarness.swift index ea74f515e117..13dcfaf9de04 100644 --- a/cmuxTests/CloudNotificationDismissParityHarness.swift +++ b/cmuxTests/CloudNotificationDismissParityHarness.swift @@ -1,4 +1,5 @@ import AppKit +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudNotificationDismissParityTests.swift b/cmuxTests/CloudNotificationDismissParityTests.swift index 90593d72951b..91930d86abc2 100644 --- a/cmuxTests/CloudNotificationDismissParityTests.swift +++ b/cmuxTests/CloudNotificationDismissParityTests.swift @@ -1,4 +1,5 @@ import AppKit +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudNotificationSyncTests.swift b/cmuxTests/CloudNotificationSyncTests.swift index 48431110d277..39c42fdf2192 100644 --- a/cmuxTests/CloudNotificationSyncTests.swift +++ b/cmuxTests/CloudNotificationSyncTests.swift @@ -1,5 +1,6 @@ #if canImport(cmux_DEV) @testable import cmux_DEV +import CmuxSurfaceCatalogModel #elseif canImport(cmux) @testable import cmux #endif diff --git a/cmuxTests/CloudOperationRecorderTests.swift b/cmuxTests/CloudOperationRecorderTests.swift index 5fdbb54f1596..ea80f975177d 100644 --- a/cmuxTests/CloudOperationRecorderTests.swift +++ b/cmuxTests/CloudOperationRecorderTests.swift @@ -1,5 +1,6 @@ import CmuxAuthRuntime import AppKit +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudPaneCreationRetryTests.swift b/cmuxTests/CloudPaneCreationRetryTests.swift index 763acee26232..d14463c2aa3a 100644 --- a/cmuxTests/CloudPaneCreationRetryTests.swift +++ b/cmuxTests/CloudPaneCreationRetryTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudPlacementCoordinatorTests.swift b/cmuxTests/CloudPlacementCoordinatorTests.swift index 705262eb44e8..c3bc2cfaec0c 100644 --- a/cmuxTests/CloudPlacementCoordinatorTests.swift +++ b/cmuxTests/CloudPlacementCoordinatorTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudPlacementTestProvider.swift b/cmuxTests/CloudPlacementTestProvider.swift index 5131d1598478..0b9564a48778 100644 --- a/cmuxTests/CloudPlacementTestProvider.swift +++ b/cmuxTests/CloudPlacementTestProvider.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudPortOpenRegressionTests.swift b/cmuxTests/CloudPortOpenRegressionTests.swift index 4c93a67f5d91..72adbc71b4fd 100644 --- a/cmuxTests/CloudPortOpenRegressionTests.swift +++ b/cmuxTests/CloudPortOpenRegressionTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing import CmuxWorkspaces diff --git a/cmuxTests/CloudPortRoutePlanTests.swift b/cmuxTests/CloudPortRoutePlanTests.swift index 362fccace26c..d3d7c42b92cb 100644 --- a/cmuxTests/CloudPortRoutePlanTests.swift +++ b/cmuxTests/CloudPortRoutePlanTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudPortsVPNAffordanceTests.swift b/cmuxTests/CloudPortsVPNAffordanceTests.swift index 78e50afce24d..b9e4e87c6695 100644 --- a/cmuxTests/CloudPortsVPNAffordanceTests.swift +++ b/cmuxTests/CloudPortsVPNAffordanceTests.swift @@ -1,5 +1,6 @@ import AppKit import CmuxSettings +import CmuxSurfaceCatalogModel import SwiftUI import Testing diff --git a/cmuxTests/CloudProviderRefreshCoordinatorTests.swift b/cmuxTests/CloudProviderRefreshCoordinatorTests.swift index 7bf112aaf998..36e7ce373eac 100644 --- a/cmuxTests/CloudProviderRefreshCoordinatorTests.swift +++ b/cmuxTests/CloudProviderRefreshCoordinatorTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudRenameOptimismTests.swift b/cmuxTests/CloudRenameOptimismTests.swift index 3604201bf51b..b54250274435 100644 --- a/cmuxTests/CloudRenameOptimismTests.swift +++ b/cmuxTests/CloudRenameOptimismTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudSidebarConsistencyTests.swift b/cmuxTests/CloudSidebarConsistencyTests.swift index f6875fc56f8c..80eb4038149b 100644 --- a/cmuxTests/CloudSidebarConsistencyTests.swift +++ b/cmuxTests/CloudSidebarConsistencyTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudSidebarNativeDropTests.swift b/cmuxTests/CloudSidebarNativeDropTests.swift index b9ed21032dc7..753980a99bf7 100644 --- a/cmuxTests/CloudSidebarNativeDropTests.swift +++ b/cmuxTests/CloudSidebarNativeDropTests.swift @@ -1,4 +1,5 @@ import AppKit +import CmuxSurfaceCatalogModel import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudSidebarNotificationTests.swift b/cmuxTests/CloudSidebarNotificationTests.swift index 6ebd8b95f2ac..d6183d9dde6a 100644 --- a/cmuxTests/CloudSidebarNotificationTests.swift +++ b/cmuxTests/CloudSidebarNotificationTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import CmuxSettings import Testing diff --git a/cmuxTests/CloudSidebarOrderingTests.swift b/cmuxTests/CloudSidebarOrderingTests.swift index 03c51a07dafd..9472d2d270ce 100644 --- a/cmuxTests/CloudSidebarOrderingTests.swift +++ b/cmuxTests/CloudSidebarOrderingTests.swift @@ -1,5 +1,6 @@ import AppKit import Bonsplit +import CmuxSurfaceCatalogModel import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudSidebarRenameFixture.swift b/cmuxTests/CloudSidebarRenameFixture.swift index 03f3fdb3a862..ea8454245c50 100644 --- a/cmuxTests/CloudSidebarRenameFixture.swift +++ b/cmuxTests/CloudSidebarRenameFixture.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudSidebarRenameReconciliationTests.swift b/cmuxTests/CloudSidebarRenameReconciliationTests.swift index 0365e6d91e3b..29be48d4494a 100644 --- a/cmuxTests/CloudSidebarRenameReconciliationTests.swift +++ b/cmuxTests/CloudSidebarRenameReconciliationTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudSidebarSurfaceRegressionTests.swift b/cmuxTests/CloudSidebarSurfaceRegressionTests.swift index 5bb71daafcc7..c105238c1bb5 100644 --- a/cmuxTests/CloudSidebarSurfaceRegressionTests.swift +++ b/cmuxTests/CloudSidebarSurfaceRegressionTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudSurfaceDragFeedbackTests.swift b/cmuxTests/CloudSurfaceDragFeedbackTests.swift index b0647510836d..dc64c9a22f46 100644 --- a/cmuxTests/CloudSurfaceDragFeedbackTests.swift +++ b/cmuxTests/CloudSurfaceDragFeedbackTests.swift @@ -1,5 +1,6 @@ import AppKit import Bonsplit +import CmuxSurfaceCatalogModel import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudSurfaceDragFixture.swift b/cmuxTests/CloudSurfaceDragFixture.swift index 8e8c6f9a42d4..8f262c2f99ec 100644 --- a/cmuxTests/CloudSurfaceDragFixture.swift +++ b/cmuxTests/CloudSurfaceDragFixture.swift @@ -1,5 +1,6 @@ import AppKit import Bonsplit +import CmuxSurfaceCatalogModel import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudSurfaceMoveOwnershipTests.swift b/cmuxTests/CloudSurfaceMoveOwnershipTests.swift index 81803505d564..0954467f24e8 100644 --- a/cmuxTests/CloudSurfaceMoveOwnershipTests.swift +++ b/cmuxTests/CloudSurfaceMoveOwnershipTests.swift @@ -2,6 +2,7 @@ import AppKit import Bonsplit import CmuxCore import CmuxRemoteSession +import CmuxSurfaceCatalogModel import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudSurfaceOwnershipTests.swift b/cmuxTests/CloudSurfaceOwnershipTests.swift index 4958d4510999..472c9ab4aed0 100644 --- a/cmuxTests/CloudSurfaceOwnershipTests.swift +++ b/cmuxTests/CloudSurfaceOwnershipTests.swift @@ -1,5 +1,6 @@ import AppKit import Bonsplit +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudTerminalCreationCoordinatorTests.swift b/cmuxTests/CloudTerminalCreationCoordinatorTests.swift index 7a15cf3c0944..848116d65e5f 100644 --- a/cmuxTests/CloudTerminalCreationCoordinatorTests.swift +++ b/cmuxTests/CloudTerminalCreationCoordinatorTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudTerminalDragSplitRoutingTests.swift b/cmuxTests/CloudTerminalDragSplitRoutingTests.swift index fdfe5cbc0c89..8f7d655a7a42 100644 --- a/cmuxTests/CloudTerminalDragSplitRoutingTests.swift +++ b/cmuxTests/CloudTerminalDragSplitRoutingTests.swift @@ -1,5 +1,6 @@ import Bonsplit import CmuxCore +import CmuxSurfaceCatalogModel import Foundation import Testing @testable import CmuxTerminal diff --git a/cmuxTests/CloudTerminalLayoutCreationTests.swift b/cmuxTests/CloudTerminalLayoutCreationTests.swift index a83fe8fda549..fe149e6a3599 100644 --- a/cmuxTests/CloudTerminalLayoutCreationTests.swift +++ b/cmuxTests/CloudTerminalLayoutCreationTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudTerminalNavigationCapabilityTests.swift b/cmuxTests/CloudTerminalNavigationCapabilityTests.swift index 5879d95ce16b..4a96fb1166d2 100644 --- a/cmuxTests/CloudTerminalNavigationCapabilityTests.swift +++ b/cmuxTests/CloudTerminalNavigationCapabilityTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudTerminalNavigationFixture.swift b/cmuxTests/CloudTerminalNavigationFixture.swift index 2b78608e5605..aafd6b84eeb8 100644 --- a/cmuxTests/CloudTerminalNavigationFixture.swift +++ b/cmuxTests/CloudTerminalNavigationFixture.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudTerminalNavigationTests.swift b/cmuxTests/CloudTerminalNavigationTests.swift index dce7b44fd851..d0e639a4ab80 100644 --- a/cmuxTests/CloudTerminalNavigationTests.swift +++ b/cmuxTests/CloudTerminalNavigationTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudTerminalPaneReservationTests.swift b/cmuxTests/CloudTerminalPaneReservationTests.swift index 7a54a5e30b46..56b19a68bffc 100644 --- a/cmuxTests/CloudTerminalPaneReservationTests.swift +++ b/cmuxTests/CloudTerminalPaneReservationTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudTerminalPlacementTestProvider.swift b/cmuxTests/CloudTerminalPlacementTestProvider.swift index c0003a4ce351..ab09d6331835 100644 --- a/cmuxTests/CloudTerminalPlacementTestProvider.swift +++ b/cmuxTests/CloudTerminalPlacementTestProvider.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudTerminalPlacementTests.swift b/cmuxTests/CloudTerminalPlacementTests.swift index 89a1abdc4281..37a8a14f30cb 100644 --- a/cmuxTests/CloudTerminalPlacementTests.swift +++ b/cmuxTests/CloudTerminalPlacementTests.swift @@ -1,5 +1,6 @@ import Bonsplit import CmuxControlSocket +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudTreeAvailabilityTests.swift b/cmuxTests/CloudTreeAvailabilityTests.swift index 0b95f0c1f74d..eab068f0f2fa 100644 --- a/cmuxTests/CloudTreeAvailabilityTests.swift +++ b/cmuxTests/CloudTreeAvailabilityTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudTreeMachineMenuTests.swift b/cmuxTests/CloudTreeMachineMenuTests.swift index e12b126a368f..947a0aada74c 100644 --- a/cmuxTests/CloudTreeMachineMenuTests.swift +++ b/cmuxTests/CloudTreeMachineMenuTests.swift @@ -1,5 +1,6 @@ import AppKit import CmuxCloudMachines +import CmuxSurfaceCatalogModel import Testing import Observation diff --git a/cmuxTests/CloudTreeMachineResourcesTests.swift b/cmuxTests/CloudTreeMachineResourcesTests.swift index 9c83491ad07b..b1375fad5799 100644 --- a/cmuxTests/CloudTreeMachineResourcesTests.swift +++ b/cmuxTests/CloudTreeMachineResourcesTests.swift @@ -1,6 +1,7 @@ import AppKit import CmuxCloudMachines import CmuxFoundation +import CmuxSurfaceCatalogModel import Foundation import SwiftUI import Testing diff --git a/cmuxTests/CloudTreeNativeDragOwnershipTests.swift b/cmuxTests/CloudTreeNativeDragOwnershipTests.swift index 50c5ad6b0cc4..1f3baa33a638 100644 --- a/cmuxTests/CloudTreeNativeDragOwnershipTests.swift +++ b/cmuxTests/CloudTreeNativeDragOwnershipTests.swift @@ -1,5 +1,6 @@ import AppKit import Bonsplit +import CmuxSurfaceCatalogModel import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudTreeOneMachineManyWorkspacesTests.swift b/cmuxTests/CloudTreeOneMachineManyWorkspacesTests.swift index 6c6852b4bf36..4e42f35b8650 100644 --- a/cmuxTests/CloudTreeOneMachineManyWorkspacesTests.swift +++ b/cmuxTests/CloudTreeOneMachineManyWorkspacesTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudTreeWorkspaceTitleLayoutTests.swift b/cmuxTests/CloudTreeWorkspaceTitleLayoutTests.swift index b7a4e57b60d8..e42a7cf313b7 100644 --- a/cmuxTests/CloudTreeWorkspaceTitleLayoutTests.swift +++ b/cmuxTests/CloudTreeWorkspaceTitleLayoutTests.swift @@ -1,4 +1,5 @@ import AppKit +import CmuxSurfaceCatalogModel import Testing #if canImport(cmux_DEV) @testable import cmux_DEV diff --git a/cmuxTests/CloudVMStateSnapshotComparisonTests.swift b/cmuxTests/CloudVMStateSnapshotComparisonTests.swift index 877355349c62..3f4a290f32d2 100644 --- a/cmuxTests/CloudVMStateSnapshotComparisonTests.swift +++ b/cmuxTests/CloudVMStateSnapshotComparisonTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudWorkspaceBindingRegressionTests.swift b/cmuxTests/CloudWorkspaceBindingRegressionTests.swift index bb3d3b40e0b1..1f3d0e546e96 100644 --- a/cmuxTests/CloudWorkspaceBindingRegressionTests.swift +++ b/cmuxTests/CloudWorkspaceBindingRegressionTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudWorkspaceCreationSidebarProvider.swift b/cmuxTests/CloudWorkspaceCreationSidebarProvider.swift index 7ffdb148e0fc..c2e732beea59 100644 --- a/cmuxTests/CloudWorkspaceCreationSidebarProvider.swift +++ b/cmuxTests/CloudWorkspaceCreationSidebarProvider.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudWorkspaceDeleteOptimismTests.swift b/cmuxTests/CloudWorkspaceDeleteOptimismTests.swift index 2ab162f830b9..465049463e2c 100644 --- a/cmuxTests/CloudWorkspaceDeleteOptimismTests.swift +++ b/cmuxTests/CloudWorkspaceDeleteOptimismTests.swift @@ -1,5 +1,6 @@ import AppKit import CmuxCloudMachines +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudWorkspaceDeleteTestProvider.swift b/cmuxTests/CloudWorkspaceDeleteTestProvider.swift index 9cdb23258651..0d404b8425fd 100644 --- a/cmuxTests/CloudWorkspaceDeleteTestProvider.swift +++ b/cmuxTests/CloudWorkspaceDeleteTestProvider.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation #if canImport(cmux_DEV) @testable import cmux_DEV diff --git a/cmuxTests/CloudWorkspaceDeletionLedgerTests.swift b/cmuxTests/CloudWorkspaceDeletionLedgerTests.swift index e13374fa030d..931f092473c3 100644 --- a/cmuxTests/CloudWorkspaceDeletionLedgerTests.swift +++ b/cmuxTests/CloudWorkspaceDeletionLedgerTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import CmuxCore import Testing diff --git a/cmuxTests/CloudWorkspaceLayoutTranslatorTests.swift b/cmuxTests/CloudWorkspaceLayoutTranslatorTests.swift index 09831a01eee8..8913cbf0590c 100644 --- a/cmuxTests/CloudWorkspaceLayoutTranslatorTests.swift +++ b/cmuxTests/CloudWorkspaceLayoutTranslatorTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudWorkspaceLiveProjectionTests.swift b/cmuxTests/CloudWorkspaceLiveProjectionTests.swift index b6818fa77077..2c17d8f89d8b 100644 --- a/cmuxTests/CloudWorkspaceLiveProjectionTests.swift +++ b/cmuxTests/CloudWorkspaceLiveProjectionTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing import WebKit diff --git a/cmuxTests/CloudWorkspaceMembershipTests.swift b/cmuxTests/CloudWorkspaceMembershipTests.swift index 0aad91439a15..b7e9a9af2ec5 100644 --- a/cmuxTests/CloudWorkspaceMembershipTests.swift +++ b/cmuxTests/CloudWorkspaceMembershipTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudWorkspaceRenameSurfaceParityTests.swift b/cmuxTests/CloudWorkspaceRenameSurfaceParityTests.swift index 85d8b45a0a14..36f5ac533c41 100644 --- a/cmuxTests/CloudWorkspaceRenameSurfaceParityTests.swift +++ b/cmuxTests/CloudWorkspaceRenameSurfaceParityTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudWorkspaceRestoreNamesTests.swift b/cmuxTests/CloudWorkspaceRestoreNamesTests.swift index 0186d288e615..2e6431344a2f 100644 --- a/cmuxTests/CloudWorkspaceRestoreNamesTests.swift +++ b/cmuxTests/CloudWorkspaceRestoreNamesTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import CmuxWorkspaces import Foundation import Testing diff --git a/cmuxTests/CmuxTuiSurfaceProviderTests.swift b/cmuxTests/CmuxTuiSurfaceProviderTests.swift index e9c01d6d6f46..e89f02bfdd2d 100644 --- a/cmuxTests/CmuxTuiSurfaceProviderTests.swift +++ b/cmuxTests/CmuxTuiSurfaceProviderTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Darwin import Foundation import Testing diff --git a/cmuxTests/CurrentWorkCommandPaletteTests.swift b/cmuxTests/CurrentWorkCommandPaletteTests.swift index 70e077760a6e..d08d3c7e297d 100644 --- a/cmuxTests/CurrentWorkCommandPaletteTests.swift +++ b/cmuxTests/CurrentWorkCommandPaletteTests.swift @@ -1,4 +1,5 @@ import CmuxCommandPalette +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CurrentWorkQueryServiceTests.swift b/cmuxTests/CurrentWorkQueryServiceTests.swift index 3a7295eba73a..2348a275c981 100644 --- a/cmuxTests/CurrentWorkQueryServiceTests.swift +++ b/cmuxTests/CurrentWorkQueryServiceTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CurrentWorkReducerTests.swift b/cmuxTests/CurrentWorkReducerTests.swift index c7a8959ff417..1973db235c7c 100644 --- a/cmuxTests/CurrentWorkReducerTests.swift +++ b/cmuxTests/CurrentWorkReducerTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/DeviceDirectoryLifecycleTests.swift b/cmuxTests/DeviceDirectoryLifecycleTests.swift index 9a6d00600570..e9821b681bb0 100644 --- a/cmuxTests/DeviceDirectoryLifecycleTests.swift +++ b/cmuxTests/DeviceDirectoryLifecycleTests.swift @@ -2,6 +2,7 @@ import CMUXAuthCore import CMUXMobileCore import CmuxAuthRuntime import CmuxMobileRPC +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/DeviceDirectoryMergeTests.swift b/cmuxTests/DeviceDirectoryMergeTests.swift index a9c6eb103c80..afeb7d73d3de 100644 --- a/cmuxTests/DeviceDirectoryMergeTests.swift +++ b/cmuxTests/DeviceDirectoryMergeTests.swift @@ -1,6 +1,7 @@ import CMUXMobileCore import CmuxIrohTransport import CmuxIrxTransport +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/DevicePresenceWireTests.swift b/cmuxTests/DevicePresenceWireTests.swift index 9a3a8719311e..8cb8c1e6e8de 100644 --- a/cmuxTests/DevicePresenceWireTests.swift +++ b/cmuxTests/DevicePresenceWireTests.swift @@ -1,4 +1,5 @@ import CMUXMobileCore +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/DeviceRouteSelectorTests.swift b/cmuxTests/DeviceRouteSelectorTests.swift index c50d5d1ba8fd..334c3c0ba741 100644 --- a/cmuxTests/DeviceRouteSelectorTests.swift +++ b/cmuxTests/DeviceRouteSelectorTests.swift @@ -1,5 +1,6 @@ import CMUXMobileCore import CmuxMobileRPC +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/DeviceWorkspaceProjectionTests.swift b/cmuxTests/DeviceWorkspaceProjectionTests.swift index e90df5afaad8..8c50f302f274 100644 --- a/cmuxTests/DeviceWorkspaceProjectionTests.swift +++ b/cmuxTests/DeviceWorkspaceProjectionTests.swift @@ -1,5 +1,6 @@ import CMUXMobileCore import CmuxCore +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/DevicesCloudTreeBuilderTests.swift b/cmuxTests/DevicesCloudTreeBuilderTests.swift index fa2be035b4ab..c0d298d7625c 100644 --- a/cmuxTests/DevicesCloudTreeBuilderTests.swift +++ b/cmuxTests/DevicesCloudTreeBuilderTests.swift @@ -1,4 +1,5 @@ import AppKit +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/DevicesSidebarModeTests.swift b/cmuxTests/DevicesSidebarModeTests.swift index 3d259850007a..0ad2e7bdc66e 100644 --- a/cmuxTests/DevicesSidebarModeTests.swift +++ b/cmuxTests/DevicesSidebarModeTests.swift @@ -1,4 +1,5 @@ import CmuxSettings +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/LastSurfaceClosePreferenceTests.swift b/cmuxTests/LastSurfaceClosePreferenceTests.swift index da6945ffd5b5..64265e2661e1 100644 --- a/cmuxTests/LastSurfaceClosePreferenceTests.swift +++ b/cmuxTests/LastSurfaceClosePreferenceTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing import CmuxSettings diff --git a/cmuxTests/LocalSurfaceProviderTests.swift b/cmuxTests/LocalSurfaceProviderTests.swift index bbeb9b587cde..05cd961af663 100644 --- a/cmuxTests/LocalSurfaceProviderTests.swift +++ b/cmuxTests/LocalSurfaceProviderTests.swift @@ -1,4 +1,5 @@ import Bonsplit +import CmuxSurfaceCatalogModel import XCTest #if canImport(cmux_DEV) @testable import cmux_DEV diff --git a/cmuxTests/MachineCreateCoordinatorTests.swift b/cmuxTests/MachineCreateCoordinatorTests.swift index 864e1971477b..41329d2e6ced 100644 --- a/cmuxTests/MachineCreateCoordinatorTests.swift +++ b/cmuxTests/MachineCreateCoordinatorTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/MachinesPanelModelTests.swift b/cmuxTests/MachinesPanelModelTests.swift index ad1b0e1f5f30..c83c6d4572a9 100644 --- a/cmuxTests/MachinesPanelModelTests.swift +++ b/cmuxTests/MachinesPanelModelTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Bonsplit import Testing diff --git a/cmuxTests/RemoteTerminalFilePreviewLoaderTests.swift b/cmuxTests/RemoteTerminalFilePreviewLoaderTests.swift index 19a23cc14217..18b580b24bfa 100644 --- a/cmuxTests/RemoteTerminalFilePreviewLoaderTests.swift +++ b/cmuxTests/RemoteTerminalFilePreviewLoaderTests.swift @@ -1,4 +1,5 @@ import CmuxCore +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/SSHTuiMigrationTests.swift b/cmuxTests/SSHTuiMigrationTests.swift index e91490828e85..b0c5056c41d9 100644 --- a/cmuxTests/SSHTuiMigrationTests.swift +++ b/cmuxTests/SSHTuiMigrationTests.swift @@ -1,5 +1,6 @@ import CmuxCore import CmuxFoundation +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/SetAutoTitleSocketTests+CloudSidebar.swift b/cmuxTests/SetAutoTitleSocketTests+CloudSidebar.swift index dfb7c4f3527a..49f176f9039b 100644 --- a/cmuxTests/SetAutoTitleSocketTests+CloudSidebar.swift +++ b/cmuxTests/SetAutoTitleSocketTests+CloudSidebar.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/SidebarCloudWorkspaceBadgeTests.swift b/cmuxTests/SidebarCloudWorkspaceBadgeTests.swift index 290cafa1e4fa..8de9e76739b6 100644 --- a/cmuxTests/SidebarCloudWorkspaceBadgeTests.swift +++ b/cmuxTests/SidebarCloudWorkspaceBadgeTests.swift @@ -1,6 +1,7 @@ import AppKit import CmuxCore import CmuxSettings +import CmuxSurfaceCatalogModel import Observation import Testing @testable import cmux_DEV diff --git a/cmuxTests/SurfaceCatalogQueryServiceTests.swift b/cmuxTests/SurfaceCatalogQueryServiceTests.swift index 13bfcc69af83..776d205e6c5a 100644 --- a/cmuxTests/SurfaceCatalogQueryServiceTests.swift +++ b/cmuxTests/SurfaceCatalogQueryServiceTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/SurfaceCatalogTests.swift b/cmuxTests/SurfaceCatalogTests.swift index 01f149fe33b8..08029f51c2aa 100644 --- a/cmuxTests/SurfaceCatalogTests.swift +++ b/cmuxTests/SurfaceCatalogTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import CmuxTerminal import Testing diff --git a/cmuxTests/SurfaceMachineIDDeviceEncodingTests.swift b/cmuxTests/SurfaceMachineIDDeviceEncodingTests.swift index ab4095c3a266..a888e9244af9 100644 --- a/cmuxTests/SurfaceMachineIDDeviceEncodingTests.swift +++ b/cmuxTests/SurfaceMachineIDDeviceEncodingTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/SurfacePaneFactoryFocusTests.swift b/cmuxTests/SurfacePaneFactoryFocusTests.swift index 7b4992002a1f..6309dcb0843d 100644 --- a/cmuxTests/SurfacePaneFactoryFocusTests.swift +++ b/cmuxTests/SurfacePaneFactoryFocusTests.swift @@ -3,6 +3,7 @@ import Bonsplit import CmuxAppKitSupportUI import CmuxAuthRuntime import CmuxPanes +import CmuxSurfaceCatalogModel import Testing import SwiftUI diff --git a/cmuxTests/SurfaceProjectionIdentityTests.swift b/cmuxTests/SurfaceProjectionIdentityTests.swift index 0ec6d8852c09..47b42f51c5ee 100644 --- a/cmuxTests/SurfaceProjectionIdentityTests.swift +++ b/cmuxTests/SurfaceProjectionIdentityTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/SurfaceSocketCommandTests.swift b/cmuxTests/SurfaceSocketCommandTests.swift index c3dd4304461b..4d46239e56d6 100644 --- a/cmuxTests/SurfaceSocketCommandTests.swift +++ b/cmuxTests/SurfaceSocketCommandTests.swift @@ -1,4 +1,5 @@ import AppKit +import CmuxSurfaceCatalogModel import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/TabManagerSessionSnapshotTests.swift b/cmuxTests/TabManagerSessionSnapshotTests.swift index d7a377aa310e..14a2839fc65b 100644 --- a/cmuxTests/TabManagerSessionSnapshotTests.swift +++ b/cmuxTests/TabManagerSessionSnapshotTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import CmuxWorkspaces import Darwin import CmuxCore diff --git a/cmuxTests/WorkspaceCreateInitialInputTests.swift b/cmuxTests/WorkspaceCreateInitialInputTests.swift index 6956ea5160aa..28b10f15d0af 100644 --- a/cmuxTests/WorkspaceCreateInitialInputTests.swift +++ b/cmuxTests/WorkspaceCreateInitialInputTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import struct CmuxSettings.AccountCatalogSection import Testing diff --git a/cmuxTests/WorkspaceTitleProvenanceTests.swift b/cmuxTests/WorkspaceTitleProvenanceTests.swift index da1908605552..4f230d12fd9c 100644 --- a/cmuxTests/WorkspaceTitleProvenanceTests.swift +++ b/cmuxTests/WorkspaceTitleProvenanceTests.swift @@ -1,3 +1,4 @@ +import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/scripts/stage-cloud-navigation-tests.py b/scripts/stage-cloud-navigation-tests.py index 6d42683695de..6a2ed20cbba2 100644 --- a/scripts/stage-cloud-navigation-tests.py +++ b/scripts/stage-cloud-navigation-tests.py @@ -49,15 +49,15 @@ def stage(path, declaration=None, test=False): "CloudTerminalNavigationHost", "CloudTerminalNavigationScheduling", "SurfaceResourceGroup+CloudNavigation"]: stage(f"Sources/Cloud/{name}.swift") - stage("Sources/Surfaces/SurfaceMachineID.swift", "enum SurfaceMachineID:") + stage("Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceMachineID.swift", "enum SurfaceMachineID:") for kind, name in [("enum", "SurfaceResourceKind"), ("enum", "SurfaceLifecycle"), ("struct", "SurfaceAgentBadge"), ("struct", "CloudCreationAttachment"), ("struct", "SurfaceResource"), ("struct", "SurfaceResourceID"), ("struct", "SurfaceRemoteWorkspace"), ("struct", "SurfaceRemoteView"), ("struct", "SurfaceProjection"), ("enum", "SurfaceSplitDirection"), ("enum", "SurfaceCatalogError")]: - stage("Sources/Surfaces/SurfaceCatalogModel.swift", f"{kind} {name}:") - stage("Sources/Surfaces/SurfaceResourcePlacement.swift") + stage("Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceCatalogModel.swift", f"{kind} {name}:") + stage("Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceResourcePlacement.swift") stage("Sources/Surfaces/SurfaceCatalog+Groups.swift", "struct SurfaceResourceGroup:") stage("Sources/Surfaces/CloudWorkspaceLayoutTranslator.swift", "indirect enum SurfaceProjectionLayout:") for name in ["CloudTerminalNavigationFixture", "CloudTerminalNavigationCapabilityTests"]: diff --git a/tests/run_cloud_command_deadline_tests.sh b/tests/run_cloud_command_deadline_tests.sh index 77062874065c..a3257ddc1b35 100755 --- a/tests/run_cloud_command_deadline_tests.sh +++ b/tests/run_cloud_command_deadline_tests.sh @@ -18,12 +18,19 @@ let package = Package( swiftLanguageModes: [.v5] ) SWIFT -for name in CloudTuiPersistentResourceConnection CloudTuiPersistentRequestBuilder CloudTuiTerminalProjectionTarget \ +# CloudTuiTerminalProjectionTarget lives in the CmuxSurfaceCatalogModel package, not in Sources/Cloud. +cp "$ROOT/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudTuiTerminalProjectionTarget.swift" \ + "$DEST/Sources/CloudCommandFixture/" +for name in CloudTuiPersistentResourceConnection CloudTuiPersistentRequestBuilder \ CloudTuiManualIOConnection CloudTuiManualIODescriptorLease CloudTuiManualIOCommand \ CloudTuiManualIOFrame CloudTuiManualIOFrameDecoder CloudTuiRemoteColors; do cp "$ROOT/Sources/Cloud/$name.swift" "$DEST/Sources/CloudCommandFixture/" done cp "$ROOT/tests/fixtures/cloud-command-deadlines/StandaloneDependencies.swift" "$DEST/Sources/CloudCommandFixture/" +# The fixture stubs the catalog value types it needs (StandaloneDependencies.swift), so the +# copied sources must not import the real package. +sed -i.bak '/^import CmuxSurfaceCatalogModel$/d' "$DEST/Sources/CloudCommandFixture/"*.swift +rm -f "$DEST/Sources/CloudCommandFixture/"*.bak for name in CloudCommandDeadlineClock CloudCommandDeadlineTests CloudTuiManualIOConnectionTests; do cp "$ROOT/cmuxTests/$name.swift" "$DEST/Tests/CloudCommandFixtureTests/" done From 1edb2c1dcc289bd01d24e653f534cf2c2286d2e8 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Thu, 24 Sep 2026 00:29:29 -0700 Subject: [PATCH 2/8] Justify the three static namespaces that moved into the catalog package The iOS/package conventions lint rejects public all-static types in packages. CmuxTuiSnapshotParser, CloudWireNumber and CloudVMEventFeedRecoveryDecision were internal static namespaces in the app and moved unchanged; reshaping them is a separate change, so each carries the lint's inline lint:allow justification. Co-Authored-By: Claude Opus 5.5 --- .../Sources/CmuxSurfaceCatalogModel/CmuxTuiSnapshotParser.swift | 1 + .../Sources/CmuxSurfaceCatalogModel/SurfaceCatalogModel.swift | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CmuxTuiSnapshotParser.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CmuxTuiSnapshotParser.swift index 11eeb0d562d9..19e64d121f9b 100644 --- a/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CmuxTuiSnapshotParser.swift +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CmuxTuiSnapshotParser.swift @@ -38,6 +38,7 @@ public struct CloudVMStateDeltaApplication: Sendable { } } +/// lint:allow namespace-type — moved unchanged from the app target, where it was an internal static namespace; reshaping the parser's many static call sites is a separate change from this package move. public struct CmuxTuiSnapshotParser: Sendable { /// Chooses a stable destination for projecting a terminal that currently has no remote /// tab view. The session snapshot lists structural records separately, so selection walks diff --git a/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceCatalogModel.swift b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceCatalogModel.swift index ca94866bbe53..43c69a9d8597 100644 --- a/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceCatalogModel.swift +++ b/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/SurfaceCatalogModel.swift @@ -157,6 +157,7 @@ public struct CloudVMCursor: Hashable, Codable, Sendable { /// paths. Coercing that value with intValue would turn `true`, fractions, and /// overflowing values into a different cursor or index, which can make a delta /// look contiguous when it is not. +/// lint:allow namespace-type — moved unchanged from the app target, where it was an internal static namespace; reshaping it is a separate change from this package move. public enum CloudWireNumber { public static func unsigned(_ raw: Any?) -> UInt64? { // A JSON number decodes as NSNumber, and `NSNumber(0) is Bool` is true, @@ -1626,6 +1627,7 @@ public struct CloudVMPendingMutation: Hashable, Codable, Sendable { /// A snapshot repairs the document, but it does not by itself repair the live /// event feed. Keep an existing transport warning until the versioned feed is /// running again, so an agent never mistakes a point-in-time read for live sync. +/// lint:allow namespace-type — moved unchanged from the app target, where it was an internal static namespace; reshaping it is a separate change from this package move. public enum CloudVMEventFeedRecoveryDecision { public static func shouldClearWarning( snapshotCursor: CloudVMCursor?, From 4519d365a04f30784959d4c52aa7edd766a7b2bb Mon Sep 17 00:00:00 2001 From: Leo Li Date: Thu, 24 Sep 2026 05:15:32 -0400 Subject: [PATCH 3/8] refactor: move the cloud tui transport into a CmuxCloudTui package Next slice of the package-extraction RFC (#13108), picked by edit frequency: Sources/Cloud gets about a quarter of 30-day app source touches, and every PR compile recompiles the whole app target, so moving hot Cloud code out of the target is what shortens edit-build loops. Twenty files move from Sources/Cloud to Packages/macOS/CmuxCloudTui: the cmux-tui request and argv builders, the manual-IO socket (frame, decoder, command, connection, descriptor lease, grid, input router, resize scheduler), the manual-mirror deadlines, phase, request kind and watchdog, the remote colors document, the resolved-surface result and the client paths. The package depends on CmuxSurfaceCatalogModel (from #13135, which this branch is stacked on), CmuxTerminal, CmuxCloudImagePaste and CmuxFoundation; no app imports. Staying in the app: CloudTuiManualMirrorSession (binds a TerminalSurface) and the three files that speak CloudMachineLink.LinkError (CommandRunning, DaemonAnswer, PersistentResourceConnection). Besides the move: declarations become public, four structs that relied on the synthesized initializer get an explicit public init, and the resize scheduler's private(set) state is publicly readable. 62 app and test files gain a top-level `import CmuxCloudTui`. The cloud command deadline fixture, the CLI hostname test and the deadline workflow's path filter follow the files; CI's package list gains CmuxCloudTui. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci-macos.yml | 1 + .github/workflows/cloud-command-deadlines.yml | 5 +- Packages/macOS/CmuxCloudTui/Package.swift | 23 ++++ Packages/macOS/CmuxCloudTui/README.md | 27 +++++ .../CmuxCloudTui}/CloudTuiClientPaths.swift | 41 +++---- .../CloudTuiCommandLine+Placement.swift | 2 +- .../CmuxCloudTui}/CloudTuiCommandLine.swift | 70 ++++++------ .../CloudTuiLegacySnapshotParser.swift | 18 ++-- .../CloudTuiManualIOCommand.swift | 38 +++---- .../CloudTuiManualIOConnection.swift | 14 +-- .../CloudTuiManualIODescriptorLease.swift | 12 +-- .../CmuxCloudTui}/CloudTuiManualIOFrame.swift | 2 +- .../CloudTuiManualIOFrameDecoder.swift | 6 +- .../CloudTuiManualIOGrid+SizingSample.swift | 2 +- .../CmuxCloudTui}/CloudTuiManualIOGrid.swift | 10 +- .../CloudTuiManualIOInputRouter.swift | 14 +-- .../CloudTuiManualIOResizeScheduler.swift | 22 ++-- .../CloudTuiManualMirrorDeadlines.swift | 12 +-- .../CloudTuiManualMirrorPhase.swift | 2 +- .../CloudTuiManualMirrorRequestKind.swift | 2 +- .../CloudTuiManualMirrorWatchdog.swift | 14 +-- .../CloudTuiPersistentRequestBuilder.swift | 70 ++++++------ .../CmuxCloudTui}/CloudTuiRemoteColors.swift | 20 ++-- .../CloudTuiResolvedSurface.swift | 2 +- .../CloudTuiPackageSurfaceTests.swift | 35 ++++++ Sources/AppDelegate.swift | 1 + Sources/Cloud/CloudGuestURLService.swift | 1 + Sources/Cloud/CloudMachineLink.swift | 1 + Sources/Cloud/CloudMachineLinkManager.swift | 1 + .../Cloud/CloudManualMirrorPresentation.swift | 1 + .../Cloud/CloudTerminalAttachmentLog.swift | 1 + ...TerminalConnectionPresentationPolicy.swift | 1 + .../Cloud/CloudTerminalCreationRequest.swift | 1 + Sources/Cloud/CloudTuiCommandRunning.swift | 1 + .../Cloud/CloudTuiManualMirrorSession.swift | 1 + ...CloudTuiPersistentResourceConnection.swift | 1 + Sources/Cloud/CloudWireGuardHub.swift | 1 + Sources/Cloud/VMClientSocketCommands.swift | 1 + Sources/Cloud/VMTunnelManager.swift | 1 + Sources/RemoteTui/RemoteTuiMachine.swift | 1 + Sources/RemoteTui/SSHTuiConnection.swift | 1 + Sources/RemoteTui/SSHTuiLinkManager.swift | 1 + .../SSHTuiWorkspaceCoordinator.swift | 1 + Sources/Surfaces/CloudAgentNameContext.swift | 1 + .../CloudAttachmentReconcileDecision.swift | 1 + .../CloudTerminalAttachmentResolver.swift | 1 + .../CloudTerminalLayoutCreation.swift | 1 + .../CloudTerminalMutationCommandRunner.swift | 1 + .../CloudTerminalPaneReservation.swift | 1 + ...CmuxTuiSurfaceProvider+CloseTerminal.swift | 1 + .../CmuxTuiSurfaceProvider+Environment.swift | 1 + .../CmuxTuiSurfaceProvider+FileDelivery.swift | 1 + .../CmuxTuiSurfaceProvider+GuestURL.swift | 1 + .../CmuxTuiSurfaceProvider+ManualMirror.swift | 1 + ...CmuxTuiSurfaceProvider+Notifications.swift | 1 + ...CmuxTuiSurfaceProvider+PlacementSync.swift | 1 + ...xTuiSurfaceProvider+TerminalCreation.swift | 1 + .../CmuxTuiSurfaceProvider+TerminalIO.swift | 1 + ...uiSurfaceProvider+TerminalPrimitives.swift | 1 + ...uiSurfaceProvider+WorkspaceLifecycle.swift | 1 + ...uiSurfaceProviderRegistry+Production.swift | 1 + .../Surfaces/CmuxTuiSurfaceProviders.swift | 1 + cmux.xcodeproj/project.pbxproj | 101 ++++-------------- cmux.xcworkspace/contents.xcworkspacedata | 3 + ...loudAttachmentReconcileDecisionTests.swift | 1 + cmuxTests/CloudCommandDeadlineTests.swift | 1 + cmuxTests/CloudCommandOperationTests.swift | 1 + cmuxTests/CloudEnvDeliveryTests.swift | 1 + ...loudImagePasteMirrorIntegrationTests.swift | 1 + .../CloudManualMirrorTransportTests.swift | 1 + cmuxTests/CloudNotificationSyncTests.swift | 1 + .../CloudPlacementCoordinatorTests.swift | 1 + cmuxTests/CloudPortOpenRegressionTests.swift | 1 + .../CloudPrivateRouteSelectionTests.swift | 1 + .../CloudRemoteColorOwnershipTests.swift | 1 + ...CloudTerminalAttachmentRecoveryTests.swift | 1 + ...nalConnectionPresentationPolicyTests.swift | 1 + .../CloudTerminalCreationRequestTests.swift | 1 + .../CloudTerminalLayoutCreationTests.swift | 1 + .../CloudTerminalMutationLifecycleTests.swift | 1 + .../CloudTerminalMutationSocketRunner.swift | 1 + .../CloudTerminalMutationSocketTests.swift | 1 + .../CloudTerminalMutationTestTransport.swift | 1 + .../CloudTerminalPaneReservationTests.swift | 1 + .../CloudTuiManualIOConnectionTests.swift | 1 + ...urfaceProviderRegistryDiscoveryTests.swift | 1 + ...urfaceProviderTests+LinkCancellation.swift | 1 + cmuxTests/CmuxTuiSurfaceProviderTests.swift | 1 + cmuxTests/SurfacePaneFactoryFocusTests.swift | 1 + tests/run_cloud_command_deadline_tests.sh | 16 ++- tests/test_cli_cloud_hostname.py | 2 +- 91 files changed, 379 insertions(+), 269 deletions(-) create mode 100644 Packages/macOS/CmuxCloudTui/Package.swift create mode 100644 Packages/macOS/CmuxCloudTui/README.md rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiClientPaths.swift (80%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiCommandLine+Placement.swift (96%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiCommandLine.swift (83%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiLegacySnapshotParser.swift (92%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiManualIOCommand.swift (84%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiManualIOConnection.swift (98%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiManualIODescriptorLease.swift (81%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiManualIOFrame.swift (95%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiManualIOFrameDecoder.swift (97%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiManualIOGrid+SizingSample.swift (98%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiManualIOGrid.swift (91%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiManualIOInputRouter.swift (95%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiManualIOResizeScheduler.swift (79%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiManualMirrorDeadlines.swift (78%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiManualMirrorPhase.swift (70%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiManualMirrorRequestKind.swift (88%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiManualMirrorWatchdog.swift (89%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiPersistentRequestBuilder.swift (54%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiRemoteColors.swift (89%) rename {Sources/Cloud => Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui}/CloudTuiResolvedSurface.swift (86%) create mode 100644 Packages/macOS/CmuxCloudTui/Tests/CmuxCloudTuiTests/CloudTuiPackageSurfaceTests.swift diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index b75cbd27b94d..f779876d1d3f 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -2410,6 +2410,7 @@ jobs: CmuxBrowser CmuxCanvasUI CmuxCloudMachines + CmuxCloudTui CmuxComputerUse CmuxCore CmuxRemoteDaemon diff --git a/.github/workflows/cloud-command-deadlines.yml b/.github/workflows/cloud-command-deadlines.yml index d701ab8b90d3..e6d9ba75fc33 100644 --- a/.github/workflows/cloud-command-deadlines.yml +++ b/.github/workflows/cloud-command-deadlines.yml @@ -3,9 +3,10 @@ on: pull_request: paths: - Sources/Cloud/CloudTuiPersistent*.swift - - Sources/Cloud/CloudTuiManualIO*.swift + - Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIO*.swift + - Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiPersistentRequestBuilder.swift - Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudTuiTerminalProjectionTarget.swift - - Sources/Cloud/CloudTuiRemoteColors.swift + - Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiRemoteColors.swift - cmuxTests/CloudCommand*.swift - cmuxTests/CloudTuiManualIOConnectionTests.swift - tests/fixtures/cloud-command-deadlines/** diff --git a/Packages/macOS/CmuxCloudTui/Package.swift b/Packages/macOS/CmuxCloudTui/Package.swift new file mode 100644 index 000000000000..dd33febbe97d --- /dev/null +++ b/Packages/macOS/CmuxCloudTui/Package.swift @@ -0,0 +1,23 @@ +// swift-tools-version: 6.0 +import PackageDescription + +let package = Package( + name: "CmuxCloudTui", + platforms: [.macOS(.v14)], + products: [.library(name: "CmuxCloudTui", targets: ["CmuxCloudTui"])], + dependencies: [ + .package(path: "../CmuxCloudImagePaste"), + .package(path: "../CmuxFoundation"), + .package(path: "../CmuxSurfaceCatalogModel"), + .package(path: "../CmuxTerminal") + ], + targets: [ + .target( + name: "CmuxCloudTui", + dependencies: ["CmuxCloudImagePaste", "CmuxFoundation", "CmuxSurfaceCatalogModel", "CmuxTerminal"], + // The files moved out of the app target unchanged; keep its language mode. + swiftSettings: [.swiftLanguageMode(.v5)] + ), + .testTarget(name: "CmuxCloudTuiTests", dependencies: ["CmuxCloudTui"]) + ] +) diff --git a/Packages/macOS/CmuxCloudTui/README.md b/Packages/macOS/CmuxCloudTui/README.md new file mode 100644 index 000000000000..1af4f8fc33c2 --- /dev/null +++ b/Packages/macOS/CmuxCloudTui/README.md @@ -0,0 +1,27 @@ +# CmuxCloudTui + +The cmux-tui client transport for cloud terminals: request and argv builders for the daemon, the manual-IO socket (framing, decoding, input routing, resize coalescing), the manual-mirror deadlines and watchdog, the remote color document, and the on-disk client paths. No app imports; depends on `CmuxSurfaceCatalogModel` (cursor and projection targets), `CmuxTerminal` (raw sizing samples and manual input), `CmuxCloudImagePaste` and `CmuxFoundation`. + +```swift +import CmuxCloudTui + +var scheduler = CloudTuiManualIOResizeScheduler() +if let grid = CloudTuiManualIOGrid(columns: 120, rows: 40), + let send = scheduler.sample(grid, canSend: true) { + connection.send(CloudTuiManualIOCommand().resize(surfaceID: id, columns: send.columns, rows: send.rows)) +} +``` + +## Moved files + +From `Sources/Cloud/`: `CloudTuiClientPaths.swift`, `CloudTuiCommandLine.swift`, `CloudTuiCommandLine+Placement.swift`, `CloudTuiLegacySnapshotParser.swift`, `CloudTuiManualIOCommand.swift`, `CloudTuiManualIOConnection.swift`, `CloudTuiManualIODescriptorLease.swift`, `CloudTuiManualIOFrame.swift`, `CloudTuiManualIOFrameDecoder.swift`, `CloudTuiManualIOGrid.swift`, `CloudTuiManualIOGrid+SizingSample.swift`, `CloudTuiManualIOInputRouter.swift`, `CloudTuiManualIOResizeScheduler.swift`, `CloudTuiManualMirrorDeadlines.swift`, `CloudTuiManualMirrorPhase.swift`, `CloudTuiManualMirrorRequestKind.swift`, `CloudTuiManualMirrorWatchdog.swift`, `CloudTuiPersistentRequestBuilder.swift`, `CloudTuiRemoteColors.swift`, `CloudTuiResolvedSurface.swift`. + +## What stays in the app + +- `CloudTuiManualMirrorSession` (and `+Capabilities`): it binds a `TerminalSurface`. +- `CloudTuiCommandRunning`, `CloudTuiDaemonAnswer`, `CloudTuiPersistentResourceConnection`: they speak `CloudMachineLink.LinkError`, which is nested in the app's `CloudMachineLink`. Moving `LinkError` out is the next step for them. + +## What changed besides the move + +- Declarations became `public`. Structs that relied on the synthesized initializer (`CloudTuiClientPaths.DeviceRecord`, `CloudTuiLegacySnapshotParser`, `CloudTuiManualIOFrameDecoder`, `CloudTuiManualIOResizeScheduler`) got an explicit `public init` with the same parameters. +- `CloudTuiManualIOResizeScheduler`'s `private(set)` state is publicly readable, as the app's tests read it. diff --git a/Sources/Cloud/CloudTuiClientPaths.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiClientPaths.swift similarity index 80% rename from Sources/Cloud/CloudTuiClientPaths.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiClientPaths.swift index 74718bd82558..b7389f61a977 100644 --- a/Sources/Cloud/CloudTuiClientPaths.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiClientPaths.swift @@ -8,11 +8,16 @@ import Security /// `cmux vm-tui-connect` must present the same device to a machine's daemon, so one /// enrollment covers both. The CLI helpers are compiled only into the CLI target, hence /// the duplicate paths here. -struct CloudTuiClientPaths: Sendable { +public struct CloudTuiClientPaths: Sendable { /// One enrolled device per machine, as the CLI stores it (`vm-tui-devices.json`). - struct DeviceRecord: Codable, Sendable, Equatable { - let deviceFingerprint: String - let updatedAtUnix: Int + public struct DeviceRecord: Codable, Sendable, Equatable { + public let deviceFingerprint: String + public let updatedAtUnix: Int + + public init(deviceFingerprint: String, updatedAtUnix: Int) { + self.deviceFingerprint = deviceFingerprint + self.updatedAtUnix = updatedAtUnix + } } /// Stored in place of a device fingerprint for a machine this Mac reaches over @@ -20,26 +25,26 @@ struct CloudTuiClientPaths: Sendable { /// dials `--carrier` again with no control-plane call. A real fingerprint means /// the Mac enrolled before the machine's daemon served the trusted listener and /// keeps presenting its stored key. Mirrored by the CLI's own store. - static let carrierDeviceMarker = "carrier" + public static let carrierDeviceMarker = "carrier" - let home: URL + public let home: URL - init(home: URL = URL(fileURLWithPath: NSHomeDirectory(), isDirectory: true)) { + public init(home: URL = URL(fileURLWithPath: NSHomeDirectory(), isDirectory: true)) { self.home = home } /// Per-Mac cmux-tui client state (device key, known daemons). - var stateDir: URL { + public var stateDir: URL { home.appendingPathComponent(".cmuxterm", isDirectory: true) .appendingPathComponent("cmux-tui-client", isDirectory: true) } - var devicesStoreURL: URL { + public var devicesStoreURL: URL { home.appendingPathComponent(".cmuxterm", isDirectory: true) .appendingPathComponent("vm-tui-devices.json", isDirectory: false) } - func ensureStateDir() throws { + public func ensureStateDir() throws { try FileManager.default.createDirectory( at: stateDir, withIntermediateDirectories: true, @@ -47,7 +52,7 @@ struct CloudTuiClientPaths: Sendable { ) } - func loadDevices() -> [String: DeviceRecord] { + public func loadDevices() -> [String: DeviceRecord] { guard let data = try? Data(contentsOf: devicesStoreURL), let store = try? JSONDecoder().decode([String: DeviceRecord].self, from: data) else { return [:] @@ -59,11 +64,11 @@ struct CloudTuiClientPaths: Sendable { /// one per install: `mac-` plus 32 hex digits, minted on first use and kept in /// the client state dir beside the device key. Every machine sees the same id, /// so per-client read state follows the install, not the machine. - var notificationClientIDURL: URL { + public var notificationClientIDURL: URL { stateDir.appendingPathComponent("notification-client-id", isDirectory: false) } - func notificationClientID() -> String { + public func notificationClientID() -> String { if let existing = try? String(contentsOf: notificationClientIDURL, encoding: .utf8) { let trimmed = existing.trimmingCharacters(in: .whitespacesAndNewlines) if Self.isValidNotificationClientID(trimmed) { return trimmed } @@ -78,16 +83,16 @@ struct CloudTuiClientPaths: Sendable { } /// The daemon's rule: 1 to 128 printable ASCII bytes, no spaces. - static func isValidNotificationClientID(_ value: String) -> Bool { + public static func isValidNotificationClientID(_ value: String) -> Bool { !value.isEmpty && value.utf8.count <= 128 && value.utf8.allSatisfy { $0 > 0x20 && $0 < 0x7f } } - func deviceFingerprint(for machineID: String) -> String? { + public func deviceFingerprint(for machineID: String) -> String? { loadDevices()[machineID]?.deviceFingerprint } - func saveDeviceFingerprint(_ fingerprint: String, for machineID: String) { + public func saveDeviceFingerprint(_ fingerprint: String, for machineID: String) { var store = loadDevices() store[machineID] = DeviceRecord(deviceFingerprint: fingerprint, updatedAtUnix: Int(Date().timeIntervalSince1970)) guard let data = try? JSONEncoder().encode(store) else { return } @@ -97,14 +102,14 @@ struct CloudTuiClientPaths: Sendable { } /// The app and CLI share a local label that never needs hostname resolution. - static func deviceName(hostName: String? = nil) -> String { + public static func deviceName(hostName: String? = nil) -> String { (hostName.map { RemoteClientDeviceName(hostName: $0) } ?? RemoteClientDeviceName()).value } /// The cmux-tui client the app drives: the bundled one /// (`Contents/Resources/bin/cmux-tui`, installed by scripts/install-cmux-tui-client.sh), /// else `CMUX_TUI_CLIENT`. No PATH search: the app must not pick up a stray binary. - static func clientURL( + public static func clientURL( bundle: Bundle = .main, environment: [String: String] = ProcessInfo.processInfo.environment ) -> URL? { diff --git a/Sources/Cloud/CloudTuiCommandLine+Placement.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiCommandLine+Placement.swift similarity index 96% rename from Sources/Cloud/CloudTuiCommandLine+Placement.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiCommandLine+Placement.swift index 02568c533590..449f8d5b1114 100644 --- a/Sources/Cloud/CloudTuiCommandLine+Placement.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiCommandLine+Placement.swift @@ -5,7 +5,7 @@ extension CloudTuiCommandLine { /// `tab move --workspace --screen --pane --index ` /// (spec `tab.move`, the destination grammar `terminal … project` uses): re-parents /// one placement; the terminal or browser behind it is untouched. - static func moveTabArguments( + public static func moveTabArguments( socketPath: String, tabID: String, target: CloudTuiTerminalProjectionTarget, diff --git a/Sources/Cloud/CloudTuiCommandLine.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiCommandLine.swift similarity index 83% rename from Sources/Cloud/CloudTuiCommandLine.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiCommandLine.swift index bb89b6464341..42086a1187a8 100644 --- a/Sources/Cloud/CloudTuiCommandLine.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiCommandLine.swift @@ -6,7 +6,7 @@ import Foundation /// `cmux-tui/spec/cli.md`: `cmux [GLOBAL OPTIONS] [OPTIONS]`, with /// `--socket`/`--json`/`--jsonl` as global options, and `attach --terminal ` as the /// single-terminal renderer (`spec/cli.md` §"attach"). -struct CloudTuiCommandLine: Sendable { +public struct CloudTuiCommandLine: Sendable { /// `remote connect --device-name … --state-dir … --headless --json [--carrier]`: /// a headless link whose stdout carries `connection-snapshot` JSON lines with the /// local mux socket path (`remote_cli.rs` `connect_with_flags`). @@ -17,7 +17,7 @@ struct CloudTuiCommandLine: Sendable { /// `--wireguard-hub ` makes the client dial the route through the app's /// in-process WireGuard hub (``CloudWireGuardHub``) instead of the OS network stack; /// it is added only for routes inside the private Cloud VM network. - static func linkArguments(route: String, deviceName: String, stateDir: String, carrier: Bool = false, wireguardHubSocket: String? = nil) -> [String] { + public static func linkArguments(route: String, deviceName: String, stateDir: String, carrier: Bool = false, wireguardHubSocket: String? = nil) -> [String] { var arguments = [ "remote", "connect", route, "--device-name", deviceName, @@ -35,16 +35,16 @@ struct CloudTuiCommandLine: Sendable { /// `wg hub --config --socket `: the one process that owns the /// app's WireGuard tunnel and serves SOCKS5 to every link on this Mac. - static func wireGuardHubArguments(configPath: String, socketPath: String) -> [String] { + public static func wireGuardHubArguments(configPath: String, socketPath: String) -> [String] { ["wg", "hub", "--config", configPath, "--socket", socketPath, "--exit-with-parent"] } /// The probe capability a client advertises when it understands `--wireguard-hub`. - static let wireGuardHubCapability = "wireguard-hub" + public static let wireGuardHubCapability = "wireguard-hub" /// Private addresses are browser identities; the daemon opens each requested port on its loopback /// after the authenticated CONNECT proxy or Cloud WebSocket bridge accepts the browser connection. - static func browserProxyArguments(route: String, addresses: [String], stateDir: String, wireGuardHubSocket: String, carrier: Bool) -> [String] { + public static func browserProxyArguments(route: String, addresses: [String], stateDir: String, wireGuardHubSocket: String, carrier: Bool) -> [String] { var args = ["remote", "browser-proxy", route, "--workspace-root", "/", "--state-dir", stateDir, "--wireguard-hub", wireGuardHubSocket, "--exit-with-parent"] for address in addresses { args += ["--allowed-host", address] } @@ -53,14 +53,14 @@ struct CloudTuiCommandLine: Sendable { } /// Whole-session public snapshot (`session current snapshot`, `--json`). - static func snapshotArguments(socketPath: String) -> [String] { + public static func snapshotArguments(socketPath: String) -> [String] { ["--socket", socketPath, "--json", "session", "current", "snapshot"] } /// Live delta stream (`session current events`, `--jsonl`). A cursor lets a /// restarted reader resume from the last accepted revision instead of /// creating a blind polling gap. - static func eventsArguments(socketPath: String, cursor: CloudVMCursor? = nil) -> [String] { + public static func eventsArguments(socketPath: String, cursor: CloudVMCursor? = nil) -> [String] { var arguments = ["--socket", socketPath, "--jsonl", "session", "current", "events"] if let cursor { arguments += ["--generation", cursor.generation, "--revision", String(cursor.revision)] @@ -71,7 +71,7 @@ struct CloudTuiCommandLine: Sendable { /// `workspace run -- `: a new terminal in that cmux-tui workspace /// running the exact argv. Result: `MutationResult` /// (`spec/resource-operations-v2.json` → `workspace.run`). - static func runArguments(socketPath: String, workspaceID: String, command: [String], onExit: String? = nil, idempotencyKey: String? = nil, correlationKey: String? = nil) -> [String] { + public static func runArguments(socketPath: String, workspaceID: String, command: [String], onExit: String? = nil, idempotencyKey: String? = nil, correlationKey: String? = nil) -> [String] { var arguments = ["--socket", socketPath, "--json", "workspace", workspaceID, "run"] if let idempotencyKey { arguments += ["--idempotency-key", idempotencyKey] } if let correlationKey { arguments += ["--correlation-key", correlationKey] } @@ -83,7 +83,7 @@ struct CloudTuiCommandLine: Sendable { } /// `workspace create [--name ]`: the daemon owns auto-naming. - static func createWorkspaceArguments(socketPath: String, name: String? = nil, empty: Bool = false) -> [String] { + public static func createWorkspaceArguments(socketPath: String, name: String? = nil, empty: Bool = false) -> [String] { var arguments = ["--socket", socketPath, "--json", "workspace", "create"] if let name, !name.isEmpty { arguments += ["--name", name] @@ -93,20 +93,20 @@ struct CloudTuiCommandLine: Sendable { } /// `terminal close`: end that remote terminal (spec `terminal.close`). - static func closeTerminalArguments(socketPath: String, terminalID: String) -> [String] { + public static func closeTerminalArguments(socketPath: String, terminalID: String) -> [String] { ["--socket", socketPath, "--json", "terminal", terminalID, "close"] } /// `tab close`: drop the tab that held a terminal whose process already /// exited — cmux-tui no longer resolves such a terminal by its own selector. - static func closeTabArguments(socketPath: String, tabID: String) -> [String] { + public static func closeTabArguments(socketPath: String, tabID: String) -> [String] { ["--socket", socketPath, "--json", "tab", tabID, "close"] } /// `workspace close`: remove the workspace view. Its terminals detach /// (alive, zero views) rather than die (`spec/cli.md`) — close them first for /// a full delete. - static func closeWorkspaceArguments(socketPath: String, workspaceID: String) -> [String] { + public static func closeWorkspaceArguments(socketPath: String, workspaceID: String) -> [String] { ["--socket", socketPath, "--json", "workspace", workspaceID, "close"] } @@ -115,7 +115,7 @@ struct CloudTuiCommandLine: Sendable { /// operation is deliberately separate from the native pane destination: the remote view /// only makes the daemon's process-local surface attachable; local rendering remains in /// Ghostty. - static func projectTerminalArguments( + public static func projectTerminalArguments( socketPath: String, terminalID: String, target: CloudTuiTerminalProjectionTarget, @@ -140,7 +140,7 @@ struct CloudTuiCommandLine: Sendable { /// `workspace rename --name ` (verified live: the positional /// form is `usage.invalid`; the name rides the `--name` flag). - static func renameWorkspaceArguments( + public static func renameWorkspaceArguments( socketPath: String, workspaceID: String, name: String, @@ -156,7 +156,7 @@ struct CloudTuiCommandLine: Sendable { /// records this Mac's reads on the machine. The idempotency key is minted once /// per batch by the sync and reused on every retry, so a retried ack replays the /// committed result instead of a second revision. - static func notificationAckArguments( + public static func notificationAckArguments( socketPath: String, clientID: String, notificationIDs: [String], @@ -168,33 +168,33 @@ struct CloudTuiCommandLine: Sendable { /// `terminal write --text ` (spec `terminal.input.write`): the bytes /// land on the PTY as typed; no newline is added, send `keys enter` for that. - static func writeArguments(socketPath: String, terminalID: String, text: String) -> [String] { + public static func writeArguments(socketPath: String, terminalID: String, text: String) -> [String] { ["--socket", socketPath, "--json", "terminal", terminalID, "write", "--text", text] } /// `terminal write` reads the UTF-8 receiver wire from stdin. /// Keeping payloads out of argv prevents local process inspection from /// exposing file or environment secrets before they enter the link. - static func writeBytesArguments(socketPath: String, terminalID: String) -> [String] { + public static func writeBytesArguments(socketPath: String, terminalID: String) -> [String] { ["--socket", socketPath, "--json", "terminal", terminalID, "write"] } /// `terminal keys …` (spec `terminal.input.keys`): named keys such as /// `enter`, `tab`, `escape`, `up`, and `+`-joined chords such as `ctrl+c` (verified /// live; `ctrl-c` is `validation.invalid`). The daemon rejects empty names. - static func keysArguments(socketPath: String, terminalID: String, keys: [String]) -> [String] { + public static func keysArguments(socketPath: String, terminalID: String, keys: [String]) -> [String] { ["--socket", socketPath, "--json", "terminal", terminalID, "keys"] + keys } /// `terminal screen read` (spec `terminal.screen.read`): the visible grid as /// `{cols, rows, cursor_row, cursor_col, cursor_visible, text}`. - static func screenReadArguments(socketPath: String, terminalID: String) -> [String] { + public static func screenReadArguments(socketPath: String, terminalID: String) -> [String] { ["--socket", socketPath, "--json", "terminal", terminalID, "screen", "read"] } /// `terminal screen wait --pattern [--timeout-ms ]` (spec /// `terminal.wait`): blocks until the screen matches, `{matched, text}`. - static func screenWaitArguments(socketPath: String, terminalID: String, pattern: String, timeoutMs: Int?) -> [String] { + public static func screenWaitArguments(socketPath: String, terminalID: String, pattern: String, timeoutMs: Int?) -> [String] { var arguments = ["--socket", socketPath, "--json", "terminal", terminalID, "screen", "wait", "--pattern", pattern] if let timeoutMs, timeoutMs > 0 { arguments += ["--timeout-ms", String(timeoutMs)] @@ -206,7 +206,7 @@ struct CloudTuiCommandLine: Sendable { /// until the terminal's process exits or the timeout elapses — /// `{state: "exited", outcome: {kind: exit|signal|unknown, …}, exited_at}` or `{state: "pending", …}`. /// The complement of `screen wait`: an exit is a fact, a prompt regex is a guess. - static func processWaitArguments(socketPath: String, terminalID: String, timeoutMs: Int?) -> [String] { + public static func processWaitArguments(socketPath: String, terminalID: String, timeoutMs: Int?) -> [String] { var arguments = ["--socket", socketPath, "--json", "terminal", terminalID, "process", "wait"] if let timeoutMs, timeoutMs > 0 { arguments += ["--timeout-ms", String(timeoutMs)] @@ -216,13 +216,13 @@ struct CloudTuiCommandLine: Sendable { /// `terminal process show` (spec `terminal.process.get`): reads /// the foreground process cwd live from the PTY's controlling terminal. - static func processInfoArguments(socketPath: String, terminalID: String) -> [String] { + public static func processInfoArguments(socketPath: String, terminalID: String) -> [String] { ["--socket", socketPath, "--json", "terminal", terminalID, "process", "show"] } /// Extracts the live foreground cwd. The sibling `cwd` field is the spawn /// directory and is stale after the shell changes directory. - static func foregroundWorkingDirectory(fromProcessInfo result: [String: Any]) -> String? { + public static func foregroundWorkingDirectory(fromProcessInfo result: [String: Any]) -> String? { guard let cwd = result["foreground_cwd"] as? String else { return nil } let trimmed = cwd.trimmingCharacters(in: .whitespacesAndNewlines) return trimmed.isEmpty ? nil : trimmed @@ -232,7 +232,7 @@ struct CloudTuiCommandLine: Sendable { /// `terminal.output_read`): the terminal's retained OUTPUT as text — the whole build log, /// not the 24 rows currently on screen — with `{text, start_offset, next_offset, complete}`; /// `next_offset` fed back as `after` reads only what arrived since. - static func outputReadArguments(socketPath: String, terminalID: String, after: Int?, maxBytes: Int?) -> [String] { + public static func outputReadArguments(socketPath: String, terminalID: String, after: Int?, maxBytes: Int?) -> [String] { var arguments = ["--socket", socketPath, "--json", "terminal", terminalID, "output", "read"] if let after, after >= 0 { arguments += ["--after", String(after)] @@ -247,7 +247,7 @@ struct CloudTuiCommandLine: Sendable { /// view of a terminal (spec `tab.rename`). The daemon persists it in its /// registry and broadcasts `tab-renamed`, so every attached client sees it. /// The empty string is the protocol's explicit clear value. - static func renameTabArguments( + public static func renameTabArguments( socketPath: String, tabID: String, name: String, @@ -260,7 +260,7 @@ struct CloudTuiCommandLine: Sendable { } /// `attach --terminal `: render exactly one remote terminal into this tty. - static func attachArguments(socketPath: String, terminalID: String) -> [String] { + public static func attachArguments(socketPath: String, terminalID: String) -> [String] { ["--socket", socketPath, "attach", "--terminal", terminalID] } @@ -274,7 +274,7 @@ struct CloudTuiCommandLine: Sendable { /// as a resource scope and rejects it with `unknown resource scope /// "list-workspaces"`, so the tree was unreachable from the CLI even though /// the daemon still serves the command over the wire. - static func legacyListWorkspacesArguments(socketPath: String) -> [String] { + public static func legacyListWorkspacesArguments(socketPath: String) -> [String] { // A fixed literal, so this cannot fail to encode and the request stays // byte-stable across runs. [ @@ -292,7 +292,7 @@ struct CloudTuiCommandLine: Sendable { /// its registry; a daemon that only knows UUIDv4 host ids rejects both /// spellings the same way (`invalid_terminal_id`), and the resolver then /// reads the authoritative snapshot instead. - static func resolveTerminalArguments(socketPath: String, terminalID: String) -> [String]? { + public static func resolveTerminalArguments(socketPath: String, terminalID: String) -> [String]? { let payload = terminalID.hasPrefix("term_") ? String(terminalID.dropFirst("term_".count)) : terminalID @@ -312,7 +312,7 @@ struct CloudTuiCommandLine: Sendable { /// Returns the raw `identify` command used to negotiate the daemon protocol /// before selecting a compatibility-only resolver path. - static func identifyArguments(socketPath: String) -> [String]? { + public static func identifyArguments(socketPath: String) -> [String]? { rawCommandArguments( socketPath: socketPath, request: ["id": 1, "cmd": "identify"] @@ -322,7 +322,7 @@ struct CloudTuiCommandLine: Sendable { /// Lists the VM host's listening TCP sockets through the authenticated /// cmux-tui link. This is part of the private data path, not VM provider /// exec or the web control plane. - static func listeningPortsArguments(socketPath: String) -> [String]? { + public static func listeningPortsArguments(socketPath: String) -> [String]? { [ "--socket", socketPath, "--json", "raw", "command", @@ -352,7 +352,7 @@ struct CloudTuiCommandLine: Sendable { /// Pushing this Mac's resolved Ghostty colors makes remote panes match the local /// theme. (The flat `set-default-colors` verb in spec/commands.md is the protocol /// name; the v2 resource CLI rejects it — verified live against a machine.) - static func setDefaultColorsArguments(socketPath: String, foreground: String?, background: String?) -> [String]? { + public static func setDefaultColorsArguments(socketPath: String, foreground: String?, background: String?) -> [String]? { var arguments = ["--socket", socketPath, "--json", "session", "current", "terminal", "defaults", "set"] if let foreground { arguments += ["--foreground", foreground] } if let background { arguments += ["--background", background] } @@ -361,23 +361,23 @@ struct CloudTuiCommandLine: Sendable { /// The argv `vm.terminal_new` runs in the machine when the caller gives none: a login /// shell in the persistent home. - static let defaultTerminalCommand = ["bash", "-l"] + public static let defaultTerminalCommand = ["bash", "-l"] /// A `cwd` wraps the command so it starts there; the remote shell does the `cd`. - static func commandStartingIn(cwd: String?, command: [String]) -> [String] { + public static func commandStartingIn(cwd: String?, command: [String]) -> [String] { guard let cwd = cwd?.trimmingCharacters(in: .whitespacesAndNewlines), !cwd.isEmpty else { return command } let quoted = command.map(shellQuote).joined(separator: " ") return ["sh", "-lc", "cd \(shellQuote(cwd)) && exec \(quoted)"] } /// The pane's initial command for a local terminal showing one remote terminal. - static func attachShellCommand(clientPath: String, socketPath: String, terminalID: String) -> String { + public static func attachShellCommand(clientPath: String, socketPath: String, terminalID: String) -> String { ([clientPath] + attachArguments(socketPath: socketPath, terminalID: terminalID)) .map(shellQuote) .joined(separator: " ") } - static func shellQuote(_ value: String) -> String { + public static func shellQuote(_ value: String) -> String { if value.isEmpty { return "''" } if value.range(of: "^[A-Za-z0-9_./:@%+=,-]+$", options: .regularExpression) != nil { return value diff --git a/Sources/Cloud/CloudTuiLegacySnapshotParser.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiLegacySnapshotParser.swift similarity index 92% rename from Sources/Cloud/CloudTuiLegacySnapshotParser.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiLegacySnapshotParser.swift index a7eb99a0cecd..ee3b41970d50 100644 --- a/Sources/Cloud/CloudTuiLegacySnapshotParser.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiLegacySnapshotParser.swift @@ -7,11 +7,13 @@ import Foundation /// the legacy `list-workspaces` compatibility snapshot carries both identities /// at the terminal tab boundary. Keeping this translation here prevents the /// native pane from depending on the TUI's renderer or public-id internals. -struct CloudTuiLegacySnapshotParser: Sendable { +public struct CloudTuiLegacySnapshotParser: Sendable { + public init() {} + /// Finds a numeric surface in the result of the private /// `resolve-terminal` command. A null or malformed response returns nil; /// callers that need to distinguish those cases can use ``resolvedSurface(from:)``. - func resolvedSurfaceID(from data: Data) -> UInt64? { + public func resolvedSurfaceID(from data: Data) -> UInt64? { if case let .surface(surface) = resolvedSurface(from: data) { return surface } @@ -21,7 +23,7 @@ struct CloudTuiLegacySnapshotParser: Sendable { /// Decodes the resolver response without conflating a valid zero-view /// terminal (`surface:null`) with an exited terminal, or with malformed or /// failed data. - func resolvedSurface(from data: Data) -> CloudTuiResolvedSurface { + public func resolvedSurface(from data: Data) -> CloudTuiResolvedSurface { guard let root = try? JSONSerialization.jsonObject(with: data), let object = (root as? [String: Any])?["data"] as? [String: Any] ?? root as? [String: Any] else { @@ -42,7 +44,7 @@ struct CloudTuiLegacySnapshotParser: Sendable { /// Reads the protocol integer from an `identify` response envelope. A /// malformed, failed, fractional, or non-boolean response returns `nil` so /// a caller cannot infer compatibility from an unreliable value. - func protocolVersion(from data: Data) -> Int? { + public func protocolVersion(from data: Data) -> Int? { guard let root = try? JSONSerialization.jsonObject(with: data), let object = root as? [String: Any], positiveInteger(from: object["id"]) == 1, @@ -68,7 +70,7 @@ struct CloudTuiLegacySnapshotParser: Sendable { } /// Finds the numeric surface backing `terminalID` in a legacy tree payload. - func surfaceID(from data: Data, terminalID: String) -> UInt64? { + public func surfaceID(from data: Data, terminalID: String) -> UInt64? { guard let root = try? JSONSerialization.jsonObject(with: data), let object = (root as? [String: Any])?["data"] as? [String: Any] ?? root as? [String: Any] else { @@ -78,7 +80,7 @@ struct CloudTuiLegacySnapshotParser: Sendable { } /// Finds the numeric surface backing `terminalID` in an already-decoded tree. - func surfaceID(from object: [String: Any], terminalID: String) -> UInt64? { + public func surfaceID(from object: [String: Any], terminalID: String) -> UInt64? { surfaceIDs(from: object, terminalIDs: [terminalID])[terminalID] } @@ -88,7 +90,7 @@ struct CloudTuiLegacySnapshotParser: Sendable { /// the workspace/screen/pane/tab hierarchy once keeps the legacy fallback /// O(number of tree records + number of requested terminals), rather than /// rescanning the same tree for every pane. - func surfaceIDs( + public func surfaceIDs( from object: [String: Any], terminalIDs: Set ) -> [String: UInt64] { @@ -123,7 +125,7 @@ struct CloudTuiLegacySnapshotParser: Sendable { /// Resolves many terminal identities from a JSON or response-envelope /// payload in one tree walk. - func surfaceIDs(from data: Data, terminalIDs: Set) -> [String: UInt64] { + public func surfaceIDs(from data: Data, terminalIDs: Set) -> [String: UInt64] { guard let root = try? JSONSerialization.jsonObject(with: data), let object = (root as? [String: Any])?["data"] as? [String: Any] ?? root as? [String: Any] else { diff --git a/Sources/Cloud/CloudTuiManualIOCommand.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOCommand.swift similarity index 84% rename from Sources/Cloud/CloudTuiManualIOCommand.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOCommand.swift index 719a78320178..3ba3b1c1c21e 100644 --- a/Sources/Cloud/CloudTuiManualIOCommand.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOCommand.swift @@ -1,31 +1,31 @@ import Foundation /// Builds private transport commands for a native Cloud Ghostty byte mirror. -struct CloudTuiManualIOCommand: Sendable { +public struct CloudTuiManualIOCommand: Sendable { /// cmux-tui's terminal geometry clamp (the protocol's uint16 values are /// additionally bounded to keep pathological panes from exhausting the /// remote PTY). - let maximumGridDimension: Int + public let maximumGridDimension: Int /// Creates a command builder with the daemon's documented grid bound. /// /// - Parameter maximumGridDimension: Upper bound used when validating /// caller-provided cell dimensions. Tests may inject a smaller bound to /// exercise rejection without opening a socket. - init(maximumGridDimension: Int = 10_000) { + public init(maximumGridDimension: Int = 10_000) { self.maximumGridDimension = max(1, maximumGridDimension) } /// The capability understood by protocol-v9+ servers that returns a /// connection-owned lease for each byte attachment. - let viewAttachmentLeaseCapability = "view-attachment-lease-v1" + public let viewAttachmentLeaseCapability = "view-attachment-lease-v1" /// The capability understood by protocol-v9+ servers that allows a /// client to retire one attachment without dropping the whole socket. - let viewAttachmentDetachCapability = "view-attachment-detach-v1" + public let viewAttachmentDetachCapability = "view-attachment-detach-v1" /// Begins the protocol handshake so optional attach fields are sent only /// when the daemon advertises the matching capability. - func identify(requestID: UInt64 = 1) -> [String: Any] { + public func identify(requestID: UInt64 = 1) -> [String: Any] { [ "id": requestID, "cmd": "identify", @@ -35,7 +35,7 @@ struct CloudTuiManualIOCommand: Sendable { /// A round trip that proves the control connection is alive. Every daemon /// answers it; the watchdog sends it when an attached stream has carried /// no frame for a while. - func ping(requestID: UInt64 = 1) -> [String: Any] { + public func ping(requestID: UInt64 = 1) -> [String: Any] { [ "id": requestID, "cmd": "ping", @@ -45,7 +45,7 @@ struct CloudTuiManualIOCommand: Sendable { /// Advertises this connection as the native Ghostty mirror. The server /// only adds capabilities it recognizes, so sending these to an older /// daemon is safe and leaves the byte attach fallback available. - func setClientInfo( + public func setClientInfo( name: String, kind: String, requestID: UInt64 = 1 @@ -69,7 +69,7 @@ struct CloudTuiManualIOCommand: Sendable { /// requires a reported size before it can promote a client, and the /// explicit claim is what makes a native pane's grid authoritative rather /// than merely a passive viewport hint. - func claimGeometry(surfaceID: UInt64, requestID: UInt64 = 1) -> [String: Any] { + public func claimGeometry(surfaceID: UInt64, requestID: UInt64 = 1) -> [String: Any] { [ "id": requestID, "cmd": "set-client-sizing", @@ -80,7 +80,7 @@ struct CloudTuiManualIOCommand: Sendable { } /// Opens a byte attach stream for one numeric cmux-tui surface. - func attach( + public func attach( surfaceID: UInt64, columns: Int? = nil, rows: Int? = nil, @@ -107,7 +107,7 @@ struct CloudTuiManualIOCommand: Sendable { } /// Writes raw input bytes to the remote PTY. - func input(surfaceID: UInt64, bytes: Data, requestID: UInt64 = 1) -> [String: Any] { + public func input(surfaceID: UInt64, bytes: Data, requestID: UInt64 = 1) -> [String: Any] { [ "id": requestID, "cmd": "send", @@ -117,7 +117,7 @@ struct CloudTuiManualIOCommand: Sendable { } /// Sends one semantic key chord through the remote terminal's key encoder. - func namedKey(surfaceID: UInt64, key: String, requestID: UInt64 = 1) -> [String: Any] { + public func namedKey(surfaceID: UInt64, key: String, requestID: UInt64 = 1) -> [String: Any] { [ "id": requestID, "cmd": "send-key", @@ -127,7 +127,7 @@ struct CloudTuiManualIOCommand: Sendable { } /// Reports the native pane's current cell grid to the remote PTY. - func resize(surfaceID: UInt64, columns: Int, rows: Int, requestID: UInt64 = 1) -> [String: Any] { + public func resize(surfaceID: UInt64, columns: Int, rows: Int, requestID: UInt64 = 1) -> [String: Any] { [ "id": requestID, "cmd": "resize-surface", @@ -139,7 +139,7 @@ struct CloudTuiManualIOCommand: Sendable { /// Reports a grid for this exact leased attach stream. Lease fencing keeps a /// delayed resize from changing a replacement view after reconnect. - func resizeAttachedView( + public func resizeAttachedView( surfaceID: UInt64, lease: String, columns: Int, @@ -164,7 +164,7 @@ struct CloudTuiManualIOCommand: Sendable { /// Releases this connection's terminal-size report while the native pane /// is hidden. The remote PTY keeps its last authoritative grid frozen /// until a visible client claims it again. - func releaseSizing(surfaceID: UInt64, requestID: UInt64 = 0) -> [String: Any] { + public func releaseSizing(surfaceID: UInt64, requestID: UInt64 = 0) -> [String: Any] { [ "id": requestID, "cmd": "release-surface-size", @@ -175,7 +175,7 @@ struct CloudTuiManualIOCommand: Sendable { /// Removes this exact attach stream's size contribution while retaining /// the stream for cached output. The server treats a repeated release as /// an idempotent no-op for the same lease. - func releaseAttachedViewSize( + public func releaseAttachedViewSize( surfaceID: UInt64, lease: String, requestID: UInt64 = 0 @@ -191,7 +191,7 @@ struct CloudTuiManualIOCommand: Sendable { /// Explicitly detaches one legacy attachment. Closing the connection is the /// fallback for older servers; this command is useful for protocol fixtures. - func detach(surfaceID: UInt64, requestID: UInt64 = 1) -> [String: Any] { + public func detach(surfaceID: UInt64, requestID: UInt64 = 1) -> [String: Any] { [ "id": requestID, "cmd": "detach-surface", @@ -201,7 +201,7 @@ struct CloudTuiManualIOCommand: Sendable { /// Retires a capability-negotiated attachment while keeping the control /// socket usable for any other future view. - func detachAttachedView( + public func detachAttachedView( surfaceID: UInt64, lease: String, requestID: UInt64 = 1 @@ -215,7 +215,7 @@ struct CloudTuiManualIOCommand: Sendable { } /// Serializes a command as one newline-delimited protocol message. - func line(_ command: [String: Any]) -> Data? { + public func line(_ command: [String: Any]) -> Data? { guard let data = try? JSONSerialization.data(withJSONObject: command) else { return nil } return data + Data([0x0A]) } diff --git a/Sources/Cloud/CloudTuiManualIOConnection.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOConnection.swift similarity index 98% rename from Sources/Cloud/CloudTuiManualIOConnection.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOConnection.swift index f74a40fd63aa..7ef3532f42c1 100644 --- a/Sources/Cloud/CloudTuiManualIOConnection.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOConnection.swift @@ -10,7 +10,7 @@ import Foundation // @unchecked Sendable is safe here because every mutable descriptor/source/ // framing field and pending demand are accessed only on `queue`. Continuations // hand immutable frames back to the single async consumer. -final class CloudTuiManualIOConnection: @unchecked Sendable { +public final class CloudTuiManualIOConnection: @unchecked Sendable { private static let maximumLineBytes = 16 * 1024 * 1024 private static let readChunkBytes = 16 * 1024 @@ -21,7 +21,7 @@ final class CloudTuiManualIOConnection: @unchecked Sendable { /// Single-consumer stream. Each next() requests one frame, so a busy renderer /// applies socket backpressure instead of overflowing a decoded-frame queue. - var events: AsyncStream { + public var events: AsyncStream { AsyncStream(unfolding: { [weak self] in await self?.nextFrame() }, onCancel: { [weak self] in @@ -48,7 +48,7 @@ final class CloudTuiManualIOConnection: @unchecked Sendable { private let pendingWriteByteLimit = 256 * 1024 private var closed = false - init( + public init( socketPath: String, deliversJSONMessages: Bool = false, queue: DispatchQueue = DispatchQueue( @@ -84,7 +84,7 @@ final class CloudTuiManualIOConnection: @unchecked Sendable { } /// Connects to the local link socket and starts line delivery. - func start() async throws { + public func start() async throws { try await withTaskCancellationHandler(operation: { try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in queue.async { [self] in @@ -118,14 +118,14 @@ final class CloudTuiManualIOConnection: @unchecked Sendable { } /// Enqueues one JSON command. Commands are serialized with incoming lines. - func send(_ command: [String: Any]) { + public func send(_ command: [String: Any]) { guard let line = commandBuilder.line(command) else { return } send(line: line) } /// Enqueues an already framed JSON line. Used by the input router so it can /// preserve ordering while a connection is being rebound. - func send(line: Data) { + public func send(line: Data) { queue.async { [self, line] in guard !closed, descriptor >= 0 else { return } guard pendingWriteBytes + line.count <= pendingWriteByteLimit else { @@ -144,7 +144,7 @@ final class CloudTuiManualIOConnection: @unchecked Sendable { /// Closes only this attachment connection. The remote terminal session stays /// owned by cmux-tui and can be attached again later. - func close() { + public func close() { queue.async { [self] in closeLocked() } diff --git a/Sources/Cloud/CloudTuiManualIODescriptorLease.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIODescriptorLease.swift similarity index 81% rename from Sources/Cloud/CloudTuiManualIODescriptorLease.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIODescriptorLease.swift index 8adf90786079..6be0c6484cc7 100644 --- a/Sources/Cloud/CloudTuiManualIODescriptorLease.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIODescriptorLease.swift @@ -3,12 +3,12 @@ import Darwin /// Closes a socket descriptor only after every registered dispatch source cancels. // @unchecked Sendable is safe because every method is invoked by a cancellation // handler targeted at the owning connection's serial queue. -final class CloudTuiManualIODescriptorLease: @unchecked Sendable { - let descriptor: Int32 +public final class CloudTuiManualIODescriptorLease: @unchecked Sendable { + public let descriptor: Int32 private var remainingSources = 0 private var didClose = false - init(descriptor: Int32) { + public init(descriptor: Int32) { self.descriptor = descriptor } @@ -18,19 +18,19 @@ final class CloudTuiManualIODescriptorLease: @unchecked Sendable { deinit {} /// Registers one dispatch source before it is activated. - func registerSource() { + public func registerSource() { remainingSources += 1 } /// Called by a source cancellation handler on the connection queue. - func sourceDidCancel() { + public func sourceDidCancel() { guard remainingSources > 0 else { return } remainingSources -= 1 closeIfReady() } /// Closes an unregistered descriptor after all sources have cancelled. - func closeIfReady() { + public func closeIfReady() { guard remainingSources == 0, !didClose else { return } didClose = true Darwin.close(descriptor) diff --git a/Sources/Cloud/CloudTuiManualIOFrame.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOFrame.swift similarity index 95% rename from Sources/Cloud/CloudTuiManualIOFrame.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOFrame.swift index 575002fb445d..3261683a33ab 100644 --- a/Sources/Cloud/CloudTuiManualIOFrame.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOFrame.swift @@ -5,7 +5,7 @@ import Foundation /// The native cloud pane consumes these events as terminal bytes. It deliberately /// does not contain a rendered-cell representation: libghostty remains the only /// renderer in a native pane. -enum CloudTuiManualIOFrame: Equatable, Sendable { +public enum CloudTuiManualIOFrame: Equatable, Sendable { /// `colors` is the sparse sidecar that travels with a theme-portable replay /// or a palette-changing output chunk; `nil` means the frame carried none. case snapshot(surfaceID: UInt64, columns: Int, rows: Int, bytes: Data, colors: CloudTuiRemoteColors? = nil) diff --git a/Sources/Cloud/CloudTuiManualIOFrameDecoder.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOFrameDecoder.swift similarity index 97% rename from Sources/Cloud/CloudTuiManualIOFrameDecoder.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOFrameDecoder.swift index 6370e26845fc..00b23db41b83 100644 --- a/Sources/Cloud/CloudTuiManualIOFrameDecoder.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOFrameDecoder.swift @@ -5,10 +5,12 @@ import Foundation /// /// The decoder is intentionally stateless. A socket reader owns line framing; /// this value only validates the event discriminator and base64 payload. -struct CloudTuiManualIOFrameDecoder: Sendable { +public struct CloudTuiManualIOFrameDecoder: Sendable { + public init() {} + /// Decodes a complete JSON object line, returning `nil` for malformed or /// unrelated messages. - func decode(_ line: Data) -> CloudTuiManualIOFrame? { + public func decode(_ line: Data) -> CloudTuiManualIOFrame? { guard let object = try? JSONSerialization.jsonObject(with: line) as? [String: Any] else { return nil } diff --git a/Sources/Cloud/CloudTuiManualIOGrid+SizingSample.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOGrid+SizingSample.swift similarity index 98% rename from Sources/Cloud/CloudTuiManualIOGrid+SizingSample.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOGrid+SizingSample.swift index f330825909da..a94d4d909b20 100644 --- a/Sources/Cloud/CloudTuiManualIOGrid+SizingSample.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOGrid+SizingSample.swift @@ -7,7 +7,7 @@ extension CloudTuiManualIOGrid { /// 800×600 startup window before AppKit lays out the real pane; checking /// the reported pixel size against the attached view keeps that transient /// grid from becoming the remote PTY's authority. - static func usable( + public static func usable( from sample: TerminalSurfaceRawSizingSample, validatePanePixels: Bool ) -> CloudTuiManualIOGrid? { diff --git a/Sources/Cloud/CloudTuiManualIOGrid.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOGrid.swift similarity index 91% rename from Sources/Cloud/CloudTuiManualIOGrid.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOGrid.swift index 279ac6835f27..2df394572c57 100644 --- a/Sources/Cloud/CloudTuiManualIOGrid.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOGrid.swift @@ -2,12 +2,12 @@ import CmuxTerminal import Foundation /// A cell grid sampled from the native Ghostty pane. -struct CloudTuiManualIOGrid: Equatable, Sendable { - let columns: Int - let rows: Int +public struct CloudTuiManualIOGrid: Equatable, Sendable { + public let columns: Int + public let rows: Int /// Creates a bounded, usable terminal grid. - init?(columns: Int, rows: Int) { + public init?(columns: Int, rows: Int) { guard (2...10_000).contains(columns), (2...10_000).contains(rows) else { return nil } @@ -19,7 +19,7 @@ struct CloudTuiManualIOGrid: Equatable, Sendable { /// the hidden 800×600 startup window before AppKit lays out the real pane; /// checking the reported pixel size against the attached view keeps that /// transient grid from becoming the remote PTY's authority. - init?( + public init?( sample: TerminalSurfaceRawSizingSample, validatePanePixels: Bool ) { diff --git a/Sources/Cloud/CloudTuiManualIOInputRouter.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOInputRouter.swift similarity index 95% rename from Sources/Cloud/CloudTuiManualIOInputRouter.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOInputRouter.swift index a3795d72c36b..fe81bae74081 100644 --- a/Sources/Cloud/CloudTuiManualIOInputRouter.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOInputRouter.swift @@ -10,7 +10,7 @@ import Foundation // @unchecked Sendable is safe because all mutable pending-input state is // isolated to `queue`; callers cross the boundary only with immutable input // values and the connection's thread-safe enqueue operation. -final class CloudTuiManualIOInputRouter: @unchecked Sendable { +public final class CloudTuiManualIOInputRouter: @unchecked Sendable { private var surfaceID: UInt64 private let queue: DispatchQueue private let commandBuilder: CloudTuiManualIOCommand @@ -19,7 +19,7 @@ final class CloudTuiManualIOInputRouter: @unchecked Sendable { private let pendingByteLimit = 256 * 1024 private var pendingByteCount = 0 - init( + public init( surfaceID: UInt64, queue: DispatchQueue = DispatchQueue(label: "com.cmux.cloud-manual-io-input", qos: .userInitiated), commandBuilder: CloudTuiManualIOCommand = CloudTuiManualIOCommand() @@ -32,7 +32,7 @@ final class CloudTuiManualIOInputRouter: @unchecked Sendable { /// Updates the numeric surface target after a cmux-tui daemon restart. /// Public terminal resource IDs survive a restart, while the compatibility /// tree's numeric surface IDs may be allocated again. - func updateSurfaceID(_ surfaceID: UInt64) { + public func updateSurfaceID(_ surfaceID: UInt64) { queue.async { [self, surfaceID] in guard self.surfaceID != surfaceID else { return } let previousSurfaceID = self.surfaceID @@ -57,7 +57,7 @@ final class CloudTuiManualIOInputRouter: @unchecked Sendable { } /// Rebinds pending input to a newly connected transport. - func setConnection(_ connection: CloudTuiManualIOConnection?) { + public func setConnection(_ connection: CloudTuiManualIOConnection?) { queue.async { [self, connection] in self.connection = connection guard let connection else { return } @@ -68,7 +68,7 @@ final class CloudTuiManualIOInputRouter: @unchecked Sendable { } /// Stops delivery and discards queued bytes during permanent pane teardown. - func invalidate() { + public func invalidate() { queue.async { [self] in connection = nil pendingLines.removeAll(keepingCapacity: false) @@ -77,7 +77,7 @@ final class CloudTuiManualIOInputRouter: @unchecked Sendable { } /// Orders a control request with the manual input that preceded the paste. - func sendControl( + public func sendControl( _ command: [String: Any], on connection: CloudTuiManualIOConnection, requestID: UInt64 ) throws -> UInt64 { let command = command.merging(["id": requestID]) { _, value in value } @@ -91,7 +91,7 @@ final class CloudTuiManualIOInputRouter: @unchecked Sendable { } /// Enqueues one manual input event. - func send(_ input: TerminalManualInput) { + public func send(_ input: TerminalManualInput) { // Keep base64/JSON work off Ghostty's synchronous I/O callback. The // callback only copies the already-owned Sendable value and enqueues it // on this serial transport lane. diff --git a/Sources/Cloud/CloudTuiManualIOResizeScheduler.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOResizeScheduler.swift similarity index 79% rename from Sources/Cloud/CloudTuiManualIOResizeScheduler.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOResizeScheduler.swift index 5b34ff3ba78f..412990c38085 100644 --- a/Sources/Cloud/CloudTuiManualIOResizeScheduler.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualIOResizeScheduler.swift @@ -7,13 +7,15 @@ import Foundation /// sample queues stale `SIGWINCH` work on the remote PTY and can leave the /// terminal visibly behind the pane. This latest-wins state machine keeps one /// request in flight and retains only the newest desired grid. -struct CloudTuiManualIOResizeScheduler: Equatable, Sendable { - private(set) var desired: CloudTuiManualIOGrid? = nil - private(set) var inFlight: CloudTuiManualIOGrid? = nil - private(set) var lastAcknowledged: CloudTuiManualIOGrid? = nil +public struct CloudTuiManualIOResizeScheduler: Equatable, Sendable { + public private(set) var desired: CloudTuiManualIOGrid? = nil + public private(set) var inFlight: CloudTuiManualIOGrid? = nil + public private(set) var lastAcknowledged: CloudTuiManualIOGrid? = nil + + public init() {} /// Records a sample and returns a grid that may be sent immediately. - mutating func sample( + public mutating func sample( _ grid: CloudTuiManualIOGrid, canSend: Bool ) -> CloudTuiManualIOGrid? { @@ -24,7 +26,7 @@ struct CloudTuiManualIOResizeScheduler: Equatable, Sendable { /// Completes the current request and optionally starts the newest pending /// request. When `canSend` is false (for example while geometry authority /// is being claimed), the newest sample remains parked in `desired`. - mutating func acknowledge(canSend: Bool) -> CloudTuiManualIOGrid? { + public mutating func acknowledge(canSend: Bool) -> CloudTuiManualIOGrid? { if let inFlight { lastAcknowledged = inFlight } @@ -37,7 +39,7 @@ struct CloudTuiManualIOResizeScheduler: Equatable, Sendable { /// Responses are correlated by request id, but a response from before a /// hide/reveal or reconnect can still arrive after the scheduler has been /// reset. Ignoring that stale response preserves the newer in-flight grid. - mutating func acknowledge( + public mutating func acknowledge( _ grid: CloudTuiManualIOGrid, canSend: Bool ) -> CloudTuiManualIOGrid? { @@ -46,14 +48,14 @@ struct CloudTuiManualIOResizeScheduler: Equatable, Sendable { } /// Resumes a parked sample after the connection becomes authoritative. - mutating func resume() -> CloudTuiManualIOGrid? { + public mutating func resume() -> CloudTuiManualIOGrid? { beginIfPossible(canSend: true) } /// Forces the desired grid to be sent again, even when it matches the last /// acknowledged request. Used after a server replay reports a clamped or /// otherwise different authoritative grid. - mutating func force(_ grid: CloudTuiManualIOGrid) -> CloudTuiManualIOGrid? { + public mutating func force(_ grid: CloudTuiManualIOGrid) -> CloudTuiManualIOGrid? { desired = grid lastAcknowledged = nil return beginIfPossible(canSend: true) @@ -61,7 +63,7 @@ struct CloudTuiManualIOResizeScheduler: Equatable, Sendable { /// Retires the current request on reconnect while retaining the latest /// local sample for the new attachment. - mutating func resetForReconnect() { + public mutating func resetForReconnect() { inFlight = nil lastAcknowledged = nil } diff --git a/Sources/Cloud/CloudTuiManualMirrorDeadlines.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualMirrorDeadlines.swift similarity index 78% rename from Sources/Cloud/CloudTuiManualMirrorDeadlines.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualMirrorDeadlines.swift index b820ad201a7c..1c823b611d88 100644 --- a/Sources/Cloud/CloudTuiManualMirrorDeadlines.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualMirrorDeadlines.swift @@ -4,18 +4,18 @@ import Foundation /// attachment has a deadline, so a wedged socket (accepted by the daemon but /// never answered, or silently stalled) is detected instead of waited on /// forever. -struct CloudTuiManualMirrorDeadlines: Equatable, Sendable { +public struct CloudTuiManualMirrorDeadlines: Equatable, Sendable { /// The identify → set-client-info → attach handshake must complete within /// this bound, measured from the socket connect. - let handshake: Duration + public let handshake: Duration /// While attached, a connection that delivered no frame for this long is /// probed with `ping`. - let livenessInterval: Duration + public let livenessInterval: Duration /// The probe must be answered within this bound or the attachment is /// declared stalled and reconnected. - let livenessAnswer: Duration + public let livenessAnswer: Duration - init(handshake: Duration, livenessInterval: Duration, livenessAnswer: Duration) { + public init(handshake: Duration, livenessInterval: Duration, livenessAnswer: Duration) { precondition(handshake > .zero) precondition(livenessInterval > .zero) precondition(livenessAnswer > .zero) @@ -26,7 +26,7 @@ struct CloudTuiManualMirrorDeadlines: Equatable, Sendable { /// Production bounds: generous enough for a cold link over a slow route, /// short enough that a user sees a reconnect instead of a dead pane. - static let standard = Self( + public static let standard = Self( handshake: .seconds(20), livenessInterval: .seconds(30), livenessAnswer: .seconds(10) diff --git a/Sources/Cloud/CloudTuiManualMirrorPhase.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualMirrorPhase.swift similarity index 70% rename from Sources/Cloud/CloudTuiManualMirrorPhase.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualMirrorPhase.swift index a3ecec08a71f..cf9a63506b74 100644 --- a/Sources/Cloud/CloudTuiManualMirrorPhase.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualMirrorPhase.swift @@ -1,5 +1,5 @@ /// Lifecycle of one cloud terminal's byte attachment. -enum CloudTuiManualMirrorPhase: Equatable, Sendable { +public enum CloudTuiManualMirrorPhase: Equatable, Sendable { case idle case connecting case attached diff --git a/Sources/Cloud/CloudTuiManualMirrorRequestKind.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualMirrorRequestKind.swift similarity index 88% rename from Sources/Cloud/CloudTuiManualMirrorRequestKind.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualMirrorRequestKind.swift index 8a8abd03a2be..6cb74aaa3f81 100644 --- a/Sources/Cloud/CloudTuiManualMirrorRequestKind.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualMirrorRequestKind.swift @@ -3,7 +3,7 @@ /// Resize requests retain the exact grid they carried. A response can arrive /// after a visibility transition has reset the scheduler, so acknowledging by /// request kind alone could accidentally retire a newer grid. -enum CloudTuiManualMirrorRequestKind: Equatable, Sendable { +public enum CloudTuiManualMirrorRequestKind: Equatable, Sendable { case identify case clientInfo case attach diff --git a/Sources/Cloud/CloudTuiManualMirrorWatchdog.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualMirrorWatchdog.swift similarity index 89% rename from Sources/Cloud/CloudTuiManualMirrorWatchdog.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualMirrorWatchdog.swift index 16c2ddf83a0a..b82d154843db 100644 --- a/Sources/Cloud/CloudTuiManualMirrorWatchdog.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiManualMirrorWatchdog.swift @@ -7,21 +7,21 @@ import Foundation /// probed with `ping`. The watchdog only measures time; the owning session /// decides every transition on the main actor when an expiry is reported. @MainActor -final class CloudTuiManualMirrorWatchdog { +public final class CloudTuiManualMirrorWatchdog { private let deadlines: CloudTuiManualMirrorDeadlines private let clock: any Clock private var task: Task? private var framesSinceCheck = 0 private var probeAnswered = false - init(deadlines: CloudTuiManualMirrorDeadlines, clock: any Clock) { + public init(deadlines: CloudTuiManualMirrorDeadlines, clock: any Clock) { self.deadlines = deadlines self.clock = clock } /// Starts the handshake deadline. `onExpiry` fires once if it elapses /// before ``armLiveness(probe:onExpiry:)`` or ``cancel()`` replaces it. - func armHandshake(onExpiry: @escaping @MainActor () -> Void) { + public func armHandshake(onExpiry: @escaping @MainActor () -> Void) { task?.cancel() let deadline = deadlines.handshake task = Task { @MainActor [clock] in @@ -39,7 +39,7 @@ final class CloudTuiManualMirrorWatchdog { /// `livenessInterval` without a frame, `probe` is sent; if neither a frame /// nor ``noteProbeAnswered()`` arrives within `livenessAnswer`, `onExpiry` /// fires once and monitoring stops. - func armLiveness( + public func armLiveness( probe: @escaping @MainActor () -> Void, onExpiry: @escaping @MainActor () -> Void ) { @@ -79,15 +79,15 @@ final class CloudTuiManualMirrorWatchdog { } /// Any inbound frame proves the stream is alive. - func noteFrame() { + public func noteFrame() { framesSinceCheck += 1 } - func noteProbeAnswered() { + public func noteProbeAnswered() { probeAnswered = true } - func cancel() { + public func cancel() { task?.cancel() task = nil } diff --git a/Sources/Cloud/CloudTuiPersistentRequestBuilder.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiPersistentRequestBuilder.swift similarity index 54% rename from Sources/Cloud/CloudTuiPersistentRequestBuilder.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiPersistentRequestBuilder.swift index 8cb2937f8219..3a869125cf61 100644 --- a/Sources/Cloud/CloudTuiPersistentRequestBuilder.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiPersistentRequestBuilder.swift @@ -3,13 +3,13 @@ import Foundation /// Immutable wire request. Application code uses resource operations directly; /// it never round-trips commands through CLI argument parsing. -struct CloudTuiRequest: Sendable, Equatable { - let operation: String - let parameters: Data - let raw: Bool - var idempotencyKey: String? +public struct CloudTuiRequest: Sendable, Equatable { + public let operation: String + public let parameters: Data + public let raw: Bool + public var idempotencyKey: String? - init(_ operation: String, _ params: [String: Any] = [:], mutation: Bool = false, key: String? = nil, raw: Bool = false) { + public init(_ operation: String, _ params: [String: Any] = [:], mutation: Bool = false, key: String? = nil, raw: Bool = false) { self.operation = operation self.raw = raw var params = params @@ -22,19 +22,19 @@ struct CloudTuiRequest: Sendable, Equatable { idempotencyKey = mutation ? key ?? "mutation-\(UUID().uuidString.lowercased())" : nil } - var params: [String: Any] { (try? JSONSerialization.jsonObject(with: parameters)) as? [String: Any] ?? [:] } + public var params: [String: Any] { (try? JSONSerialization.jsonObject(with: parameters)) as? [String: Any] ?? [:] } - func adding(_ fields: [String: Any]) -> Self { + public func adding(_ fields: [String: Any]) -> Self { Self(operation, params.merging(fields) { _, new in new }, mutation: idempotencyKey != nil, key: idempotencyKey, raw: raw) } - func withIdempotencyKey(_ key: String) -> Self { + public func withIdempotencyKey(_ key: String) -> Self { var request = self request.idempotencyKey = key return request } - func envelope(id: String) throws -> Data { + public func envelope(id: String) throws -> Data { var object: [String: Any] if raw { object = params.merging(["id": id, "cmd": operation]) { _, new in new } @@ -48,34 +48,34 @@ struct CloudTuiRequest: Sendable, Equatable { /// Builders for the Cloud application's complete control surface. /// Public IDs are absolute. Never add focused workspace/pane selectors to them. -enum CloudTuiRequests { - static func snapshotArguments(socketPath: String) -> CloudTuiRequest { CloudTuiRequest("session.snapshot") } - static func createWorkspaceArguments(socketPath: String, name: String? = nil, empty: Bool = false) -> CloudTuiRequest { +public enum CloudTuiRequests { + public static func snapshotArguments(socketPath: String) -> CloudTuiRequest { CloudTuiRequest("session.snapshot") } + public static func createWorkspaceArguments(socketPath: String, name: String? = nil, empty: Bool = false) -> CloudTuiRequest { var fields: [String: Any] = ["initial_content": empty ? "empty" : "terminal"] if let name, !name.isEmpty { fields["name"] = name } return CloudTuiRequest("workspace.create", fields, mutation: true) } - static func runArguments(socketPath: String, workspaceID: String, command: [String], onExit: String? = nil, cwd: String? = nil, idempotencyKey: String? = nil, correlationKey: String? = nil) -> CloudTuiRequest { + public static func runArguments(socketPath: String, workspaceID: String, command: [String], onExit: String? = nil, cwd: String? = nil, idempotencyKey: String? = nil, correlationKey: String? = nil) -> CloudTuiRequest { var fields: [String: Any] = ["workspace": workspaceID, "argv": command] if let onExit { fields["on_exit"] = onExit } if let cwd = cwd?.trimmingCharacters(in: .whitespacesAndNewlines), !cwd.isEmpty { fields["cwd"] = cwd } if let correlationKey { fields["correlation_key"] = correlationKey } return CloudTuiRequest("workspace.run", fields, mutation: true, key: idempotencyKey) } - static func paneCreate(paneID: String, direction: String?, command: [String], revision: UInt64?, key: String, correlationKey: String?) -> CloudTuiRequest { + public static func paneCreate(paneID: String, direction: String?, command: [String], revision: UInt64?, key: String, correlationKey: String?) -> CloudTuiRequest { var fields: [String: Any] = ["pane": paneID] if let direction { fields["direction"] = direction } else { fields["argv"] = command } if let revision { fields["expected_revision"] = String(revision) } if let correlationKey { fields["correlation_key"] = correlationKey } return CloudTuiRequest(direction == nil ? "pane.run" : "pane.split", fields, mutation: true, key: key) } - static func closeTerminalArguments(socketPath: String, terminalID: String) -> CloudTuiRequest { CloudTuiRequest("terminal.close", ["terminal": terminalID], mutation: true) } - static func closeTabArguments(socketPath: String, tabID: String) -> CloudTuiRequest { CloudTuiRequest("tab.close", ["tab": tabID], mutation: true) } - static func closeWorkspaceArguments(socketPath: String, workspaceID: String) -> CloudTuiRequest { CloudTuiRequest("workspace.close", ["workspace": workspaceID], mutation: true) } - static func renameWorkspaceArguments(socketPath: String, workspaceID: String, name: String, expectedRevision: UInt64? = nil) -> CloudTuiRequest { + public static func closeTerminalArguments(socketPath: String, terminalID: String) -> CloudTuiRequest { CloudTuiRequest("terminal.close", ["terminal": terminalID], mutation: true) } + public static func closeTabArguments(socketPath: String, tabID: String) -> CloudTuiRequest { CloudTuiRequest("tab.close", ["tab": tabID], mutation: true) } + public static func closeWorkspaceArguments(socketPath: String, workspaceID: String) -> CloudTuiRequest { CloudTuiRequest("workspace.close", ["workspace": workspaceID], mutation: true) } + public static func renameWorkspaceArguments(socketPath: String, workspaceID: String, name: String, expectedRevision: UInt64? = nil) -> CloudTuiRequest { rename("workspace.rename", fields: ["workspace": workspaceID, "name": name], revision: expectedRevision) } - static func renameTabArguments(socketPath: String, tabID: String, name: String, expectedRevision: UInt64? = nil) -> CloudTuiRequest { + public static func renameTabArguments(socketPath: String, tabID: String, name: String, expectedRevision: UInt64? = nil) -> CloudTuiRequest { rename("tab.rename", fields: ["tab": tabID, "name": name], revision: expectedRevision) } private static func rename(_ op: String, fields: [String: Any], revision: UInt64?) -> CloudTuiRequest { @@ -83,10 +83,10 @@ enum CloudTuiRequests { if let revision { fields["expected_revision"] = String(revision) } return CloudTuiRequest(op, fields, mutation: true) } - static func projectTerminalArguments(socketPath: String, terminalID: String, target: CloudTuiTerminalProjectionTarget, expectedRevision: String? = nil, idempotencyKey: String? = nil) -> CloudTuiRequest { + public static func projectTerminalArguments(socketPath: String, terminalID: String, target: CloudTuiTerminalProjectionTarget, expectedRevision: String? = nil, idempotencyKey: String? = nil) -> CloudTuiRequest { placement("terminal.project", source: ["terminal": terminalID], target: target, revision: expectedRevision, key: idempotencyKey) } - static func moveTabArguments(socketPath: String, tabID: String, target: CloudTuiTerminalProjectionTarget, expectedRevision: String? = nil, idempotencyKey: String? = nil) -> CloudTuiRequest { + public static func moveTabArguments(socketPath: String, tabID: String, target: CloudTuiTerminalProjectionTarget, expectedRevision: String? = nil, idempotencyKey: String? = nil) -> CloudTuiRequest { placement("tab.move", source: ["tab": tabID], target: target, revision: expectedRevision, key: idempotencyKey) } private static func placement(_ op: String, source: [String: Any], target: CloudTuiTerminalProjectionTarget, revision: String?, key: String?) -> CloudTuiRequest { @@ -98,38 +98,38 @@ enum CloudTuiRequests { if let revision { fields["expected_revision"] = revision } return CloudTuiRequest(op, fields, mutation: true, key: key) } - static func notificationAckArguments(socketPath: String, clientID: String, notificationIDs: [String], idempotencyKey: String) -> CloudTuiRequest { + public static func notificationAckArguments(socketPath: String, clientID: String, notificationIDs: [String], idempotencyKey: String) -> CloudTuiRequest { CloudTuiRequest("notification.ack", ["client_id": clientID, "notifications": notificationIDs], mutation: true, key: idempotencyKey) } - static func keysArguments(socketPath: String, terminalID: String, keys: [String]) -> CloudTuiRequest { CloudTuiRequest("terminal.input.keys", ["terminal": terminalID, "keys": keys], mutation: true) } - static func writeBytes(terminalID: String, data: Data) -> CloudTuiRequest { CloudTuiRequest("terminal.input.write", ["terminal": terminalID, "bytes_base64": data.base64EncodedString()], mutation: true) } - static func screenReadArguments(socketPath: String, terminalID: String) -> CloudTuiRequest { CloudTuiRequest("terminal.screen.read", ["terminal": terminalID]) } - static func processInfoArguments(socketPath: String, terminalID: String) -> CloudTuiRequest { CloudTuiRequest("terminal.process.get", ["terminal": terminalID]) } - static func screenWaitArguments(socketPath: String, terminalID: String, pattern: String, timeoutMs: Int?) -> CloudTuiRequest { + public static func keysArguments(socketPath: String, terminalID: String, keys: [String]) -> CloudTuiRequest { CloudTuiRequest("terminal.input.keys", ["terminal": terminalID, "keys": keys], mutation: true) } + public static func writeBytes(terminalID: String, data: Data) -> CloudTuiRequest { CloudTuiRequest("terminal.input.write", ["terminal": terminalID, "bytes_base64": data.base64EncodedString()], mutation: true) } + public static func screenReadArguments(socketPath: String, terminalID: String) -> CloudTuiRequest { CloudTuiRequest("terminal.screen.read", ["terminal": terminalID]) } + public static func processInfoArguments(socketPath: String, terminalID: String) -> CloudTuiRequest { CloudTuiRequest("terminal.process.get", ["terminal": terminalID]) } + public static func screenWaitArguments(socketPath: String, terminalID: String, pattern: String, timeoutMs: Int?) -> CloudTuiRequest { var fields: [String: Any] = ["terminal": terminalID, "pattern": pattern] if let timeoutMs { fields["timeout_ms"] = String(timeoutMs) } return CloudTuiRequest("terminal.wait", fields) } - static func processWaitArguments(socketPath: String, terminalID: String, timeoutMs: Int?) -> CloudTuiRequest { + public static func processWaitArguments(socketPath: String, terminalID: String, timeoutMs: Int?) -> CloudTuiRequest { var fields: [String: Any] = ["terminal": terminalID] if let timeoutMs { fields["timeout_ms"] = String(timeoutMs) } return CloudTuiRequest("terminal.wait_exit", fields) } - static func outputReadArguments(socketPath: String, terminalID: String, after: Int?, maxBytes: Int?) -> CloudTuiRequest { + public static func outputReadArguments(socketPath: String, terminalID: String, after: Int?, maxBytes: Int?) -> CloudTuiRequest { var fields: [String: Any] = ["terminal": terminalID] if let after, after >= 0 { fields["after"] = String(after) } if let maxBytes, maxBytes > 0 { fields["max_bytes"] = maxBytes } return CloudTuiRequest("terminal.output_read", fields) } - static func legacyListWorkspacesArguments(socketPath: String) -> CloudTuiRequest { CloudTuiRequest("list-workspaces", raw: true) } - static func identifyArguments(socketPath: String) -> CloudTuiRequest? { CloudTuiRequest("identify", raw: true) } - static func listeningPortsArguments(socketPath: String) -> CloudTuiRequest? { CloudTuiRequest("machine-listening-tcp", raw: true) } - static func resolveTerminalArguments(socketPath: String, terminalID: String) -> CloudTuiRequest? { + public static func legacyListWorkspacesArguments(socketPath: String) -> CloudTuiRequest { CloudTuiRequest("list-workspaces", raw: true) } + public static func identifyArguments(socketPath: String) -> CloudTuiRequest? { CloudTuiRequest("identify", raw: true) } + public static func listeningPortsArguments(socketPath: String) -> CloudTuiRequest? { CloudTuiRequest("machine-listening-tcp", raw: true) } + public static func resolveTerminalArguments(socketPath: String, terminalID: String) -> CloudTuiRequest? { let payload = terminalID.hasPrefix("term_") ? String(terminalID.dropFirst(5)) : terminalID guard payload.utf8.count == 32, payload.utf8.allSatisfy({ (48...57).contains($0) || (97...102).contains($0) }) else { return nil } return CloudTuiRequest("resolve-terminal", ["terminal_id": "term_" + payload], raw: true) } - static func setDefaultColorsArguments(socketPath: String, foreground: String?, background: String?) -> CloudTuiRequest? { + public static func setDefaultColorsArguments(socketPath: String, foreground: String?, background: String?) -> CloudTuiRequest? { var fields: [String: Any] = [:] if let foreground { fields["foreground"] = foreground } if let background { fields["background"] = background } diff --git a/Sources/Cloud/CloudTuiRemoteColors.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiRemoteColors.swift similarity index 89% rename from Sources/Cloud/CloudTuiRemoteColors.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiRemoteColors.swift index 5c03cfb5eb24..c3aac6b04c77 100644 --- a/Sources/Cloud/CloudTuiRemoteColors.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiRemoteColors.swift @@ -3,14 +3,14 @@ import Foundation /// Application-authored colors beside a theme-portable Cloud replay. /// Missing entries retain the viewer's Ghostty theme. The pane feeds the /// equivalent OSC sequences to its own libghostty to preserve reset semantics. -struct CloudTuiRemoteColors: Equatable, Sendable { - var foreground: String? - var background: String? - var cursor: String? +public struct CloudTuiRemoteColors: Equatable, Sendable { + public var foreground: String? + public var background: String? + public var cursor: String? /// Palette index (0...255) to `#rrggbb`. - var palette: [Int: String] + public var palette: [Int: String] - init(foreground: String? = nil, background: String? = nil, cursor: String? = nil, palette: [Int: String] = [:]) { + public init(foreground: String? = nil, background: String? = nil, cursor: String? = nil, palette: [Int: String] = [:]) { self.foreground = foreground self.background = background self.cursor = cursor @@ -20,7 +20,7 @@ struct CloudTuiRemoteColors: Equatable, Sendable { /// Parses the protocol object. Unknown keys and malformed values are /// dropped rather than rejecting the frame, since a color is never worth /// losing the screen bytes it travels with. - init?(json: Any?) { + public init?(json: Any?) { guard let object = json as? [String: Any] else { return nil } // Only older daemons omit provenance. Never treat a newer daemon's // shared effective defaults as application OSC, even if malformed. @@ -38,14 +38,14 @@ struct CloudTuiRemoteColors: Equatable, Sendable { self.palette = palette } - var isEmpty: Bool { + public var isEmpty: Bool { foreground == nil && background == nil && cursor == nil && palette.isEmpty } /// OSC 10/11/12 for the special colors and OSC 4 per authored palette /// entry, in index order so output is deterministic. Equivalent to the /// delta from a terminal with no remote colors applied. - var oscBytes: Data { + public var oscBytes: Data { oscDelta(from: CloudTuiRemoteColors()) } @@ -55,7 +55,7 @@ struct CloudTuiRemoteColors: Equatable, Sendable { /// so this emits the matching reset for every entry `previous` carried /// that `self` no longer does, and a set for every entry that is new or /// changed. Unchanged entries produce nothing. - func oscDelta(from previous: CloudTuiRemoteColors) -> Data { + public func oscDelta(from previous: CloudTuiRemoteColors) -> Data { var text = "" Self.appendSpecial(&text, set: 10, reset: 110, previous: previous.foreground, next: foreground) Self.appendSpecial(&text, set: 11, reset: 111, previous: previous.background, next: background) diff --git a/Sources/Cloud/CloudTuiResolvedSurface.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiResolvedSurface.swift similarity index 86% rename from Sources/Cloud/CloudTuiResolvedSurface.swift rename to Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiResolvedSurface.swift index 08c92ba4982d..dc4ceb3f1a92 100644 --- a/Sources/Cloud/CloudTuiResolvedSurface.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiResolvedSurface.swift @@ -1,7 +1,7 @@ import Foundation /// Outcome of decoding the private terminal-to-surface resolver response. -enum CloudTuiResolvedSurface: Equatable, Sendable { +public enum CloudTuiResolvedSurface: Equatable, Sendable { case surface(UInt64) case noPlacement /// The remote shell is gone: the daemon reports the terminal as exited, or diff --git a/Packages/macOS/CmuxCloudTui/Tests/CmuxCloudTuiTests/CloudTuiPackageSurfaceTests.swift b/Packages/macOS/CmuxCloudTui/Tests/CmuxCloudTuiTests/CloudTuiPackageSurfaceTests.swift new file mode 100644 index 000000000000..9c5049fa1d0c --- /dev/null +++ b/Packages/macOS/CmuxCloudTui/Tests/CmuxCloudTuiTests/CloudTuiPackageSurfaceTests.swift @@ -0,0 +1,35 @@ +import Foundation +import Testing +import CmuxCloudTui + +/// Exercises the transport through its public surface only, the way the app +/// target consumes it after the move. +@Suite struct CloudTuiPackageSurfaceTests { + @Test func shellQuoteLeavesSafeWordsAndQuotesTheRest() { + #expect(CloudTuiCommandLine.shellQuote("") == "''") + #expect(CloudTuiCommandLine.shellQuote("/tmp/cmux.sock") == "/tmp/cmux.sock") + #expect(CloudTuiCommandLine.shellQuote("it's") == "'it'\\''s'") + } + + @Test func gridRejectsDimensionsOutsideTheSupportedRange() { + #expect(CloudTuiManualIOGrid(columns: 1, rows: 24) == nil) + #expect(CloudTuiManualIOGrid(columns: 80, rows: 10_001) == nil) + #expect(CloudTuiManualIOGrid(columns: 80, rows: 24)?.columns == 80) + } + + @Test func resizeSchedulerKeepsOneRequestInFlightAndSendsTheNewestNext() throws { + let small = try #require(CloudTuiManualIOGrid(columns: 80, rows: 24)) + let medium = try #require(CloudTuiManualIOGrid(columns: 100, rows: 30)) + let large = try #require(CloudTuiManualIOGrid(columns: 120, rows: 40)) + var scheduler = CloudTuiManualIOResizeScheduler() + #expect(scheduler.sample(small, canSend: true) == small) + #expect(scheduler.sample(medium, canSend: true) == nil) + #expect(scheduler.sample(large, canSend: true) == nil) + #expect(scheduler.acknowledge(canSend: true) == large) + #expect(scheduler.lastAcknowledged == small) + } + + @Test func legacyParserReadsNothingFromMalformedData() { + #expect(CloudTuiLegacySnapshotParser().protocolVersion(from: Data("not json".utf8)) == nil) + } +} diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index a478153f3ab6..1d544e076da9 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxComputerUse import CmuxCloudMachines import AppKit diff --git a/Sources/Cloud/CloudGuestURLService.swift b/Sources/Cloud/CloudGuestURLService.swift index 27a93d4ec192..91460d923cc4 100644 --- a/Sources/Cloud/CloudGuestURLService.swift +++ b/Sources/Cloud/CloudGuestURLService.swift @@ -1,5 +1,6 @@ import AppKit import CmuxCloudMachines +import CmuxCloudTui import Foundation /// Owns one ephemeral opener subscription per connected VM. The daemon holds diff --git a/Sources/Cloud/CloudMachineLink.swift b/Sources/Cloud/CloudMachineLink.swift index 4d98a5b7d327..4ff6bd06ee70 100644 --- a/Sources/Cloud/CloudMachineLink.swift +++ b/Sources/Cloud/CloudMachineLink.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation import CmuxCore diff --git a/Sources/Cloud/CloudMachineLinkManager.swift b/Sources/Cloud/CloudMachineLinkManager.swift index f35bd4849a5d..3035b750de83 100644 --- a/Sources/Cloud/CloudMachineLinkManager.swift +++ b/Sources/Cloud/CloudMachineLinkManager.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxFoundation import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/Cloud/CloudManualMirrorPresentation.swift b/Sources/Cloud/CloudManualMirrorPresentation.swift index 351bfb4931bf..4ca210c80c8b 100644 --- a/Sources/Cloud/CloudManualMirrorPresentation.swift +++ b/Sources/Cloud/CloudManualMirrorPresentation.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxCore /// Transport state consumed by the pane's presentation owner. diff --git a/Sources/Cloud/CloudTerminalAttachmentLog.swift b/Sources/Cloud/CloudTerminalAttachmentLog.swift index a6b7df9e575b..d334bc75452e 100644 --- a/Sources/Cloud/CloudTerminalAttachmentLog.swift +++ b/Sources/Cloud/CloudTerminalAttachmentLog.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation import os diff --git a/Sources/Cloud/CloudTerminalConnectionPresentationPolicy.swift b/Sources/Cloud/CloudTerminalConnectionPresentationPolicy.swift index 27e69d64d723..aeb57f47e2b3 100644 --- a/Sources/Cloud/CloudTerminalConnectionPresentationPolicy.swift +++ b/Sources/Cloud/CloudTerminalConnectionPresentationPolicy.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation /// When a native cloud pane shows its connection card. diff --git a/Sources/Cloud/CloudTerminalCreationRequest.swift b/Sources/Cloud/CloudTerminalCreationRequest.swift index ce3741c1cf4e..35fe6c0d4e39 100644 --- a/Sources/Cloud/CloudTerminalCreationRequest.swift +++ b/Sources/Cloud/CloudTerminalCreationRequest.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/Cloud/CloudTuiCommandRunning.swift b/Sources/Cloud/CloudTuiCommandRunning.swift index 6a5e19edc881..6d2380eef833 100644 --- a/Sources/Cloud/CloudTuiCommandRunning.swift +++ b/Sources/Cloud/CloudTuiCommandRunning.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation /// The one machine-link operation the attachment resolver depends on: run a diff --git a/Sources/Cloud/CloudTuiManualMirrorSession.swift b/Sources/Cloud/CloudTuiManualMirrorSession.swift index cd7cf8df53fc..09538970bc66 100644 --- a/Sources/Cloud/CloudTuiManualMirrorSession.swift +++ b/Sources/Cloud/CloudTuiManualMirrorSession.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import CmuxTerminal import CmuxCore diff --git a/Sources/Cloud/CloudTuiPersistentResourceConnection.swift b/Sources/Cloud/CloudTuiPersistentResourceConnection.swift index ec998243772f..926334be4edb 100644 --- a/Sources/Cloud/CloudTuiPersistentResourceConnection.swift +++ b/Sources/Cloud/CloudTuiPersistentResourceConnection.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import CoreFoundation import Foundation diff --git a/Sources/Cloud/CloudWireGuardHub.swift b/Sources/Cloud/CloudWireGuardHub.swift index 18941d9c91e9..1bf7515d2813 100644 --- a/Sources/Cloud/CloudWireGuardHub.swift +++ b/Sources/Cloud/CloudWireGuardHub.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation /// The app's one in-process WireGuard tunnel into the user's private Cloud VM diff --git a/Sources/Cloud/VMClientSocketCommands.swift b/Sources/Cloud/VMClientSocketCommands.swift index 3f5e085c1207..29aeb1d77413 100644 --- a/Sources/Cloud/VMClientSocketCommands.swift +++ b/Sources/Cloud/VMClientSocketCommands.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxControlSocket import CmuxSettings import CmuxSurfaceCatalogModel diff --git a/Sources/Cloud/VMTunnelManager.swift b/Sources/Cloud/VMTunnelManager.swift index bc206a7141c3..10bb30e9dcc6 100644 --- a/Sources/Cloud/VMTunnelManager.swift +++ b/Sources/Cloud/VMTunnelManager.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CryptoKit import CmuxSettings import Darwin diff --git a/Sources/RemoteTui/RemoteTuiMachine.swift b/Sources/RemoteTui/RemoteTuiMachine.swift index f947b9ecbf57..309a0d82d9e3 100644 --- a/Sources/RemoteTui/RemoteTuiMachine.swift +++ b/Sources/RemoteTui/RemoteTuiMachine.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxCore import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/RemoteTui/SSHTuiConnection.swift b/Sources/RemoteTui/SSHTuiConnection.swift index 6e85f0de32b8..5c6f48e96e83 100644 --- a/Sources/RemoteTui/SSHTuiConnection.swift +++ b/Sources/RemoteTui/SSHTuiConnection.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxCore import CmuxFoundation import CryptoKit diff --git a/Sources/RemoteTui/SSHTuiLinkManager.swift b/Sources/RemoteTui/SSHTuiLinkManager.swift index 9af2b546c4c7..8fd16e7ca612 100644 --- a/Sources/RemoteTui/SSHTuiLinkManager.swift +++ b/Sources/RemoteTui/SSHTuiLinkManager.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation import CmuxCore diff --git a/Sources/RemoteTui/SSHTuiWorkspaceCoordinator.swift b/Sources/RemoteTui/SSHTuiWorkspaceCoordinator.swift index 7d5e3c824079..79e866a333ce 100644 --- a/Sources/RemoteTui/SSHTuiWorkspaceCoordinator.swift +++ b/Sources/RemoteTui/SSHTuiWorkspaceCoordinator.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxCore import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/Surfaces/CloudAgentNameContext.swift b/Sources/Surfaces/CloudAgentNameContext.swift index 2dfd9c83e882..fe2fb576393d 100644 --- a/Sources/Surfaces/CloudAgentNameContext.swift +++ b/Sources/Surfaces/CloudAgentNameContext.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/Surfaces/CloudAttachmentReconcileDecision.swift b/Sources/Surfaces/CloudAttachmentReconcileDecision.swift index e9a271d093e6..da5a31eb1e2a 100644 --- a/Sources/Surfaces/CloudAttachmentReconcileDecision.swift +++ b/Sources/Surfaces/CloudAttachmentReconcileDecision.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation /// What a refresh pass does with one open pane's resolution result. diff --git a/Sources/Surfaces/CloudTerminalAttachmentResolver.swift b/Sources/Surfaces/CloudTerminalAttachmentResolver.swift index d71ec1129cfe..cbd44eeacab8 100644 --- a/Sources/Surfaces/CloudTerminalAttachmentResolver.swift +++ b/Sources/Surfaces/CloudTerminalAttachmentResolver.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/Surfaces/CloudTerminalLayoutCreation.swift b/Sources/Surfaces/CloudTerminalLayoutCreation.swift index d9a21fae2139..bae20be8bd39 100644 --- a/Sources/Surfaces/CloudTerminalLayoutCreation.swift +++ b/Sources/Surfaces/CloudTerminalLayoutCreation.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/Surfaces/CloudTerminalMutationCommandRunner.swift b/Sources/Surfaces/CloudTerminalMutationCommandRunner.swift index 6b0eaec2cacb..6da9b92ca468 100644 --- a/Sources/Surfaces/CloudTerminalMutationCommandRunner.swift +++ b/Sources/Surfaces/CloudTerminalMutationCommandRunner.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation /// Fences each terminal mutation transport call to its admitted provider lifecycle. diff --git a/Sources/Surfaces/CloudTerminalPaneReservation.swift b/Sources/Surfaces/CloudTerminalPaneReservation.swift index bd3578c70536..4c668c6c87e9 100644 --- a/Sources/Surfaces/CloudTerminalPaneReservation.swift +++ b/Sources/Surfaces/CloudTerminalPaneReservation.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import CmuxTerminal import Foundation diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+CloseTerminal.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+CloseTerminal.swift index 95a2ab872baf..f0a460b08c3d 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+CloseTerminal.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+CloseTerminal.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+Environment.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+Environment.swift index 7f246c723f3f..db5434c34f88 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+Environment.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+Environment.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+FileDelivery.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+FileDelivery.swift index 25ad6886069b..26a20518d174 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+FileDelivery.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+FileDelivery.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+GuestURL.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+GuestURL.swift index 2a52232a9981..01be3153a959 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+GuestURL.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+GuestURL.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+ManualMirror.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+ManualMirror.swift index 17b5dd9ec09a..247b761cbf78 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+ManualMirror.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+ManualMirror.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import CmuxTerminal import CmuxRemoteSession diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+Notifications.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+Notifications.swift index d79ef1b90da5..b81db8802031 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+Notifications.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+Notifications.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+PlacementSync.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+PlacementSync.swift index 63e6bdaf2bc6..a6fd3c6f8ca9 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+PlacementSync.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+PlacementSync.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalCreation.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalCreation.swift index 2862974d23e3..7ec17985b143 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalCreation.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalCreation.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalIO.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalIO.swift index 84b25a77a28a..eaa13ccfdfa8 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalIO.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalIO.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalPrimitives.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalPrimitives.swift index 9069a473ac73..c8ce619140ac 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalPrimitives.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+TerminalPrimitives.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+WorkspaceLifecycle.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+WorkspaceLifecycle.swift index f2f7952194de..23e43147683f 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+WorkspaceLifecycle.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+WorkspaceLifecycle.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation @MainActor diff --git a/Sources/Surfaces/CmuxTuiSurfaceProviderRegistry+Production.swift b/Sources/Surfaces/CmuxTuiSurfaceProviderRegistry+Production.swift index ec517e750d50..b633ffb17fcc 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProviderRegistry+Production.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProviderRegistry+Production.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation extension CmuxTuiSurfaceProviderRegistry { diff --git a/Sources/Surfaces/CmuxTuiSurfaceProviders.swift b/Sources/Surfaces/CmuxTuiSurfaceProviders.swift index ab9628d9f7f4..4d4af4d88cb7 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProviders.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProviders.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxCore import CmuxFoundation import CmuxSettings diff --git a/cmux.xcodeproj/project.pbxproj b/cmux.xcodeproj/project.pbxproj index 054f1542803e..8b05e36ba07b 100644 --- a/cmux.xcodeproj/project.pbxproj +++ b/cmux.xcodeproj/project.pbxproj @@ -1043,32 +1043,12 @@ A12357000000000000000002 /* CloudTreeTerminalNavigationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12357000000000000000001 /* CloudTreeTerminalNavigationCoordinator.swift */; }; 12D5531F8CC84B0197E44AD6 /* CloudTreeTerminalRow+Directory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E2BECA43F1444C82AF95D3 /* CloudTreeTerminalRow+Directory.swift */; }; A12367000000000000000002 /* CloudTreeWorkspaceTitleLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12367000000000000000001 /* CloudTreeWorkspaceTitleLayoutTests.swift */; }; - BB4657280E610A6935E1906B /* CloudTuiClientPaths.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C4E59490776669AA7257527 /* CloudTuiClientPaths.swift */; }; - 17D9A335774C0C326A4B9B31 /* CloudTuiCommandLine+Placement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79FCF680CF0C607A42C4C638 /* CloudTuiCommandLine+Placement.swift */; }; - EFD39AB12F5DF90B066159C4 /* CloudTuiCommandLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47DA953E018A1329DF8C0255 /* CloudTuiCommandLine.swift */; }; A8328D481E6F60922DE0EC82 /* CloudTuiCommandRunning.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A9EEC22A7E9365D0CC3451 /* CloudTuiCommandRunning.swift */; }; 609019DB3EE8FFF70636C933 /* CloudTuiDaemonAnswer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DDDCABD07F6F7DE10F55146 /* CloudTuiDaemonAnswer.swift */; }; - C11322C10000000000000001 /* CloudTuiLegacySnapshotParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11322C20000000000000001 /* CloudTuiLegacySnapshotParser.swift */; }; - C11324110000000000000001 /* CloudTuiManualIOCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11324120000000000000001 /* CloudTuiManualIOCommand.swift */; }; - C11322D10000000000000001 /* CloudTuiManualIOConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11322D20000000000000001 /* CloudTuiManualIOConnection.swift */; }; A838AA211BDA47268C683501 /* CloudTuiManualIOConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A838AA211BDA47268C683502 /* CloudTuiManualIOConnectionTests.swift */; }; - C11324310000000000000001 /* CloudTuiManualIODescriptorLease.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11324320000000000000001 /* CloudTuiManualIODescriptorLease.swift */; }; - C11324410000000000000001 /* CloudTuiManualIOFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11324420000000000000001 /* CloudTuiManualIOFrame.swift */; }; - C11324010000000000000001 /* CloudTuiManualIOFrameDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11324020000000000000001 /* CloudTuiManualIOFrameDecoder.swift */; }; - 985EEC1D91C6F6BB7CF2DA3E /* CloudTuiManualIOGrid+SizingSample.swift in Sources */ = {isa = PBXBuildFile; fileRef = B242BF96D4FAFD2965CC24BC /* CloudTuiManualIOGrid+SizingSample.swift */; }; - C11324210000000000000001 /* CloudTuiManualIOGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11324220000000000000001 /* CloudTuiManualIOGrid.swift */; }; - C11322E10000000000000001 /* CloudTuiManualIOInputRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11322E20000000000000001 /* CloudTuiManualIOInputRouter.swift */; }; - C11323310000000000000001 /* CloudTuiManualIOResizeScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11323320000000000000001 /* CloudTuiManualIOResizeScheduler.swift */; }; - 153A3BF6310918434F479EC4 /* CloudTuiManualMirrorDeadlines.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE1BD1D71927C6ACB18945C5 /* CloudTuiManualMirrorDeadlines.swift */; }; - C11324610000000000000001 /* CloudTuiManualMirrorPhase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11324620000000000000001 /* CloudTuiManualMirrorPhase.swift */; }; - C11324510000000000000001 /* CloudTuiManualMirrorRequestKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11324520000000000000001 /* CloudTuiManualMirrorRequestKind.swift */; }; 124760000000000000000006 /* CloudTuiManualMirrorSession+Capabilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124770000000000000000006 /* CloudTuiManualMirrorSession+Capabilities.swift */; }; C11322F10000000000000001 /* CloudTuiManualMirrorSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11322F20000000000000001 /* CloudTuiManualMirrorSession.swift */; }; - 7DF05591D7CA19FB26633003 /* CloudTuiManualMirrorWatchdog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38376D59ED0A80A041DDAF3A /* CloudTuiManualMirrorWatchdog.swift */; }; - 0D828874A1D4FD0E78EDB07B /* CloudTuiPersistentRequestBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A489F666A3EAAFD7F479F6 /* CloudTuiPersistentRequestBuilder.swift */; }; 8F0DD2F0B3D621848FDA6716 /* CloudTuiPersistentResourceConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C59D0F8731BC1D5A56A253 /* CloudTuiPersistentResourceConnection.swift */; }; - C11324430000000000000001 /* CloudTuiRemoteColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11324440000000000000001 /* CloudTuiRemoteColors.swift */; }; - C11324710000000000000001 /* CloudTuiResolvedSurface.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11324720000000000000001 /* CloudTuiResolvedSurface.swift */; }; C11324910000000000000001 /* CloudTuiSurfaceIDResolution.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11324920000000000000001 /* CloudTuiSurfaceIDResolution.swift */; }; 0F2A151D749994FC57D403CA /* CloudTunnelActivationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E9D51274EC32D701633288E /* CloudTunnelActivationObserver.swift */; }; 430BEAB3F1329807647B9796 /* CloudTunnelAdmission.swift in Sources */ = {isa = PBXBuildFile; fileRef = F92F1CACC68CA3F67E73A822 /* CloudTunnelAdmission.swift */; }; @@ -1366,6 +1346,8 @@ D0F3A0000000000000000004 /* CmuxCloudImagePaste in Frameworks */ = {isa = PBXBuildFile; productRef = D0F3A0000000000000000002 /* CmuxCloudImagePaste */; }; 5996379F78D24368B729D109 /* CmuxCloudMachines in Frameworks */ = {isa = PBXBuildFile; productRef = 79E4513469FB44C7A3690415 /* CmuxCloudMachines */; }; F9400C76C7934BA6BD8DD837 /* CmuxCloudMachines in Frameworks */ = {isa = PBXBuildFile; productRef = 79E4513469FB44C7A3690415 /* CmuxCloudMachines */; }; + C1D7A0000000000000000004 /* CmuxCloudTui in Frameworks */ = {isa = PBXBuildFile; productRef = C1D7A0000000000000000002 /* CmuxCloudTui */; }; + C1D7A0000000000000000005 /* CmuxCloudTui in Frameworks */ = {isa = PBXBuildFile; productRef = C1D7A0000000000000000003 /* CmuxCloudTui */; }; D0F4A0000000000000000003 /* CmuxCloudTunnelCore in Frameworks */ = {isa = PBXBuildFile; productRef = D0F4A0000000000000000002 /* CmuxCloudTunnelCore */; }; D0F4A0000000000000000004 /* CmuxCloudTunnelCore in Frameworks */ = {isa = PBXBuildFile; productRef = D0F4A0000000000000000002 /* CmuxCloudTunnelCore */; }; 5E55500000000000000000E3 /* CmuxCommandPalette in Frameworks */ = {isa = PBXBuildFile; productRef = 5E55500000000000000000E2 /* CmuxCommandPalette */; }; @@ -5369,32 +5351,12 @@ A12357000000000000000001 /* CloudTreeTerminalNavigationCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudTreeTerminalNavigationCoordinator.swift; sourceTree = ""; }; 36E2BECA43F1444C82AF95D3 /* CloudTreeTerminalRow+Directory.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "CloudTreeTerminalRow+Directory.swift"; sourceTree = ""; }; A12367000000000000000001 /* CloudTreeWorkspaceTitleLayoutTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudTreeWorkspaceTitleLayoutTests.swift; sourceTree = ""; }; - 8C4E59490776669AA7257527 /* CloudTuiClientPaths.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudTuiClientPaths.swift; sourceTree = ""; }; - 79FCF680CF0C607A42C4C638 /* CloudTuiCommandLine+Placement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CloudTuiCommandLine+Placement.swift"; sourceTree = ""; }; - 47DA953E018A1329DF8C0255 /* CloudTuiCommandLine.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudTuiCommandLine.swift; sourceTree = ""; }; D7A9EEC22A7E9365D0CC3451 /* CloudTuiCommandRunning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiCommandRunning.swift; sourceTree = ""; }; 3DDDCABD07F6F7DE10F55146 /* CloudTuiDaemonAnswer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiDaemonAnswer.swift; sourceTree = ""; }; - C11322C20000000000000001 /* CloudTuiLegacySnapshotParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiLegacySnapshotParser.swift; sourceTree = ""; }; - C11324120000000000000001 /* CloudTuiManualIOCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiManualIOCommand.swift; sourceTree = ""; }; - C11322D20000000000000001 /* CloudTuiManualIOConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiManualIOConnection.swift; sourceTree = ""; }; A838AA211BDA47268C683502 /* CloudTuiManualIOConnectionTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudTuiManualIOConnectionTests.swift; sourceTree = ""; }; - C11324320000000000000001 /* CloudTuiManualIODescriptorLease.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiManualIODescriptorLease.swift; sourceTree = ""; }; - C11324420000000000000001 /* CloudTuiManualIOFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiManualIOFrame.swift; sourceTree = ""; }; - C11324020000000000000001 /* CloudTuiManualIOFrameDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiManualIOFrameDecoder.swift; sourceTree = ""; }; - B242BF96D4FAFD2965CC24BC /* CloudTuiManualIOGrid+SizingSample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CloudTuiManualIOGrid+SizingSample.swift"; sourceTree = ""; }; - C11324220000000000000001 /* CloudTuiManualIOGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiManualIOGrid.swift; sourceTree = ""; }; - C11322E20000000000000001 /* CloudTuiManualIOInputRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiManualIOInputRouter.swift; sourceTree = ""; }; - C11323320000000000000001 /* CloudTuiManualIOResizeScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiManualIOResizeScheduler.swift; sourceTree = ""; }; - BE1BD1D71927C6ACB18945C5 /* CloudTuiManualMirrorDeadlines.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiManualMirrorDeadlines.swift; sourceTree = ""; }; - C11324620000000000000001 /* CloudTuiManualMirrorPhase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiManualMirrorPhase.swift; sourceTree = ""; }; - C11324520000000000000001 /* CloudTuiManualMirrorRequestKind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiManualMirrorRequestKind.swift; sourceTree = ""; }; 124770000000000000000006 /* CloudTuiManualMirrorSession+Capabilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sources/Cloud/CloudTuiManualMirrorSession+Capabilities.swift"; sourceTree = SOURCE_ROOT; }; C11322F20000000000000001 /* CloudTuiManualMirrorSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiManualMirrorSession.swift; sourceTree = ""; }; - 38376D59ED0A80A041DDAF3A /* CloudTuiManualMirrorWatchdog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiManualMirrorWatchdog.swift; sourceTree = ""; }; - 15A489F666A3EAAFD7F479F6 /* CloudTuiPersistentRequestBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiPersistentRequestBuilder.swift; sourceTree = ""; }; 83C59D0F8731BC1D5A56A253 /* CloudTuiPersistentResourceConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiPersistentResourceConnection.swift; sourceTree = ""; }; - C11324440000000000000001 /* CloudTuiRemoteColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiRemoteColors.swift; sourceTree = ""; }; - C11324720000000000000001 /* CloudTuiResolvedSurface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiResolvedSurface.swift; sourceTree = ""; }; C11324920000000000000001 /* CloudTuiSurfaceIDResolution.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudTuiSurfaceIDResolution.swift; sourceTree = ""; }; 8E9D51274EC32D701633288E /* CloudTunnelActivationObserver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudTunnelActivationObserver.swift; sourceTree = ""; }; F92F1CACC68CA3F67E73A822 /* CloudTunnelAdmission.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudTunnelAdmission.swift; sourceTree = ""; }; @@ -8457,6 +8419,7 @@ D0F2A0000000000000000003 /* CmuxCloudBannerCore in Frameworks */, D0F3A0000000000000000003 /* CmuxCloudImagePaste in Frameworks */, 5996379F78D24368B729D109 /* CmuxCloudMachines in Frameworks */, + C1D7A0000000000000000004 /* CmuxCloudTui in Frameworks */, 5E55500000000000000000E3 /* CmuxCommandPalette in Frameworks */, C0C0A2026091900000000003 /* CmuxComputerUse in Frameworks */, C750100000000000000000A3 /* CmuxControlSocket in Frameworks */, @@ -8569,6 +8532,7 @@ CA52A007CA52A007CA52A007 /* CmuxCanvas in Frameworks */, D0F2A0000000000000000004 /* CmuxCloudBannerCore in Frameworks */, D0F3A0000000000000000004 /* CmuxCloudImagePaste in Frameworks */, + C1D7A0000000000000000005 /* CmuxCloudTui in Frameworks */, D0F4A0000000000000000003 /* CmuxCloudTunnelCore in Frameworks */, 5E55500000000000000000E4 /* CmuxCommandPalette in Frameworks */, C0C0A2026091900000000004 /* CmuxComputerUse in Frameworks */, @@ -9017,34 +8981,17 @@ isa = PBXGroup; children = ( C12636A00000000000000003 /* CloudReadRecoveryObserver.swift */, - C11324120000000000000001 /* CloudTuiManualIOCommand.swift */, - C11324320000000000000001 /* CloudTuiManualIODescriptorLease.swift */, - C11324420000000000000001 /* CloudTuiManualIOFrame.swift */, - C11324440000000000000001 /* CloudTuiRemoteColors.swift */, - C11324020000000000000001 /* CloudTuiManualIOFrameDecoder.swift */, - C11324220000000000000001 /* CloudTuiManualIOGrid.swift */, - C11322C20000000000000001 /* CloudTuiLegacySnapshotParser.swift */, - C11322D20000000000000001 /* CloudTuiManualIOConnection.swift */, 83C59D0F8731BC1D5A56A253 /* CloudTuiPersistentResourceConnection.swift */, - 15A489F666A3EAAFD7F479F6 /* CloudTuiPersistentRequestBuilder.swift */, - C11322E20000000000000001 /* CloudTuiManualIOInputRouter.swift */, - C11323320000000000000001 /* CloudTuiManualIOResizeScheduler.swift */, C11322F20000000000000001 /* CloudTuiManualMirrorSession.swift */, - C11324620000000000000001 /* CloudTuiManualMirrorPhase.swift */, C12537000000000000000304 /* CloudTerminalConnectionPresentationPolicy.swift */, - B242BF96D4FAFD2965CC24BC /* CloudTuiManualIOGrid+SizingSample.swift */, - 38376D59ED0A80A041DDAF3A /* CloudTuiManualMirrorWatchdog.swift */, AB2F34F568E7BBC7AE26E96E /* CloudTerminalAttachmentStatus.swift */, 25416817A702A7AC5A032C33 /* CloudTerminalAttachmentState.swift */, 79064B52A25D34860A759BF8 /* CloudTerminalAttachmentLog.swift */, 3DDDCABD07F6F7DE10F55146 /* CloudTuiDaemonAnswer.swift */, C12537000000000000000234 /* CloudTerminalCreationRetryResolution.swift */, C12537000000000000000232 /* CloudTerminalCreationRequest.swift */, - BE1BD1D71927C6ACB18945C5 /* CloudTuiManualMirrorDeadlines.swift */, D7A9EEC22A7E9365D0CC3451 /* CloudTuiCommandRunning.swift */, 163D243E14A149278220D4E6 /* CloudManualMirrorPresentation.swift */, - C11324520000000000000001 /* CloudTuiManualMirrorRequestKind.swift */, - C11324720000000000000001 /* CloudTuiResolvedSurface.swift */, 7EC4096C4A05D3A58980308A /* NewMachineSheetPresenter.swift */, 28CFF87D42FA4B0993B14DCC /* NewMachineSheetPresenting.swift */, 097212CF5CB04F3B8D671C46 /* CloudWorkspaceGroupDestination.swift */, @@ -9179,8 +9126,6 @@ 9A141BCA857942FE946A3730 /* CloudMachineLinkManager+PrivateRoute.swift */, 49DC04B459E0132708ACD366 /* CloudMachineLink.swift */, 8EAC5EBE3FB2C24E1F29B55C /* CloudGuestURLService.swift */, - 47DA953E018A1329DF8C0255 /* CloudTuiCommandLine.swift */, - 79FCF680CF0C607A42C4C638 /* CloudTuiCommandLine+Placement.swift */, 503C99BC313F48A48235DB1A /* CloudNotificationCorrelation.swift */, A12545000000000000000002 /* CloudNotificationSyncPendingAck.swift */, FCC5CAAFB9D2DBB5186E02EE /* CloudNotificationSync.swift */, @@ -9195,7 +9140,6 @@ D8F40FDCEAFAA24EB9A175A2 /* CloudWireGuardHub.swift */, 32EF99FEA7414C6589A1DFF2 /* CloudWireGuardHub+Production.swift */, 512A93AEF5E820E050D17EF1 /* IPNetworkPrefix.swift */, - 8C4E59490776669AA7257527 /* CloudTuiClientPaths.swift */, 48A1307BCCDB493F49179E0B /* SurfaceResourceDragPayload.swift */, 1CACA5A7C96FFDF0B931E7AA /* CloudTreeOutlineView.swift */, A13086000000000000000005 /* CloudTreeContainerView.swift */, @@ -12881,6 +12825,7 @@ ); name = cmux; packageProductDependencies = ( + C1D7A0000000000000000002 /* CmuxCloudTui */, 5CA7A1060000000000000002 /* CmuxSurfaceCatalogModel */, 79E4513469FB44C7A3690415 /* CmuxCloudMachines */, A5001231 /* Sparkle */, @@ -13036,6 +12981,7 @@ ); name = cmuxTests; packageProductDependencies = ( + C1D7A0000000000000000003 /* CmuxCloudTui */, 5CA7A1060000000000000003 /* CmuxSurfaceCatalogModel */, 79E4513469FB44C7A3690415 /* CmuxCloudMachines */, 29813FE5A6CBC1019289A251 /* CMUXAuthCore */, @@ -13140,6 +13086,7 @@ ); mainGroup = A5001040; packageReferences = ( + C1D7A0000000000000000001 /* XCLocalSwiftPackageReference "CmuxCloudTui" */, 5CA7A1060000000000000001 /* XCLocalSwiftPackageReference "CmuxSurfaceCatalogModel" */, 3915DFE5FE794CDF87D746C9 /* XCLocalSwiftPackageReference "CmuxCloudMachines" */, A5001232 /* XCRemoteSwiftPackageReference "Sparkle" */, @@ -14113,31 +14060,11 @@ C986A0010000000000000001 /* CloudTreeSurfaceDragPasteboardWriter.swift in Sources */, A12357000000000000000002 /* CloudTreeTerminalNavigationCoordinator.swift in Sources */, 12D5531F8CC84B0197E44AD6 /* CloudTreeTerminalRow+Directory.swift in Sources */, - BB4657280E610A6935E1906B /* CloudTuiClientPaths.swift in Sources */, - 17D9A335774C0C326A4B9B31 /* CloudTuiCommandLine+Placement.swift in Sources */, - EFD39AB12F5DF90B066159C4 /* CloudTuiCommandLine.swift in Sources */, A8328D481E6F60922DE0EC82 /* CloudTuiCommandRunning.swift in Sources */, 609019DB3EE8FFF70636C933 /* CloudTuiDaemonAnswer.swift in Sources */, - C11322C10000000000000001 /* CloudTuiLegacySnapshotParser.swift in Sources */, - C11324110000000000000001 /* CloudTuiManualIOCommand.swift in Sources */, - C11322D10000000000000001 /* CloudTuiManualIOConnection.swift in Sources */, - C11324310000000000000001 /* CloudTuiManualIODescriptorLease.swift in Sources */, - C11324410000000000000001 /* CloudTuiManualIOFrame.swift in Sources */, - C11324010000000000000001 /* CloudTuiManualIOFrameDecoder.swift in Sources */, - 985EEC1D91C6F6BB7CF2DA3E /* CloudTuiManualIOGrid+SizingSample.swift in Sources */, - C11324210000000000000001 /* CloudTuiManualIOGrid.swift in Sources */, - C11322E10000000000000001 /* CloudTuiManualIOInputRouter.swift in Sources */, - C11323310000000000000001 /* CloudTuiManualIOResizeScheduler.swift in Sources */, - 153A3BF6310918434F479EC4 /* CloudTuiManualMirrorDeadlines.swift in Sources */, - C11324610000000000000001 /* CloudTuiManualMirrorPhase.swift in Sources */, - C11324510000000000000001 /* CloudTuiManualMirrorRequestKind.swift in Sources */, 124760000000000000000006 /* CloudTuiManualMirrorSession+Capabilities.swift in Sources */, C11322F10000000000000001 /* CloudTuiManualMirrorSession.swift in Sources */, - 7DF05591D7CA19FB26633003 /* CloudTuiManualMirrorWatchdog.swift in Sources */, - 0D828874A1D4FD0E78EDB07B /* CloudTuiPersistentRequestBuilder.swift in Sources */, 8F0DD2F0B3D621848FDA6716 /* CloudTuiPersistentResourceConnection.swift in Sources */, - C11324430000000000000001 /* CloudTuiRemoteColors.swift in Sources */, - C11324710000000000000001 /* CloudTuiResolvedSurface.swift in Sources */, C11324910000000000000001 /* CloudTuiSurfaceIDResolution.swift in Sources */, 0F2A151D749994FC57D403CA /* CloudTunnelActivationObserver.swift in Sources */, 430BEAB3F1329807647B9796 /* CloudTunnelAdmission.swift in Sources */, @@ -18079,6 +18006,10 @@ /* End XCConfigurationList section */ /* Begin XCLocalSwiftPackageReference section */ + C1D7A0000000000000000001 /* XCLocalSwiftPackageReference "CmuxCloudTui" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Packages/macOS/CmuxCloudTui; + }; 5CA7A1060000000000000001 /* XCLocalSwiftPackageReference "CmuxSurfaceCatalogModel" */ = { isa = XCLocalSwiftPackageReference; relativePath = Packages/macOS/CmuxSurfaceCatalogModel; @@ -18378,6 +18309,16 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + C1D7A0000000000000000002 /* CmuxCloudTui */ = { + isa = XCSwiftPackageProductDependency; + package = C1D7A0000000000000000001 /* XCLocalSwiftPackageReference "CmuxCloudTui" */; + productName = CmuxCloudTui; + }; + C1D7A0000000000000000003 /* CmuxCloudTui */ = { + isa = XCSwiftPackageProductDependency; + package = C1D7A0000000000000000001 /* XCLocalSwiftPackageReference "CmuxCloudTui" */; + productName = CmuxCloudTui; + }; 5CA7A1060000000000000002 /* CmuxSurfaceCatalogModel */ = { isa = XCSwiftPackageProductDependency; package = 5CA7A1060000000000000001 /* XCLocalSwiftPackageReference "CmuxSurfaceCatalogModel" */; diff --git a/cmux.xcworkspace/contents.xcworkspacedata b/cmux.xcworkspace/contents.xcworkspacedata index d23ef2df6347..f79b4f16790c 100644 --- a/cmux.xcworkspace/contents.xcworkspacedata +++ b/cmux.xcworkspace/contents.xcworkspacedata @@ -147,6 +147,9 @@ + + diff --git a/cmuxTests/CloudAttachmentReconcileDecisionTests.swift b/cmuxTests/CloudAttachmentReconcileDecisionTests.swift index 03e1ff717f84..907df696f120 100644 --- a/cmuxTests/CloudAttachmentReconcileDecisionTests.swift +++ b/cmuxTests/CloudAttachmentReconcileDecisionTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation import Testing diff --git a/cmuxTests/CloudCommandDeadlineTests.swift b/cmuxTests/CloudCommandDeadlineTests.swift index feedc6325136..15bab1bb6c73 100644 --- a/cmuxTests/CloudCommandDeadlineTests.swift +++ b/cmuxTests/CloudCommandDeadlineTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudCommandOperationTests.swift b/cmuxTests/CloudCommandOperationTests.swift index 1a8aa7c20073..6bd94118485b 100644 --- a/cmuxTests/CloudCommandOperationTests.swift +++ b/cmuxTests/CloudCommandOperationTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudEnvDeliveryTests.swift b/cmuxTests/CloudEnvDeliveryTests.swift index e000fa36d143..b9c3c579ff8f 100644 --- a/cmuxTests/CloudEnvDeliveryTests.swift +++ b/cmuxTests/CloudEnvDeliveryTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudImagePasteMirrorIntegrationTests.swift b/cmuxTests/CloudImagePasteMirrorIntegrationTests.swift index 4235aa67e7c1..031072576484 100644 --- a/cmuxTests/CloudImagePasteMirrorIntegrationTests.swift +++ b/cmuxTests/CloudImagePasteMirrorIntegrationTests.swift @@ -1,4 +1,5 @@ import CmuxCloudImagePaste +import CmuxCloudTui import Foundation import Testing diff --git a/cmuxTests/CloudManualMirrorTransportTests.swift b/cmuxTests/CloudManualMirrorTransportTests.swift index f0d2de9843af..a4ad1f548bb2 100644 --- a/cmuxTests/CloudManualMirrorTransportTests.swift +++ b/cmuxTests/CloudManualMirrorTransportTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudNotificationSyncTests.swift b/cmuxTests/CloudNotificationSyncTests.swift index 39c42fdf2192..f707978dbf31 100644 --- a/cmuxTests/CloudNotificationSyncTests.swift +++ b/cmuxTests/CloudNotificationSyncTests.swift @@ -4,6 +4,7 @@ import CmuxSurfaceCatalogModel #elseif canImport(cmux) @testable import cmux #endif +import CmuxCloudTui import Foundation import Testing diff --git a/cmuxTests/CloudPlacementCoordinatorTests.swift b/cmuxTests/CloudPlacementCoordinatorTests.swift index c3bc2cfaec0c..3bfcaab07d30 100644 --- a/cmuxTests/CloudPlacementCoordinatorTests.swift +++ b/cmuxTests/CloudPlacementCoordinatorTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudPortOpenRegressionTests.swift b/cmuxTests/CloudPortOpenRegressionTests.swift index 72adbc71b4fd..1965779b1627 100644 --- a/cmuxTests/CloudPortOpenRegressionTests.swift +++ b/cmuxTests/CloudPortOpenRegressionTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudPrivateRouteSelectionTests.swift b/cmuxTests/CloudPrivateRouteSelectionTests.swift index cdb118768042..106af6c24ede 100644 --- a/cmuxTests/CloudPrivateRouteSelectionTests.swift +++ b/cmuxTests/CloudPrivateRouteSelectionTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation import Testing diff --git a/cmuxTests/CloudRemoteColorOwnershipTests.swift b/cmuxTests/CloudRemoteColorOwnershipTests.swift index 24214c68124f..3a8ad1d73967 100644 --- a/cmuxTests/CloudRemoteColorOwnershipTests.swift +++ b/cmuxTests/CloudRemoteColorOwnershipTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation import Testing diff --git a/cmuxTests/CloudTerminalAttachmentRecoveryTests.swift b/cmuxTests/CloudTerminalAttachmentRecoveryTests.swift index 8755dd6d7b93..4d5680715afb 100644 --- a/cmuxTests/CloudTerminalAttachmentRecoveryTests.swift +++ b/cmuxTests/CloudTerminalAttachmentRecoveryTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Darwin import Foundation import Testing diff --git a/cmuxTests/CloudTerminalConnectionPresentationPolicyTests.swift b/cmuxTests/CloudTerminalConnectionPresentationPolicyTests.swift index 90444e57bdad..9fcd240197ce 100644 --- a/cmuxTests/CloudTerminalConnectionPresentationPolicyTests.swift +++ b/cmuxTests/CloudTerminalConnectionPresentationPolicyTests.swift @@ -1,4 +1,5 @@ import AppKit +import CmuxCloudTui import Foundation import Testing diff --git a/cmuxTests/CloudTerminalCreationRequestTests.swift b/cmuxTests/CloudTerminalCreationRequestTests.swift index 4919c568c4c6..d70eb0001f8c 100644 --- a/cmuxTests/CloudTerminalCreationRequestTests.swift +++ b/cmuxTests/CloudTerminalCreationRequestTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation import Testing diff --git a/cmuxTests/CloudTerminalLayoutCreationTests.swift b/cmuxTests/CloudTerminalLayoutCreationTests.swift index fe149e6a3599..abd7e54dd36f 100644 --- a/cmuxTests/CloudTerminalLayoutCreationTests.swift +++ b/cmuxTests/CloudTerminalLayoutCreationTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudTerminalMutationLifecycleTests.swift b/cmuxTests/CloudTerminalMutationLifecycleTests.swift index 360578bfb450..d7fef590ec41 100644 --- a/cmuxTests/CloudTerminalMutationLifecycleTests.swift +++ b/cmuxTests/CloudTerminalMutationLifecycleTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation import Testing diff --git a/cmuxTests/CloudTerminalMutationSocketRunner.swift b/cmuxTests/CloudTerminalMutationSocketRunner.swift index 222a878a0075..13bebb33eadf 100644 --- a/cmuxTests/CloudTerminalMutationSocketRunner.swift +++ b/cmuxTests/CloudTerminalMutationSocketRunner.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudTerminalMutationSocketTests.swift b/cmuxTests/CloudTerminalMutationSocketTests.swift index 39d82f1d13e8..c2a30c451d99 100644 --- a/cmuxTests/CloudTerminalMutationSocketTests.swift +++ b/cmuxTests/CloudTerminalMutationSocketTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Darwin import Foundation import Testing diff --git a/cmuxTests/CloudTerminalMutationTestTransport.swift b/cmuxTests/CloudTerminalMutationTestTransport.swift index e8d041936cc0..6d8076ead826 100644 --- a/cmuxTests/CloudTerminalMutationTestTransport.swift +++ b/cmuxTests/CloudTerminalMutationTestTransport.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation #if canImport(cmux_DEV) diff --git a/cmuxTests/CloudTerminalPaneReservationTests.swift b/cmuxTests/CloudTerminalPaneReservationTests.swift index 56b19a68bffc..cc6a51249b7f 100644 --- a/cmuxTests/CloudTerminalPaneReservationTests.swift +++ b/cmuxTests/CloudTerminalPaneReservationTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Foundation import Testing diff --git a/cmuxTests/CloudTuiManualIOConnectionTests.swift b/cmuxTests/CloudTuiManualIOConnectionTests.swift index 536662a60332..70d821cd66b2 100644 --- a/cmuxTests/CloudTuiManualIOConnectionTests.swift +++ b/cmuxTests/CloudTuiManualIOConnectionTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Darwin import Foundation import Testing diff --git a/cmuxTests/CmuxTuiSurfaceProviderRegistryDiscoveryTests.swift b/cmuxTests/CmuxTuiSurfaceProviderRegistryDiscoveryTests.swift index ac21e2104407..2d1bb48c44dc 100644 --- a/cmuxTests/CmuxTuiSurfaceProviderRegistryDiscoveryTests.swift +++ b/cmuxTests/CmuxTuiSurfaceProviderRegistryDiscoveryTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Foundation import Testing #if canImport(cmux_DEV) diff --git a/cmuxTests/CmuxTuiSurfaceProviderTests+LinkCancellation.swift b/cmuxTests/CmuxTuiSurfaceProviderTests+LinkCancellation.swift index 964fa2b6fdbf..b42781535518 100644 --- a/cmuxTests/CmuxTuiSurfaceProviderTests+LinkCancellation.swift +++ b/cmuxTests/CmuxTuiSurfaceProviderTests+LinkCancellation.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import Darwin import Foundation import Testing diff --git a/cmuxTests/CmuxTuiSurfaceProviderTests.swift b/cmuxTests/CmuxTuiSurfaceProviderTests.swift index e89f02bfdd2d..949bcdcca7d2 100644 --- a/cmuxTests/CmuxTuiSurfaceProviderTests.swift +++ b/cmuxTests/CmuxTuiSurfaceProviderTests.swift @@ -1,3 +1,4 @@ +import CmuxCloudTui import CmuxSurfaceCatalogModel import Darwin import Foundation diff --git a/cmuxTests/SurfacePaneFactoryFocusTests.swift b/cmuxTests/SurfacePaneFactoryFocusTests.swift index 6309dcb0843d..d00b2034aafb 100644 --- a/cmuxTests/SurfacePaneFactoryFocusTests.swift +++ b/cmuxTests/SurfacePaneFactoryFocusTests.swift @@ -2,6 +2,7 @@ import AppKit import Bonsplit import CmuxAppKitSupportUI import CmuxAuthRuntime +import CmuxCloudTui import CmuxPanes import CmuxSurfaceCatalogModel import Testing diff --git a/tests/run_cloud_command_deadline_tests.sh b/tests/run_cloud_command_deadline_tests.sh index a3257ddc1b35..f3a625318d53 100755 --- a/tests/run_cloud_command_deadline_tests.sh +++ b/tests/run_cloud_command_deadline_tests.sh @@ -21,17 +21,23 @@ SWIFT # CloudTuiTerminalProjectionTarget lives in the CmuxSurfaceCatalogModel package, not in Sources/Cloud. cp "$ROOT/Packages/macOS/CmuxSurfaceCatalogModel/Sources/CmuxSurfaceCatalogModel/CloudTuiTerminalProjectionTarget.swift" \ "$DEST/Sources/CloudCommandFixture/" -for name in CloudTuiPersistentResourceConnection CloudTuiPersistentRequestBuilder \ +cp "$ROOT/Sources/Cloud/CloudTuiPersistentResourceConnection.swift" "$DEST/Sources/CloudCommandFixture/" +# The rest of the transport lives in the CmuxCloudTui package. +for name in CloudTuiPersistentRequestBuilder \ CloudTuiManualIOConnection CloudTuiManualIODescriptorLease CloudTuiManualIOCommand \ CloudTuiManualIOFrame CloudTuiManualIOFrameDecoder CloudTuiRemoteColors; do - cp "$ROOT/Sources/Cloud/$name.swift" "$DEST/Sources/CloudCommandFixture/" + cp "$ROOT/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/$name.swift" "$DEST/Sources/CloudCommandFixture/" done cp "$ROOT/tests/fixtures/cloud-command-deadlines/StandaloneDependencies.swift" "$DEST/Sources/CloudCommandFixture/" -# The fixture stubs the catalog value types it needs (StandaloneDependencies.swift), so the -# copied sources must not import the real package. -sed -i.bak '/^import CmuxSurfaceCatalogModel$/d' "$DEST/Sources/CloudCommandFixture/"*.swift +# The fixture stubs the catalog value types it needs (StandaloneDependencies.swift) and +# compiles the transport into one module, so the copied sources and tests must not import +# the real packages. +sed -i.bak -e '/^import CmuxSurfaceCatalogModel$/d' -e '/^import CmuxCloudTui$/d' \ + "$DEST/Sources/CloudCommandFixture/"*.swift rm -f "$DEST/Sources/CloudCommandFixture/"*.bak for name in CloudCommandDeadlineClock CloudCommandDeadlineTests CloudTuiManualIOConnectionTests; do cp "$ROOT/cmuxTests/$name.swift" "$DEST/Tests/CloudCommandFixtureTests/" done +sed -i.bak '/^import CmuxCloudTui$/d' "$DEST/Tests/CloudCommandFixtureTests/"*.swift +rm -f "$DEST/Tests/CloudCommandFixtureTests/"*.bak swift test --package-path "$DEST" -Xswiftc -warnings-as-errors diff --git a/tests/test_cli_cloud_hostname.py b/tests/test_cli_cloud_hostname.py index bbdfe05a4d0f..35984f38657b 100644 --- a/tests/test_cli_cloud_hostname.py +++ b/tests/test_cli_cloud_hostname.py @@ -67,7 +67,7 @@ def test_app_label_does_not_resolve_the_computers_name(self) -> None: "xcrun", "swiftc", "-swift-version", "6", "-warnings-as-errors", "-I", str(temporary), "-L", str(temporary), "-lCmuxFoundation", "-Xlinker", "-rpath", "-Xlinker", str(temporary), - str(root / "Sources/Cloud/CloudTuiClientPaths.swift"), + str(root / "Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiClientPaths.swift"), str(root / "tests/fixtures/CloudHostnameFixture.swift"), "-o", str(fixture), ], check=True, capture_output=True) environment = {**os.environ, "DYLD_INSERT_LIBRARIES": self.library} From bb7a8ffd02fe948f15a8aec1aeaec5714e8164b5 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Thu, 24 Sep 2026 05:51:12 -0400 Subject: [PATCH 4/8] Justify the two static namespaces that moved into CmuxCloudTui The package conventions lint rejects public all-static types in packages. CloudTuiCommandLine and CloudTuiRequests were internal static namespaces in the app and moved unchanged, so each carries the lint's inline lint:allow justification, matching the catalog package. Co-Authored-By: Claude Opus 5.5 --- .../CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiCommandLine.swift | 1 + .../Sources/CmuxCloudTui/CloudTuiPersistentRequestBuilder.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiCommandLine.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiCommandLine.swift index 42086a1187a8..076ac59fb3c5 100644 --- a/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiCommandLine.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiCommandLine.swift @@ -6,6 +6,7 @@ import Foundation /// `cmux-tui/spec/cli.md`: `cmux [GLOBAL OPTIONS] [OPTIONS]`, with /// `--socket`/`--json`/`--jsonl` as global options, and `attach --terminal ` as the /// single-terminal renderer (`spec/cli.md` §"attach"). +/// lint:allow namespace-type: moved unchanged from the app target, where it was an internal static namespace; reshaping its static call sites is a separate change from this package move. public struct CloudTuiCommandLine: Sendable { /// `remote connect --device-name … --state-dir … --headless --json [--carrier]`: /// a headless link whose stdout carries `connection-snapshot` JSON lines with the diff --git a/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiPersistentRequestBuilder.swift b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiPersistentRequestBuilder.swift index 3a869125cf61..ff0a2b88f3ef 100644 --- a/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiPersistentRequestBuilder.swift +++ b/Packages/macOS/CmuxCloudTui/Sources/CmuxCloudTui/CloudTuiPersistentRequestBuilder.swift @@ -48,6 +48,7 @@ public struct CloudTuiRequest: Sendable, Equatable { /// Builders for the Cloud application's complete control surface. /// Public IDs are absolute. Never add focused workspace/pane selectors to them. +/// lint:allow namespace-type: moved unchanged from the app target, where it was an internal static namespace; reshaping its static call sites is a separate change from this package move. public enum CloudTuiRequests { public static func snapshotArguments(socketPath: String) -> CloudTuiRequest { CloudTuiRequest("session.snapshot") } public static func createWorkspaceArguments(socketPath: String, name: String? = nil, empty: Bool = false) -> CloudTuiRequest { From 86034c7563593f6c0b60ba93ccac4989fb9c5fb5 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Thu, 24 Sep 2026 06:05:31 -0400 Subject: [PATCH 5/8] ci: give CmuxCloudTui's package tests GhosttyKit CmuxCloudTui depends on CmuxTerminal, which links the GhosttyKit binary target, so its swift test needs the prebuilt framework and the same tolerated binaryTarget diagnostic as the terminal packages. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci-macos.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index f8fabf0ba222..eac4c273aaea 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -2298,7 +2298,7 @@ jobs: echo "selected_packages=$selected" >> "$GITHUB_OUTPUT" echo "selected_count=$count" >> "$GITHUB_OUTPUT" - if grep -qxE 'CmuxTerminal|CmuxTerminalCore' "$selected"; then + if grep -qxE 'CmuxTerminal|CmuxTerminalCore|CmuxCloudTui' "$selected"; then echo "needs_ghosttykit=true" >> "$GITHUB_OUTPUT" else echo "needs_ghosttykit=false" >> "$GITHUB_OUTPUT" @@ -2542,7 +2542,7 @@ jobs: CmuxAgentChat|CmuxAuthRuntime|CmuxFoundation|CmuxIrohTransport|CmuxIrxTransport) ./scripts/ci/run-swift-testing-suites.sh "$pkgdir" ;; - CmuxTerminal|CmuxTerminalCore) + CmuxTerminal|CmuxTerminalCore|CmuxCloudTui) test_status=0 output="$(swift test "${swift_test_args[@]}" 2>&1)" || test_status=$? printf '%s\n' "$output" From ff7f77a8f4bceda26887334b3c854acd87879aac Mon Sep 17 00:00:00 2001 From: Leo Li Date: Thu, 24 Sep 2026 06:15:26 -0400 Subject: [PATCH 6/8] test: import CmuxSurfaceCatalogModel outside the cmux_DEV block The import sat inside #if canImport(cmux_DEV), so the file would not compile when the host module is cmux. Co-Authored-By: Claude Opus 5.5 --- cmuxTests/CloudNotificationSyncTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmuxTests/CloudNotificationSyncTests.swift b/cmuxTests/CloudNotificationSyncTests.swift index 39c42fdf2192..5b535d5f2a09 100644 --- a/cmuxTests/CloudNotificationSyncTests.swift +++ b/cmuxTests/CloudNotificationSyncTests.swift @@ -1,9 +1,9 @@ #if canImport(cmux_DEV) @testable import cmux_DEV -import CmuxSurfaceCatalogModel #elseif canImport(cmux) @testable import cmux #endif +import CmuxSurfaceCatalogModel import Foundation import Testing From 415c7510d185a82cdba8e12acc207fbf96725af3 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Thu, 24 Sep 2026 06:57:16 -0400 Subject: [PATCH 7/8] build: link CmuxTerminal into cmuxTests and stub libghostty for CmuxCloudTuiTests cmuxTests now links CmuxCloudTui, which depends on CmuxTerminal, so Xcode builds CmuxTerminal and CmuxTerminalCore as shared package frameworks instead of static code inside the app. The test bundle then no longer finds their symbols through the host app; link both directly, as it already does for every other package it shares with the app. CmuxCloudTuiTests links CmuxTerminal, whose libghostty calls SwiftPM cannot satisfy from the GhosttyKit archive. Vend CmuxTerminal's existing GhosttyRuntimeTestStubs target as a product and depend on it, the same way CmuxTerminalTests does. Co-Authored-By: Claude Opus 5.5 --- Packages/macOS/CmuxCloudTui/Package.swift | 9 ++++++++- Packages/macOS/CmuxTerminal/Package.swift | 6 ++++++ cmux.xcodeproj/project.pbxproj | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/Packages/macOS/CmuxCloudTui/Package.swift b/Packages/macOS/CmuxCloudTui/Package.swift index dd33febbe97d..379cc2c19560 100644 --- a/Packages/macOS/CmuxCloudTui/Package.swift +++ b/Packages/macOS/CmuxCloudTui/Package.swift @@ -18,6 +18,13 @@ let package = Package( // The files moved out of the app target unchanged; keep its language mode. swiftSettings: [.swiftLanguageMode(.v5)] ), - .testTarget(name: "CmuxCloudTuiTests", dependencies: ["CmuxCloudTui"]) + .testTarget( + name: "CmuxCloudTuiTests", + dependencies: [ + "CmuxCloudTui", + // CmuxTerminal binds libghostty, which SwiftPM cannot link here. + .product(name: "GhosttyRuntimeTestStubs", package: "CmuxTerminal"), + ] + ) ] ) diff --git a/Packages/macOS/CmuxTerminal/Package.swift b/Packages/macOS/CmuxTerminal/Package.swift index c261a5d9919e..8c5c15ccf8dd 100644 --- a/Packages/macOS/CmuxTerminal/Package.swift +++ b/Packages/macOS/CmuxTerminal/Package.swift @@ -12,6 +12,12 @@ let package = Package( name: "CmuxTerminal", targets: ["CmuxTerminal"] ), + // Lets other packages' test targets that link CmuxTerminal satisfy the + // libghostty symbols the same way CmuxTerminalTests does. + .library( + name: "GhosttyRuntimeTestStubs", + targets: ["GhosttyRuntimeTestStubs"] + ), ], dependencies: [ .package(path: "../CmuxFoundation"), diff --git a/cmux.xcodeproj/project.pbxproj b/cmux.xcodeproj/project.pbxproj index 10586eecb83d..8b65831c0059 100644 --- a/cmux.xcodeproj/project.pbxproj +++ b/cmux.xcodeproj/project.pbxproj @@ -1466,7 +1466,9 @@ 906901000000000000000006 /* CmuxTaskManagerMemoryGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 906901000000000000000005 /* CmuxTaskManagerMemoryGroup.swift */; }; 90690100000000000000000C /* CmuxTaskManagerMemoryGroupAttribution.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90690100000000000000000B /* CmuxTaskManagerMemoryGroupAttribution.swift */; }; C750500000000000000000A3 /* CmuxTerminal in Frameworks */ = {isa = PBXBuildFile; productRef = C750500000000000000000A2 /* CmuxTerminal */; }; + C750500000000000000000A5 /* CmuxTerminal in Frameworks */ = {isa = PBXBuildFile; productRef = C750500000000000000000A4 /* CmuxTerminal */; }; C750200000000000000000A3 /* CmuxTerminalCore in Frameworks */ = {isa = PBXBuildFile; productRef = C750200000000000000000A2 /* CmuxTerminalCore */; }; + C750200000000000000000A5 /* CmuxTerminalCore in Frameworks */ = {isa = PBXBuildFile; productRef = C750200000000000000000A4 /* CmuxTerminalCore */; }; C85390090000000000000001 /* CmuxTerminalCore in Frameworks */ = {isa = PBXBuildFile; productRef = C750200000000000000000A2 /* CmuxTerminalCore */; }; CFF12E0000000000000000A3 /* CmuxTestSupport in Frameworks */ = {isa = PBXBuildFile; productRef = CFF12E0000000000000000A2 /* CmuxTestSupport */; }; A2977C204AD69CF268D38EE3 /* CmuxTestWindowReleaseGuard.m in Sources */ = {isa = PBXBuildFile; fileRef = EA9B442F20273D64D2BA1FC5 /* CmuxTestWindowReleaseGuard.m */; }; @@ -8480,6 +8482,8 @@ D96970000000000000000005 /* CmuxSudoBrokerUI in Frameworks */, 5CA7A1060000000000000004 /* CmuxSurfaceCatalogModel in Frameworks */, C01E00AA00000000000000A4 /* CmuxSyntaxHighlighting in Frameworks */, + C750500000000000000000A5 /* CmuxTerminal in Frameworks */, + C750200000000000000000A5 /* CmuxTerminalCore in Frameworks */, 730600040000000000000001 /* CmuxWindowing in Frameworks */, 1A0B0C0D0E0F101112132214 /* IrohLib in Frameworks */, 5E2701040000000000000003 /* Sentry in Frameworks */, @@ -12871,6 +12875,8 @@ name = cmuxTests; packageProductDependencies = ( C1D7A0000000000000000003 /* CmuxCloudTui */, + C750500000000000000000A4 /* CmuxTerminal */, + C750200000000000000000A4 /* CmuxTerminalCore */, 5CA7A1060000000000000003 /* CmuxSurfaceCatalogModel */, 79E4513469FB44C7A3690415 /* CmuxCloudMachines */, 29813FE5A6CBC1019289A251 /* CMUXAuthCore */, @@ -18390,6 +18396,16 @@ package = C750500000000000000000A1 /* XCLocalSwiftPackageReference "CmuxTerminal" */; productName = CmuxTerminal; }; + C750500000000000000000A4 /* CmuxTerminal */ = { + isa = XCSwiftPackageProductDependency; + package = C750500000000000000000A1 /* XCLocalSwiftPackageReference "CmuxTerminal" */; + productName = CmuxTerminal; + }; + C750200000000000000000A4 /* CmuxTerminalCore */ = { + isa = XCSwiftPackageProductDependency; + package = C750200000000000000000A1 /* XCLocalSwiftPackageReference "CmuxTerminalCore" */; + productName = CmuxTerminalCore; + }; CD0CFE5200000000CD0CFE52 /* CmuxSettings */ = { isa = XCSwiftPackageProductDependency; package = CD0CFE5100000000CD0CFE51 /* XCLocalSwiftPackageReference "CmuxSettings" */; From 280279d59e1df4b99b678ea736a249c73fd90176 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Thu, 24 Sep 2026 08:08:14 -0400 Subject: [PATCH 8/8] build: resolve CmuxCloudTui through the host app instead of linking it into cmuxTests Linking CmuxCloudTui into cmuxTests made Xcode build CmuxTerminal and CmuxTerminalCore as shared package frameworks. CmuxTerminalCore then carried its own copy of libghostty, so the app called ghostty_config_serialize in a copy it never initialized and crashed at launch (CI run 35993531179). cmuxTests already reaches CmuxTerminal through the host app without linking it; do the same for CmuxCloudTui and drop the direct terminal links. Co-Authored-By: Claude Opus 5.5 --- cmux.xcodeproj/project.pbxproj | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/cmux.xcodeproj/project.pbxproj b/cmux.xcodeproj/project.pbxproj index 0d846624a410..4e666102c872 100644 --- a/cmux.xcodeproj/project.pbxproj +++ b/cmux.xcodeproj/project.pbxproj @@ -1301,7 +1301,6 @@ 5996379F78D24368B729D109 /* CmuxCloudMachines in Frameworks */ = {isa = PBXBuildFile; productRef = 79E4513469FB44C7A3690415 /* CmuxCloudMachines */; }; F9400C76C7934BA6BD8DD837 /* CmuxCloudMachines in Frameworks */ = {isa = PBXBuildFile; productRef = 79E4513469FB44C7A3690415 /* CmuxCloudMachines */; }; C1D7A0000000000000000004 /* CmuxCloudTui in Frameworks */ = {isa = PBXBuildFile; productRef = C1D7A0000000000000000002 /* CmuxCloudTui */; }; - C1D7A0000000000000000005 /* CmuxCloudTui in Frameworks */ = {isa = PBXBuildFile; productRef = C1D7A0000000000000000003 /* CmuxCloudTui */; }; D0F4A0000000000000000003 /* CmuxCloudTunnelCore in Frameworks */ = {isa = PBXBuildFile; productRef = D0F4A0000000000000000002 /* CmuxCloudTunnelCore */; }; D0F4A0000000000000000004 /* CmuxCloudTunnelCore in Frameworks */ = {isa = PBXBuildFile; productRef = D0F4A0000000000000000002 /* CmuxCloudTunnelCore */; }; 5E55500000000000000000E3 /* CmuxCommandPalette in Frameworks */ = {isa = PBXBuildFile; productRef = 5E55500000000000000000E2 /* CmuxCommandPalette */; }; @@ -1461,9 +1460,7 @@ 906901000000000000000006 /* CmuxTaskManagerMemoryGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 906901000000000000000005 /* CmuxTaskManagerMemoryGroup.swift */; }; 90690100000000000000000C /* CmuxTaskManagerMemoryGroupAttribution.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90690100000000000000000B /* CmuxTaskManagerMemoryGroupAttribution.swift */; }; C750500000000000000000A3 /* CmuxTerminal in Frameworks */ = {isa = PBXBuildFile; productRef = C750500000000000000000A2 /* CmuxTerminal */; }; - C750500000000000000000A5 /* CmuxTerminal in Frameworks */ = {isa = PBXBuildFile; productRef = C750500000000000000000A4 /* CmuxTerminal */; }; C750200000000000000000A3 /* CmuxTerminalCore in Frameworks */ = {isa = PBXBuildFile; productRef = C750200000000000000000A2 /* CmuxTerminalCore */; }; - C750200000000000000000A5 /* CmuxTerminalCore in Frameworks */ = {isa = PBXBuildFile; productRef = C750200000000000000000A4 /* CmuxTerminalCore */; }; C85390090000000000000001 /* CmuxTerminalCore in Frameworks */ = {isa = PBXBuildFile; productRef = C750200000000000000000A2 /* CmuxTerminalCore */; }; CFF12E0000000000000000A3 /* CmuxTestSupport in Frameworks */ = {isa = PBXBuildFile; productRef = CFF12E0000000000000000A2 /* CmuxTestSupport */; }; A2977C204AD69CF268D38EE3 /* CmuxTestWindowReleaseGuard.m in Sources */ = {isa = PBXBuildFile; fileRef = EA9B442F20273D64D2BA1FC5 /* CmuxTestWindowReleaseGuard.m */; }; @@ -8389,7 +8386,6 @@ CA52A007CA52A007CA52A007 /* CmuxCanvas in Frameworks */, D0F2A0000000000000000004 /* CmuxCloudBannerCore in Frameworks */, D0F3A0000000000000000004 /* CmuxCloudImagePaste in Frameworks */, - C1D7A0000000000000000005 /* CmuxCloudTui in Frameworks */, D0F4A0000000000000000003 /* CmuxCloudTunnelCore in Frameworks */, 5E55500000000000000000E4 /* CmuxCommandPalette in Frameworks */, C0C0A2026091900000000004 /* CmuxComputerUse in Frameworks */, @@ -8412,8 +8408,6 @@ D96970000000000000000005 /* CmuxSudoBrokerUI in Frameworks */, 5CA7A1060000000000000004 /* CmuxSurfaceCatalogModel in Frameworks */, C01E00AA00000000000000A4 /* CmuxSyntaxHighlighting in Frameworks */, - C750500000000000000000A5 /* CmuxTerminal in Frameworks */, - C750200000000000000000A5 /* CmuxTerminalCore in Frameworks */, 730600040000000000000001 /* CmuxWindowing in Frameworks */, 1A0B0C0D0E0F101112132214 /* IrohLib in Frameworks */, 5E2701040000000000000003 /* Sentry in Frameworks */, @@ -12768,9 +12762,6 @@ ); name = cmuxTests; packageProductDependencies = ( - C1D7A0000000000000000003 /* CmuxCloudTui */, - C750500000000000000000A4 /* CmuxTerminal */, - C750200000000000000000A4 /* CmuxTerminalCore */, 5CA7A1060000000000000003 /* CmuxSurfaceCatalogModel */, 79E4513469FB44C7A3690415 /* CmuxCloudMachines */, 29813FE5A6CBC1019289A251 /* CMUXAuthCore */, @@ -18035,11 +18026,6 @@ package = C1D7A0000000000000000001 /* XCLocalSwiftPackageReference "CmuxCloudTui" */; productName = CmuxCloudTui; }; - C1D7A0000000000000000003 /* CmuxCloudTui */ = { - isa = XCSwiftPackageProductDependency; - package = C1D7A0000000000000000001 /* XCLocalSwiftPackageReference "CmuxCloudTui" */; - productName = CmuxCloudTui; - }; 5CA7A1060000000000000002 /* CmuxSurfaceCatalogModel */ = { isa = XCSwiftPackageProductDependency; package = 5CA7A1060000000000000001 /* XCLocalSwiftPackageReference "CmuxSurfaceCatalogModel" */; @@ -18259,16 +18245,6 @@ package = C750500000000000000000A1 /* XCLocalSwiftPackageReference "CmuxTerminal" */; productName = CmuxTerminal; }; - C750500000000000000000A4 /* CmuxTerminal */ = { - isa = XCSwiftPackageProductDependency; - package = C750500000000000000000A1 /* XCLocalSwiftPackageReference "CmuxTerminal" */; - productName = CmuxTerminal; - }; - C750200000000000000000A4 /* CmuxTerminalCore */ = { - isa = XCSwiftPackageProductDependency; - package = C750200000000000000000A1 /* XCLocalSwiftPackageReference "CmuxTerminalCore" */; - productName = CmuxTerminalCore; - }; CD0CFE5200000000CD0CFE52 /* CmuxSettings */ = { isa = XCSwiftPackageProductDependency; package = CD0CFE5100000000CD0CFE51 /* XCLocalSwiftPackageReference "CmuxSettings" */;