From 26e14b7ca8b23122a351cc04f45158fc8d2296ad Mon Sep 17 00:00:00 2001 From: Rafal Sikora <24563931+s1korrrr@users.noreply.github.com> Date: Mon, 20 Jul 2026 22:09:09 +0200 Subject: [PATCH 1/2] feat: publish DevScope source --- .editorconfig | 12 + .gitattributes | 5 + .github/CODEOWNERS | 10 + .github/FUNDING.yml | 2 + .github/ISSUE_TEMPLATE/bug.yml | 53 + .github/ISSUE_TEMPLATE/config.yml | 8 + .github/ISSUE_TEMPLATE/feature.yml | 38 + .github/PULL_REQUEST_TEMPLATE.md | 37 + .github/dependabot.yml | 10 + .github/workflows/release-gates.yml | 73 + .github/workflows/secret-scan.yml | 45 + .gitleaks.toml | 19 + CHANGELOG.md | 45 + CODE_OF_CONDUCT.md | 31 + CONTRIBUTING.md | 48 + GOVERNANCE.md | 27 + PRIVACY.md | 49 + Package.swift | 29 + Resources/AppIcon.svg | 46 + Resources/PrivacyInfo.xcprivacy | 21 + SECURITY.md | 34 + SUPPORT.md | 19 + .../DevScope/App/AutomationComposition.swift | 136 + .../App/AutomationDocumentPanel.swift | 124 + Sources/DevScope/App/DevScopeApp.swift | 66 + Sources/DevScope/App/DevScopeIntents.swift | 62 + Sources/DevScope/App/DevScopeSupport.swift | 6 + .../AutomationManagementInfrastructure.swift | 487 ++ ...nchctlAutomationRuntimeStateProvider.swift | 127 + .../LocalAutomationFileSystem.swift | 847 +++ .../SystemAutomationCommandRunner.swift | 329 ++ .../DevScope/Stores/AutomationNotifier.swift | 156 + Sources/DevScope/Stores/AutomationStore.swift | 345 ++ .../FoundationModelGenerationOptions.swift | 22 + .../DevScope/Stores/ProcessNameEnhancer.swift | 151 + .../Stores/ProcessSnapshotWorker.swift | 18 + Sources/DevScope/Stores/ProcessStore.swift | 701 +++ .../Stores/ProcessStoreLiveSnapshot.swift | 34 + .../Stores/WorkflowIntelligenceEnhancer.swift | 180 + .../Views/ApplicationFamilyListView.swift | 213 + .../AutomationApplicationIconStore.swift | 158 + .../Views/AutomationConfirmationView.swift | 62 + .../DevScope/Views/AutomationDetailView.swift | 592 ++ .../DevScope/Views/AutomationEditorView.swift | 202 + .../Views/AutomationIdentityView.swift | 62 + .../DevScope/Views/AutomationRailView.swift | 155 + .../DevScope/Views/AutomationTableView.swift | 234 + .../Views/AutomationVisualIdentity.swift | 108 + .../AutomationWorkspaceLayoutPolicy.swift | 50 + .../Views/AutomationWorkspaceView.swift | 461 ++ Sources/DevScope/Views/ContentView.swift | 1873 ++++++ Sources/DevScope/Views/DevScopeStyle.swift | 131 + .../Views/LiveNotchPanelPresenter.swift | 269 + .../DevScope/Views/ProcessCommandBar.swift | 166 + .../DevScope/Views/ProcessDetailView.swift | 635 ++ .../DevScope/Views/ProcessHierarchyView.swift | 69 + .../Views/ProcessMetricHistoryView.swift | 171 + .../DevScope/Views/ProcessScopePicker.swift | 22 + .../Views/ProcessSelectionPolicy.swift | 36 + .../Views/ProcessStatsPanelView.swift | 1199 ++++ Sources/DevScope/Views/ProcessTableView.swift | 414 ++ Sources/DevScope/Views/SettingsView.swift | 788 +++ .../DevScope/Views/SortableTableHeader.swift | 44 + Sources/DevScope/Views/SponsorSheetView.swift | 101 + Sources/DevScope/Views/TableSortState.swift | 156 + .../AutomationCapabilityPolicy.swift | 166 + .../AutomationEventDetector.swift | 394 ++ .../DevScopeCore/AutomationExecutors.swift | 1007 ++++ .../AutomationInventoryService.swift | 615 ++ Sources/DevScopeCore/AutomationManager.swift | 4006 +++++++++++++ Sources/DevScopeCore/AutomationModels.swift | 254 + .../DevScopeCore/AutomationPresentation.swift | 1004 ++++ .../AutomationProcessCorrelator.swift | 388 ++ Sources/DevScopeCore/AutomationSource.swift | 231 + .../BoundedSystemCommandRunner.swift | 313 + .../DevScopeCore/CronAutomationSource.swift | 461 ++ Sources/DevScopeCore/DevProcess.swift | 221 + .../DevScopeCore/FullDiskAccessGuidance.swift | 67 + Sources/DevScopeCore/GPUMetricProvider.swift | 85 + .../DevScopeCore/InterfacePresentation.swift | 74 + .../LaunchctlServiceTargetClassifier.swift | 25 + .../LaunchdAutomationSource.swift | 787 +++ .../LiveActivityLayoutPolicy.swift | 69 + .../LoginBackgroundAutomationSource.swift | 532 ++ .../DevScopeCore/LongRunningAssessment.swift | 15 + .../DevScopeCore/PersistentCopyStore.swift | 133 + .../ProcessAccessAssessment.swift | 142 + .../DevScopeCore/ProcessActionPolicy.swift | 40 + .../ProcessActionPresentation.swift | 76 + .../DevScopeCore/ProcessCacheIdentity.swift | 26 + .../ProcessClassificationCache.swift | 117 + Sources/DevScopeCore/ProcessClassifier.swift | 954 +++ .../DevScopeCore/ProcessIntelligence.swift | 1020 ++++ Sources/DevScopeCore/ProcessKiller.swift | 322 ++ .../ProcessMetricHistoryStore.swift | 56 + .../DevScopeCore/ProcessPresentation.swift | 692 +++ Sources/DevScopeCore/ProcessScanner.swift | 733 +++ .../ProcessScopePresentation.swift | 456 ++ .../ProcessSignalObservation.swift | 161 + .../DevScopeCore/ProcessSnapshotBuilder.swift | 67 + .../ProcessSnapshotStabilizer.swift | 44 + .../DevScopeCore/ProcessWorkflowCache.swift | 85 + .../DevScopeCore/WorkspaceFactsCache.swift | 144 + THIRD_PARTY_NOTICES.md | 13 + TRADEMARKS.md | 17 + .../AutomationCapabilityPolicyTests.swift | 237 + .../AutomationEventDetectorTests.swift | 304 + .../AutomationExecutorsTests.swift | 1170 ++++ .../AutomationInventoryServiceTests.swift | 785 +++ .../AutomationManagerTests.swift | 5092 +++++++++++++++++ .../AutomationModelsTests.swift | 127 + .../AutomationNotificationPolicyTests.swift | 80 + .../AutomationPresentationTests.swift | 988 ++++ .../AutomationProcessCorrelatorTests.swift | 601 ++ .../AutomationTestFixtures.swift | 864 +++ .../BoundedSystemCommandRunnerTests.swift | 149 + .../CronAutomationSourceTests.swift | 351 ++ .../FullDiskAccessGuidanceTests.swift | 118 + .../InterfacePresentationTests.swift | 110 + .../LaunchdAutomationSourceTests.swift | 871 +++ .../LiveActivityLayoutPolicyTests.swift | 86 + ...LoginBackgroundAutomationSourceTests.swift | 459 ++ .../LongRunningAssessmentTests.swift | 32 + .../PersistentCopyStoreTests.swift | 107 + .../ProcessAccessAssessmentTests.swift | 76 + .../ProcessActionPolicyTests.swift | 80 + .../ProcessActionPresentationTests.swift | 56 + .../ProcessCacheIdentityTests.swift | 89 + .../ProcessClassificationCacheTests.swift | 186 + .../ProcessClassifierTests.swift | 750 +++ .../ProcessIntelligenceTests.swift | 639 +++ .../ProcessKillerTests.swift | 538 ++ .../ProcessMetricHistoryStoreTests.swift | 213 + .../ProcessPresentationTests.swift | 676 +++ .../ProcessScopePresentationTests.swift | 209 + .../ProcessSignalObservationTests.swift | 210 + .../ProcessSnapshotBuilderTests.swift | 130 + .../ProcessSnapshotStabilizerTests.swift | 99 + .../DevScopeCoreTests/ProcessTreeTests.swift | 29 + .../ProcessWorkflowCacheTests.swift | 324 ++ .../ReleaseScriptTests.swift | 1851 ++++++ .../SystemProcessScannerTests.swift | 946 +++ .../WorkspaceFactsCacheTests.swift | 159 + .../AutomationApplicationIconStoreTests.swift | 423 ++ .../AutomationDocumentPanelTests.swift | 85 + .../AutomationNotifierTests.swift | 238 + .../DevScopeTests/AutomationStoreTests.swift | 416 ++ .../AutomationVisualIdentityTests.swift | 80 + ...AutomationWorkspaceLayoutPolicyTests.swift | 87 + ...lAutomationRuntimeStateProviderTests.swift | 173 + .../LocalAutomationFileSystemTests.swift | 564 ++ .../ProcessSelectionPolicyTests.swift | 112 + .../ProcessStoreAutomationTests.swift | 199 + .../SystemAutomationCommandRunnerTests.swift | 317 + Tests/DevScopeTests/TableSortStateTests.swift | 301 + .../WorkflowIntelligenceEnhancerTests.swift | 42 + config/DevScope.entitlements | 10 + config/DevScopeDeveloperID.entitlements | 8 + docs/APP_STORE_READINESS.md | 144 + docs/ARCHITECTURE.md | 38 + docs/INSTALLATION.md | 100 + docs/RELEASE_CHECKLIST.md | 71 + docs/RELEASING.md | 100 + docs/open-source/OPEN_SOURCE_MANIFEST.json | 33 + docs/open-source/OPEN_SOURCE_STATUS.md | 46 + docs/open-source/PUBLICATION_GATE_MATRIX.md | 18 + docs/open-source/SBOM.spdx.json | 33 + docs/release/0.1.0/APP_REVIEW_NOTES.md | 11 + docs/release/0.1.0/APP_STORE_CHECKLIST.md | 23 + docs/release/0.1.0/BLOCKERS.md | 31 + docs/release/0.1.0/PRIVACY_DATA_MAP.md | 19 + docs/release/0.1.0/RELEASE_MANIFEST.json | 88 + docs/release/0.1.0/RELEASE_NOTES.md | 23 + docs/release/0.1.0/RELEASE_STATUS.md | 56 + docs/release/0.1.0/SECURITY_STATUS.md | 42 + docs/release/0.1.0/TEST_EVIDENCE.md | 48 + .../devscope-main-sanitized-2026-07-15.png | Bin 0 -> 2472263 bytes script/build_and_run.sh | 106 + script/build_release_bundle.sh | 111 + script/check_open_source_readiness.sh | 457 ++ script/generate_app_icon.swift | 263 + script/install_local_full_build.sh | 38 + script/package_app_store.sh | 75 + script/package_community_preview.sh | 101 + script/package_developer_id.sh | 57 + script/sandbox_smoke.sh | 15 + script/validate_release_bundle.sh | 166 + 187 files changed, 53994 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/CODEOWNERS create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/bug.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/release-gates.yml create mode 100644 .github/workflows/secret-scan.yml create mode 100644 .gitleaks.toml create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 GOVERNANCE.md create mode 100644 PRIVACY.md create mode 100644 Package.swift create mode 100644 Resources/AppIcon.svg create mode 100644 Resources/PrivacyInfo.xcprivacy create mode 100644 SECURITY.md create mode 100644 SUPPORT.md create mode 100644 Sources/DevScope/App/AutomationComposition.swift create mode 100644 Sources/DevScope/App/AutomationDocumentPanel.swift create mode 100644 Sources/DevScope/App/DevScopeApp.swift create mode 100644 Sources/DevScope/App/DevScopeIntents.swift create mode 100644 Sources/DevScope/App/DevScopeSupport.swift create mode 100644 Sources/DevScope/Infrastructure/AutomationManagementInfrastructure.swift create mode 100644 Sources/DevScope/Infrastructure/LaunchctlAutomationRuntimeStateProvider.swift create mode 100644 Sources/DevScope/Infrastructure/LocalAutomationFileSystem.swift create mode 100644 Sources/DevScope/Infrastructure/SystemAutomationCommandRunner.swift create mode 100644 Sources/DevScope/Stores/AutomationNotifier.swift create mode 100644 Sources/DevScope/Stores/AutomationStore.swift create mode 100644 Sources/DevScope/Stores/FoundationModelGenerationOptions.swift create mode 100644 Sources/DevScope/Stores/ProcessNameEnhancer.swift create mode 100644 Sources/DevScope/Stores/ProcessSnapshotWorker.swift create mode 100644 Sources/DevScope/Stores/ProcessStore.swift create mode 100644 Sources/DevScope/Stores/ProcessStoreLiveSnapshot.swift create mode 100644 Sources/DevScope/Stores/WorkflowIntelligenceEnhancer.swift create mode 100644 Sources/DevScope/Views/ApplicationFamilyListView.swift create mode 100644 Sources/DevScope/Views/AutomationApplicationIconStore.swift create mode 100644 Sources/DevScope/Views/AutomationConfirmationView.swift create mode 100644 Sources/DevScope/Views/AutomationDetailView.swift create mode 100644 Sources/DevScope/Views/AutomationEditorView.swift create mode 100644 Sources/DevScope/Views/AutomationIdentityView.swift create mode 100644 Sources/DevScope/Views/AutomationRailView.swift create mode 100644 Sources/DevScope/Views/AutomationTableView.swift create mode 100644 Sources/DevScope/Views/AutomationVisualIdentity.swift create mode 100644 Sources/DevScope/Views/AutomationWorkspaceLayoutPolicy.swift create mode 100644 Sources/DevScope/Views/AutomationWorkspaceView.swift create mode 100644 Sources/DevScope/Views/ContentView.swift create mode 100644 Sources/DevScope/Views/DevScopeStyle.swift create mode 100644 Sources/DevScope/Views/LiveNotchPanelPresenter.swift create mode 100644 Sources/DevScope/Views/ProcessCommandBar.swift create mode 100644 Sources/DevScope/Views/ProcessDetailView.swift create mode 100644 Sources/DevScope/Views/ProcessHierarchyView.swift create mode 100644 Sources/DevScope/Views/ProcessMetricHistoryView.swift create mode 100644 Sources/DevScope/Views/ProcessScopePicker.swift create mode 100644 Sources/DevScope/Views/ProcessSelectionPolicy.swift create mode 100644 Sources/DevScope/Views/ProcessStatsPanelView.swift create mode 100644 Sources/DevScope/Views/ProcessTableView.swift create mode 100644 Sources/DevScope/Views/SettingsView.swift create mode 100644 Sources/DevScope/Views/SortableTableHeader.swift create mode 100644 Sources/DevScope/Views/SponsorSheetView.swift create mode 100644 Sources/DevScope/Views/TableSortState.swift create mode 100644 Sources/DevScopeCore/AutomationCapabilityPolicy.swift create mode 100644 Sources/DevScopeCore/AutomationEventDetector.swift create mode 100644 Sources/DevScopeCore/AutomationExecutors.swift create mode 100644 Sources/DevScopeCore/AutomationInventoryService.swift create mode 100644 Sources/DevScopeCore/AutomationManager.swift create mode 100644 Sources/DevScopeCore/AutomationModels.swift create mode 100644 Sources/DevScopeCore/AutomationPresentation.swift create mode 100644 Sources/DevScopeCore/AutomationProcessCorrelator.swift create mode 100644 Sources/DevScopeCore/AutomationSource.swift create mode 100644 Sources/DevScopeCore/BoundedSystemCommandRunner.swift create mode 100644 Sources/DevScopeCore/CronAutomationSource.swift create mode 100644 Sources/DevScopeCore/DevProcess.swift create mode 100644 Sources/DevScopeCore/FullDiskAccessGuidance.swift create mode 100644 Sources/DevScopeCore/GPUMetricProvider.swift create mode 100644 Sources/DevScopeCore/InterfacePresentation.swift create mode 100644 Sources/DevScopeCore/LaunchctlServiceTargetClassifier.swift create mode 100644 Sources/DevScopeCore/LaunchdAutomationSource.swift create mode 100644 Sources/DevScopeCore/LiveActivityLayoutPolicy.swift create mode 100644 Sources/DevScopeCore/LoginBackgroundAutomationSource.swift create mode 100644 Sources/DevScopeCore/LongRunningAssessment.swift create mode 100644 Sources/DevScopeCore/PersistentCopyStore.swift create mode 100644 Sources/DevScopeCore/ProcessAccessAssessment.swift create mode 100644 Sources/DevScopeCore/ProcessActionPolicy.swift create mode 100644 Sources/DevScopeCore/ProcessActionPresentation.swift create mode 100644 Sources/DevScopeCore/ProcessCacheIdentity.swift create mode 100644 Sources/DevScopeCore/ProcessClassificationCache.swift create mode 100644 Sources/DevScopeCore/ProcessClassifier.swift create mode 100644 Sources/DevScopeCore/ProcessIntelligence.swift create mode 100644 Sources/DevScopeCore/ProcessKiller.swift create mode 100644 Sources/DevScopeCore/ProcessMetricHistoryStore.swift create mode 100644 Sources/DevScopeCore/ProcessPresentation.swift create mode 100644 Sources/DevScopeCore/ProcessScanner.swift create mode 100644 Sources/DevScopeCore/ProcessScopePresentation.swift create mode 100644 Sources/DevScopeCore/ProcessSignalObservation.swift create mode 100644 Sources/DevScopeCore/ProcessSnapshotBuilder.swift create mode 100644 Sources/DevScopeCore/ProcessSnapshotStabilizer.swift create mode 100644 Sources/DevScopeCore/ProcessWorkflowCache.swift create mode 100644 Sources/DevScopeCore/WorkspaceFactsCache.swift create mode 100644 THIRD_PARTY_NOTICES.md create mode 100644 TRADEMARKS.md create mode 100644 Tests/DevScopeCoreTests/AutomationCapabilityPolicyTests.swift create mode 100644 Tests/DevScopeCoreTests/AutomationEventDetectorTests.swift create mode 100644 Tests/DevScopeCoreTests/AutomationExecutorsTests.swift create mode 100644 Tests/DevScopeCoreTests/AutomationInventoryServiceTests.swift create mode 100644 Tests/DevScopeCoreTests/AutomationManagerTests.swift create mode 100644 Tests/DevScopeCoreTests/AutomationModelsTests.swift create mode 100644 Tests/DevScopeCoreTests/AutomationNotificationPolicyTests.swift create mode 100644 Tests/DevScopeCoreTests/AutomationPresentationTests.swift create mode 100644 Tests/DevScopeCoreTests/AutomationProcessCorrelatorTests.swift create mode 100644 Tests/DevScopeCoreTests/AutomationTestFixtures.swift create mode 100644 Tests/DevScopeCoreTests/BoundedSystemCommandRunnerTests.swift create mode 100644 Tests/DevScopeCoreTests/CronAutomationSourceTests.swift create mode 100644 Tests/DevScopeCoreTests/FullDiskAccessGuidanceTests.swift create mode 100644 Tests/DevScopeCoreTests/InterfacePresentationTests.swift create mode 100644 Tests/DevScopeCoreTests/LaunchdAutomationSourceTests.swift create mode 100644 Tests/DevScopeCoreTests/LiveActivityLayoutPolicyTests.swift create mode 100644 Tests/DevScopeCoreTests/LoginBackgroundAutomationSourceTests.swift create mode 100644 Tests/DevScopeCoreTests/LongRunningAssessmentTests.swift create mode 100644 Tests/DevScopeCoreTests/PersistentCopyStoreTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessAccessAssessmentTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessActionPolicyTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessActionPresentationTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessCacheIdentityTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessClassificationCacheTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessClassifierTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessIntelligenceTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessKillerTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessMetricHistoryStoreTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessPresentationTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessScopePresentationTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessSignalObservationTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessSnapshotBuilderTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessSnapshotStabilizerTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessTreeTests.swift create mode 100644 Tests/DevScopeCoreTests/ProcessWorkflowCacheTests.swift create mode 100644 Tests/DevScopeCoreTests/ReleaseScriptTests.swift create mode 100644 Tests/DevScopeCoreTests/SystemProcessScannerTests.swift create mode 100644 Tests/DevScopeCoreTests/WorkspaceFactsCacheTests.swift create mode 100644 Tests/DevScopeTests/AutomationApplicationIconStoreTests.swift create mode 100644 Tests/DevScopeTests/AutomationDocumentPanelTests.swift create mode 100644 Tests/DevScopeTests/AutomationNotifierTests.swift create mode 100644 Tests/DevScopeTests/AutomationStoreTests.swift create mode 100644 Tests/DevScopeTests/AutomationVisualIdentityTests.swift create mode 100644 Tests/DevScopeTests/AutomationWorkspaceLayoutPolicyTests.swift create mode 100644 Tests/DevScopeTests/LaunchctlAutomationRuntimeStateProviderTests.swift create mode 100644 Tests/DevScopeTests/LocalAutomationFileSystemTests.swift create mode 100644 Tests/DevScopeTests/ProcessSelectionPolicyTests.swift create mode 100644 Tests/DevScopeTests/ProcessStoreAutomationTests.swift create mode 100644 Tests/DevScopeTests/SystemAutomationCommandRunnerTests.swift create mode 100644 Tests/DevScopeTests/TableSortStateTests.swift create mode 100644 Tests/DevScopeTests/WorkflowIntelligenceEnhancerTests.swift create mode 100644 config/DevScope.entitlements create mode 100644 config/DevScopeDeveloperID.entitlements create mode 100644 docs/APP_STORE_READINESS.md create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/INSTALLATION.md create mode 100644 docs/RELEASE_CHECKLIST.md create mode 100644 docs/RELEASING.md create mode 100644 docs/open-source/OPEN_SOURCE_MANIFEST.json create mode 100644 docs/open-source/OPEN_SOURCE_STATUS.md create mode 100644 docs/open-source/PUBLICATION_GATE_MATRIX.md create mode 100644 docs/open-source/SBOM.spdx.json create mode 100644 docs/release/0.1.0/APP_REVIEW_NOTES.md create mode 100644 docs/release/0.1.0/APP_STORE_CHECKLIST.md create mode 100644 docs/release/0.1.0/BLOCKERS.md create mode 100644 docs/release/0.1.0/PRIVACY_DATA_MAP.md create mode 100644 docs/release/0.1.0/RELEASE_MANIFEST.json create mode 100644 docs/release/0.1.0/RELEASE_NOTES.md create mode 100644 docs/release/0.1.0/RELEASE_STATUS.md create mode 100644 docs/release/0.1.0/SECURITY_STATUS.md create mode 100644 docs/release/0.1.0/TEST_EVIDENCE.md create mode 100644 docs/screenshots/devscope-main-sanitized-2026-07-15.png create mode 100755 script/build_and_run.sh create mode 100755 script/build_release_bundle.sh create mode 100755 script/check_open_source_readiness.sh create mode 100644 script/generate_app_icon.swift create mode 100755 script/install_local_full_build.sh create mode 100755 script/package_app_store.sh create mode 100755 script/package_community_preview.sh create mode 100755 script/package_developer_id.sh create mode 100755 script/sandbox_smoke.sh create mode 100755 script/validate_release_bundle.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f15441a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..26af077 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +* text=auto eol=lf + +*.icns binary +*.png binary +*.zip binary diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..f2f844e --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,10 @@ +# Default owner and security-sensitive release surfaces. +* @s1korrrr +/Sources/DevScopeCore/ @s1korrrr +/Sources/DevScope/Infrastructure/ @s1korrrr +/Sources/DevScope/Stores/ @s1korrrr +/config/ @s1korrrr +/script/ @s1korrrr +/.github/workflows/ @s1korrrr +/SECURITY.md @s1korrrr +/PRIVACY.md @s1korrrr diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..e7c7ff3 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +custom: + - https://buymeacoffee.com/s1korrrr diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..3a9b9ed --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,53 @@ +name: Bug report +description: Report a reproducible DevScope defect without sensitive process data. +title: "[Bug]: " +labels: + - bug +body: + - type: markdown + attributes: + value: >- + Do not report security vulnerabilities here. Redact usernames, commands, + process arguments, project names, credentials, and private paths. + - type: input + id: version + attributes: + label: DevScope version or commit + placeholder: 0.1.0 or commit SHA + validations: + required: true + - type: input + id: environment + attributes: + label: macOS and Mac architecture + placeholder: macOS 15.x, Apple silicon + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behavior + validations: + required: true + - type: textarea + id: actual + attributes: + label: Actual behavior + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: Minimal reproduction + description: Use only fixture processes or automation data you can safely modify. + validations: + required: true + - type: checkboxes + id: checks + attributes: + label: Safety checks + options: + - label: I removed secrets and sensitive process, command, and path data. + required: true + - label: This is not a vulnerability report. + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..a6bc2fc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Security vulnerability + url: https://github.com/rsitech-ai/devscope/security/advisories/new + about: Report suspected vulnerabilities privately. Do not open a public issue. + - name: Support and privacy guidance + url: https://github.com/rsitech-ai/devscope/blob/main/SUPPORT.md + about: Read the support and data-redaction guidance before sharing diagnostics. diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml new file mode 100644 index 0000000..b716764 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -0,0 +1,38 @@ +name: Feature request +description: Propose a focused improvement to DevScope. +title: "[Feature]: " +labels: + - enhancement +body: + - type: textarea + id: problem + attributes: + label: Problem + description: What user problem is not handled today? + validations: + required: true + - type: textarea + id: outcome + attributes: + label: Desired outcome + description: Describe the result rather than a required implementation. + validations: + required: true + - type: textarea + id: safety + attributes: + label: Security, privacy, and destructive-action impact + description: Note any new data access, process control, automation mutation, or network behavior. + validations: + required: true + - type: dropdown + id: distribution + attributes: + label: Distribution surface + options: + - Full-feature direct download + - Sandbox-compatible build + - Both, with explicitly different behavior + - Not applicable + validations: + required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..ac74e78 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,37 @@ +## Outcome + + + +## Related issue + + + +## Change type + +- [ ] Bug fix +- [ ] Feature +- [ ] Documentation +- [ ] CI, packaging, or maintenance +- [ ] Security or privacy hardening + +## Risk and boundaries + + + +## Breaking-change impact + + + +## Verification + +- [ ] Added or updated focused tests for behavior changes +- [ ] `./script/check_open_source_readiness.sh` +- [ ] `swift test -Xswiftc -warnings-as-errors` +- [ ] `swift build -c release -Xswiftc -warnings-as-errors` +- [ ] Relevant exact-app or package smoke completed +- [ ] Public docs and changelog updated when guarantees changed +- [ ] No secrets, private paths, unredacted logs, generated bundles, or unrelated changes included + +## Evidence + + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..fab0b1b --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 5 + labels: + - dependencies + - github-actions diff --git a/.github/workflows/release-gates.yml b/.github/workflows/release-gates.yml new file mode 100644 index 0000000..f27f33b --- /dev/null +++ b/.github/workflows/release-gates.yml @@ -0,0 +1,73 @@ +name: Release Gates + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +concurrency: + group: release-gates-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + macos: + name: macOS ${{ matrix.architecture }} + strategy: + fail-fast: false + matrix: + include: + - runner: macos-26 + architecture: arm64 + - runner: macos-26-intel + architecture: x86_64 + runs-on: ${{ matrix.runner }} + timeout-minutes: 30 + + steps: + - name: Check out source + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Record Apple toolchain + run: | + xcodebuild -version + swift --version + + - name: Validate shell scripts + run: bash -n script/*.sh + + - name: Validate public-source readiness + run: ./script/check_open_source_readiness.sh + + - name: Run tests with warnings as errors + run: swift test -Xswiftc -warnings-as-errors + + - name: Build Release with warnings as errors + run: swift build -c release -Xswiftc -warnings-as-errors + + - name: Build and validate sandbox release bundle + env: + DEVSCOPE_DIST_DIR: ${{ runner.temp }}/devscope-release + run: | + app_path="$(./script/build_release_bundle.sh)" + DEVSCOPE_REQUIRE_SANDBOX=1 ./script/validate_release_bundle.sh "$app_path" + file "$app_path/Contents/MacOS/DevScope" + + - name: Build and validate full-access release bundle + env: + DEVSCOPE_DIST_DIR: ${{ runner.temp }}/devscope-full-access + DEVSCOPE_ENTITLEMENTS: ${{ github.workspace }}/config/DevScopeDeveloperID.entitlements + run: | + app_path="$(./script/build_release_bundle.sh)" + DEVSCOPE_REQUIRE_SANDBOX=0 \ + DEVSCOPE_REQUIRE_APPLE_EVENTS=1 \ + ./script/validate_release_bundle.sh "$app_path" + file "$app_path/Contents/MacOS/DevScope" + + - name: Verify tracked files stayed clean + run: git diff --exit-code diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml new file mode 100644 index 0000000..0ef1549 --- /dev/null +++ b/.github/workflows/secret-scan.yml @@ -0,0 +1,45 @@ +name: Secret Scan + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: secret-scan-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + gitleaks: + name: Full-history Gitleaks scan + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Check out full history + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Download checksum-pinned Gitleaks CLI + env: + GITLEAKS_VERSION: 8.30.1 + GITLEAKS_LINUX_X64_SHA256: 551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb + run: | + archive="$RUNNER_TEMP/gitleaks.tar.gz" + curl --fail --location --silent --show-error \ + "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \ + --output "$archive" + echo "$GITLEAKS_LINUX_X64_SHA256 $archive" | sha256sum --check --strict + tar -xzf "$archive" -C "$RUNNER_TEMP" gitleaks + "$RUNNER_TEMP/gitleaks" version + + - name: Scan complete repository history + run: | + "$RUNNER_TEMP/gitleaks" git --redact --no-banner --no-color . diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..73d38a2 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,19 @@ +title = "DevScope Gitleaks configuration" + +[extend] +useDefault = true + +[[allowlists]] +description = "Synthetic credential-redaction fixtures in security regression tests" +condition = "AND" +regexTarget = "line" +targetRules = ["curl-auth-header", "curl-auth-user"] +paths = [ + '''(^|/)Tests/DevScopeCoreTests/ProcessPresentationTests\.swift$''', + '''(^|/)Tests/DevScopeCoreTests/AutomationManagerTests\.swift$''', +] +regexes = [ + '''SYNTH_[A-Z0-9_]+''', + '''curl\s+(?:-u|--user)\s+(?:'user:curl secret'|user:curlsecret|user:curl --header-shaped secret;stillsecret)''', + '''Authorization: secret-value''', +] diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f88eb86 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,45 @@ +# Changelog + +All notable changes to DevScope are documented here. The format follows +[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and releases use +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Changed + +- Recorded Rafal Sikora as copyright owner and RSI Tech as the public + maintainer, with `rsitech.ai` and `info@rsitech.ai` as the project routes. +- Updated release evidence to distinguish the installed Developer ID identity + from the still-missing notarization credentials and Keychain profile. + +## [0.1.0-preview.1] - 2026-07-20 + +### Added + +- Apache-2.0 licensing and an explicit open-source publication contract. +- Contribution, governance, security, privacy, support, and conduct policies. +- Deterministic public-source readiness validation. +- Installation, update, uninstall, troubleshooting, and maintainer release procedures. +- CI validation for both sandbox and full-access bundle policies. +- Fresh-history publication under the `rsitech-ai` GitHub organization. +- A reproducible checksummed community-preview packager with an embedded + unnotarized-build warning. + +### Fixed + +- Recovery-cache payload limits now account for JSON escaping and reject oversized encoded metadata before writing, so every accepted save remains readable. +- Sensitive HTTP header redaction now removes complete Digest, AWS signature, proxy-authorization, and cookie values. +- Partial filesystem mutations now expose exact local recovery locations with Copy Path and Reveal in Finder actions. +- Table context-menu command copying now updates the same durable redacted recovery payload as the command bar. +- Development staging now re-signs the completed app bundle so Info.plist and resources are covered. +- Release app bundles now embed Apache-2.0 and third-party notice files. + +### Added + +- Native macOS running-activity inspection with process hierarchy and live resource metrics. +- Local process actions with explicit TERM, tree-termination, and force-kill boundaries. +- Automation inventory and management for supported local launchd, login-item, and cron sources. +- Optional on-device Foundation Models enrichment with deterministic scanner facts as authority. +- Local recovery for bounded copied-command and export data. +- Universal local and sandbox validation paths for Apple silicon and Intel slices. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..43ae4ac --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,31 @@ +# Code of Conduct + +## Our commitment + +We commit to a welcoming, harassment-free project for everyone, regardless of +background, identity, experience, education, appearance, or ability. + +## Expected behavior + +- Be respectful, specific, and constructive. +- Assume good intent while addressing the actual technical or community impact. +- Accept correction, protect private information, and give credit. +- Keep discussion relevant to the project and its users. + +## Unacceptable behavior + +Harassment, threats, discriminatory language, sexualized attention, deliberate +intimidation, doxxing, sustained disruption, and retaliation are not tolerated. + +## Reporting and enforcement + +For ordinary moderation, contact the maintainers through a repository discussion +or issue. For a private report, email `info@rsitech.ai` with the subject +`DevScope conduct report`, or use GitHub's private vulnerability-reporting flow +described in [SECURITY.md](SECURITY.md) and prefix the report title with +`Conduct report`. Do not include more personal data than necessary. + +Maintainers may edit or remove contributions and may temporarily or permanently +restrict participation when behavior is unsafe or persistently disruptive. +Reports will be handled as privately as the investigation permits. This policy +applies in project spaces and when someone publicly represents the project. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a645af6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,48 @@ +# Contributing to DevScope + +Thank you for helping improve DevScope. This project accepts focused bug fixes, +tests, documentation, accessibility improvements, and maintainable product work. +RSI Tech maintains the project; see or contact +`info@rsitech.ai` for project matters that do not belong in a public issue. + +## Development environment + +- macOS 14 or newer +- A current stable Xcode toolchain with Swift 6 support +- Git and a POSIX-compatible shell + +Clone the repository, then run: + +```bash +./script/check_open_source_readiness.sh +swift test -Xswiftc -warnings-as-errors +swift build -c release -Xswiftc -warnings-as-errors +./script/build_and_run.sh --verify +``` + +The application is a Swift package. `DevScopeCore` holds testable domain and +system logic, while the `DevScope` executable composes macOS services and SwiftUI. +See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the boundaries. + +## Safety rules + +- Never commit secrets, credentials, provisioning profiles, private keys, or unredacted user logs. +- Never test termination behavior against a user's real work. Use an isolated fixture process you created and can safely stop. +- Keep process facts deterministic. On-device model output may enrich presentation but must not control destructive actions. +- Preserve confirmation, authorization, and result-verification boundaries around process and automation mutations. +- Add a focused regression test for behavior changes and one realistic smoke check when system integration changes. +- Keep generated bundles, archives, derived data, and local release evidence out of Git. + +## Pull requests + +1. Open an issue first for large behavior or architecture changes. +2. Keep the change scoped and explain the user-facing outcome and risk. +3. Include tests and update public documentation when behavior or guarantees change. +4. Complete the pull-request checklist and wait for required checks and review. +5. Do not include unrelated formatting or generated-file churn. + +By contributing, you agree that your contribution is licensed under the +project's [Apache License 2.0](LICENSE), consistent with section 5 of that license. + +Security vulnerabilities must not be filed as public issues. Follow +[SECURITY.md](SECURITY.md). diff --git a/GOVERNANCE.md b/GOVERNANCE.md new file mode 100644 index 0000000..45caea0 --- /dev/null +++ b/GOVERNANCE.md @@ -0,0 +1,27 @@ +# Governance + +DevScope is currently a maintainer-led project. + +## Roles and decisions + +- Rafal Sikora is the copyright owner. +- RSI Tech is the public project, release, and security maintainer. +- `@s1korrrr` is the maintainer's operational GitHub account. +- Public and confidential project contact: `info@rsitech.ai`. +- Project website: . +- Contributors propose changes through issues and pull requests. +- Maintainers decide by technical merit, product safety, compatibility, and long-term maintenance cost. +- Material architecture, security, privacy, or product-scope changes require an explicit maintainer decision recorded in the pull request or repository documentation. + +No contributor has an automatic right to merge, publish, sign, notarize, or make +a release. Security fixes may be developed privately until coordinated disclosure. + +## Releases + +A release requires passing source, test, package, and review gates. Publishing +source does not imply that a downloadable binary is Developer ID signed, +notarized, App Store reviewed, or supported on untested hardware. Release notes +must state those boundaries accurately. + +If the maintainer group grows, this document will be updated before granting +additional merge or release authority. diff --git a/PRIVACY.md b/PRIVACY.md new file mode 100644 index 0000000..47fd559 --- /dev/null +++ b/PRIVACY.md @@ -0,0 +1,49 @@ +# Privacy + +DevScope is a local-first macOS process and automation inspection tool. It does +not require an account, operate a project backend, include advertising or +analytics SDKs, or intentionally send process data to the project maintainer. + +## Data processed on the Mac + +Depending on enabled features and macOS permissions, DevScope may read: + +- running-process identifiers, names, commands, executable paths, parent/child relationships, resource usage, and working-directory hints; +- supported local automation metadata, such as launchd definitions, login items, and user cron entries; +- user preferences and bounded local history needed for graphs, favorites, watch state, and workflow presentation; +- the last command or export copied through DevScope, stored in a bounded recovery file in Application Support so it can be restored after relaunch. + +Process commands and paths can contain sensitive project names, arguments, or +local usernames. Review exports and screenshots before sharing them. + +## Optional on-device intelligence + +When Apple Foundation Models are available and the feature is enabled, DevScope +may use the operating system's on-device model APIs to improve process names or +workflow notes. Model output is presentation-only; deterministic scanner facts +remain authoritative and model output does not authorize destructive actions. + +## Storage and deletion + +DevScope stores preferences and bounded support data in the current user's macOS +Library containers/Application Support locations. Preferences can be reset in +the app where offered. Removing DevScope's related Application Support and +preferences data deletes local app state; uninstalling the app alone may leave +those standard macOS support files in place. + +## Network behavior + +Core scanning and automation features do not require a project-operated network +service. User-invoked support actions can open the documented Buy Me a Coffee +website in the default browser. macOS or Apple frameworks may have their own +system behavior and policies. + +## Permissions and control + +DevScope does not use privileged APIs. macOS controls which processes and files +the current user may inspect or change. Full Disk Access may improve visibility +for protected metadata, but it does not bypass normal process ownership or +authorization rules. App Sandbox builds have a substantially reduced feature set. + +For a security issue, follow [SECURITY.md](SECURITY.md). For other privacy +questions, open a repository issue without attaching sensitive process data. diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..3f40276 --- /dev/null +++ b/Package.swift @@ -0,0 +1,29 @@ +// swift-tools-version: 6.0 + +import PackageDescription + +let package = Package( + name: "DevScope", + platforms: [ + .macOS(.v14) + ], + products: [ + .library(name: "DevScopeCore", targets: ["DevScopeCore"]), + .executable(name: "DevScope", targets: ["DevScope"]) + ], + targets: [ + .target(name: "DevScopeCore"), + .executableTarget( + name: "DevScope", + dependencies: ["DevScopeCore"] + ), + .testTarget( + name: "DevScopeCoreTests", + dependencies: ["DevScopeCore"] + ), + .testTarget( + name: "DevScopeTests", + dependencies: ["DevScope", "DevScopeCore"] + ) + ] +) diff --git a/Resources/AppIcon.svg b/Resources/AppIcon.svg new file mode 100644 index 0000000..9ccb48f --- /dev/null +++ b/Resources/AppIcon.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/PrivacyInfo.xcprivacy b/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..534bd86 --- /dev/null +++ b/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,21 @@ + + + + + NSPrivacyTracking + + NSPrivacyCollectedDataTypes + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + + diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..785edc7 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,34 @@ +# Security Policy + +## Supported versions + +Security fixes target the latest code on `main` and the most recent published +release. Older or unofficial builds may not receive fixes. + +## Report a vulnerability privately + +Do not open a public issue for a suspected vulnerability. Use the repository's +GitHub **Security** tab and choose **Report a vulnerability**: + + + +Include the affected version or commit, macOS version, reproducible steps, +impact, and any suggested mitigation. Remove secrets, real process arguments, +private paths, and unrelated personal data. If private vulnerability reporting +is unavailable, open a minimal public issue asking the maintainer to establish a +private contact channel; do not disclose the vulnerability there. If GitHub's +private reporting flow is unavailable, email `info@rsitech.ai` with the subject +`DevScope security report`. Do not send secrets that are not required to +reproduce the issue. + +RSI Tech will assess reports on a best-effort basis, coordinate a fix and +disclosure when appropriate, and credit reporters who want attribution. This is +not an emergency-response service and no response-time guarantee is offered. + +## Security boundaries + +- DevScope runs with the current macOS user's authority and does not provide privilege escalation. +- Process and automation actions are security-sensitive. Confirmation, scope, authorization, and post-action verification must remain explicit. +- Optional Foundation Models output is never trusted to authorize a destructive action. +- Local logs, exports, commands, and paths may contain sensitive information and must be redacted before sharing. +- A local ad-hoc build is not a production distribution artifact. Public binaries require Developer ID signing, hardened runtime, notarization, stapling, and Gatekeeper validation. diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 0000000..2d7c13d --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,19 @@ +# Support + +DevScope is maintained on a best-effort basis without a support SLA. + +RSI Tech maintains the public project at . Public and +confidential project contact: `info@rsitech.ai`. + +- Use [GitHub Issues](https://github.com/rsitech-ai/devscope/issues) for reproducible bugs and focused feature requests. +- Use [GitHub Discussions](https://github.com/rsitech-ai/devscope/discussions) for usage questions when Discussions are enabled. +- Follow [SECURITY.md](SECURITY.md) for private vulnerability reports. +- Read [PRIVACY.md](PRIVACY.md) before attaching logs, screenshots, exports, commands, or local paths. + +When filing a bug, include the DevScope version or commit, macOS version, Mac +architecture, expected and actual behavior, and minimal reproduction steps. +Redact usernames, process arguments, project names, credentials, and private +paths. Do not ask contributors to run destructive commands against real work. + +The full-feature product currently targets direct macOS distribution. Sandbox +validation does not imply feature parity with the direct-download build. diff --git a/Sources/DevScope/App/AutomationComposition.swift b/Sources/DevScope/App/AutomationComposition.swift new file mode 100644 index 0000000..7e98880 --- /dev/null +++ b/Sources/DevScope/App/AutomationComposition.swift @@ -0,0 +1,136 @@ +import Darwin +import DevScopeCore +import Foundation + +@MainActor +enum DevScopeComposition { + struct Stores { + let processStore: ProcessStore + let automationStore: AutomationStore + let automationNotifier: AutomationNotifier + } + + static func make() -> Stores { + let currentUID = getuid() + let homeDirectory = FileManager.default.homeDirectoryForCurrentUser.standardizedFileURL + let applicationSupport = FileManager.default.urls( + for: .applicationSupportDirectory, + in: .userDomainMask + ).first?.appending(path: "DevScope", directoryHint: .isDirectory) + ?? homeDirectory.appending(path: "Library/Application Support/DevScope", directoryHint: .isDirectory) + let transactionRoot = applicationSupport.appending( + path: "AutomationTransactions", + directoryHint: .isDirectory + ) + let recoveryRoot = applicationSupport.appending( + path: "AutomationRecovery", + directoryHint: .isDirectory + ) + let launchAgentsRoot = homeDirectory.appending( + path: "Library/LaunchAgents", + directoryHint: .isDirectory + ) + + let runner = SystemAutomationCommandRunner() + let fileSystem = LocalAutomationFileSystem() + try? fileSystem.createDirectory(transactionRoot, permissions: 0o700) + try? fileSystem.createDirectory(recoveryRoot, permissions: 0o700) + let processScanner = SystemProcessScanner() + let legacyListing = OSACommandLegacyLoginItemAdapter(runner: runner) + let sources: [any AutomationSource] = [ + LaunchdAutomationSource( + fileSystem: fileSystem, + currentUID: currentUID, + roots: LaunchdAutomationSource.defaultRoots(homeDirectory: homeDirectory), + runtimeStateProvider: LaunchctlAutomationRuntimeStateProvider(runner: runner) + ), + BackgroundTaskAutomationSource(runner: runner), + LegacyLoginItemAutomationSource(adapter: legacyListing, currentUID: currentUID), + CronAutomationSource( + commandRunner: runner, + currentUID: currentUID, + currentUsername: currentUsername(for: currentUID) + ), + ] + let inventoryService = AutomationInventoryService( + sources: sources, + minimumRefreshInterval: 60 + ) + let executor = AutomationExecutorRouter( + launchd: LaunchdAutomationExecutor( + runner: runner, + guiUID: currentUID, + fileSystem: fileSystem + ), + cron: CronAutomationExecutor( + runner: runner, + fileSystem: fileSystem, + currentUID: currentUID, + processSnapshot: { + try processScanner.snapshot(includeCurrentDirectories: false) + } + ), + legacy: LegacyLoginItemAutomationExecutor( + runner: runner, + listing: legacyListing, + currentUID: currentUID + ) + ) + let recoverableSources = AutomationRecoverableSourceProvider( + runner: runner, + fileSystem: fileSystem, + legacyListing: legacyListing, + root: transactionRoot, + currentUID: currentUID + ) + let authority = AutomationAuthorityContextBuilder( + fileSystem: fileSystem, + currentUID: currentUID, + launchAgentsRoot: launchAgentsRoot, + transactionRoot: transactionRoot + ) + let manager = AutomationManager( + fileSystem: fileSystem, + executor: executor, + capabilityContext: { record in + try authority.context(for: record) + }, + destinationContext: { record, destination in + try authority.context(for: record, destination: destination) + }, + recoverableSource: { record in + try await recoverableSources.capture(record) + }, + refresh: { + await inventoryService.refreshAfterCurrent() + }, + refreshProcesses: { + try processScanner.snapshot(includeCurrentDirectories: false) + }, + backupDirectory: recoveryRoot, + currentUID: currentUID + ) + + return Stores( + processStore: ProcessStore(scanner: processScanner), + automationStore: AutomationStore( + inventoryService: inventoryService, + manager: manager, + capabilityDecisionProvider: AutomationAuthorityCapabilityDecisionProvider( + authority: authority + ), + destinationProvider: AutomationManagementDestinationProvider( + transactionRoot: transactionRoot + ) + ), + automationNotifier: AutomationNotifier() + ) + } + + private static func currentUsername(for uid: uid_t) -> String { + guard let passwordEntry = getpwuid(uid), let name = passwordEntry.pointee.pw_name else { + return NSUserName() + } + return String(cString: name) + } +} diff --git a/Sources/DevScope/App/AutomationDocumentPanel.swift b/Sources/DevScope/App/AutomationDocumentPanel.swift new file mode 100644 index 0000000..459a640 --- /dev/null +++ b/Sources/DevScope/App/AutomationDocumentPanel.swift @@ -0,0 +1,124 @@ +import AppKit +import Darwin +import DevScopeCore +import Foundation +import UniformTypeIdentifiers + +struct AutomationImportedDocument: Identifiable { + let id = UUID() + let sourceURL: URL + let data: Data +} + +@MainActor +final class AutomationDocumentPanel { + static let maximumImportBytes = 2 * 1_024 * 1_024 + + static func validatedImportData(at url: URL) throws -> Data { + let descriptor = Darwin.open( + url.path, + O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK + ) + guard descriptor >= 0 else { throw currentPOSIXError() } + let handle = FileHandle(fileDescriptor: descriptor, closeOnDealloc: true) + defer { try? handle.close() } + + var before = stat() + guard fstat(descriptor, &before) == 0 else { throw currentPOSIXError() } + guard (before.st_mode & mode_t(S_IFMT)) == mode_t(S_IFREG) else { + throw CocoaError(.fileReadCorruptFile) + } + guard before.st_size >= 0, + before.st_size <= off_t(maximumImportBytes) else { + throw CocoaError(.fileReadTooLarge) + } + + var data = Data() + while data.count <= maximumImportBytes { + let remaining = maximumImportBytes + 1 - data.count + guard let chunk = try handle.read(upToCount: min(64 * 1_024, remaining)), + !chunk.isEmpty else { break } + data.append(chunk) + } + guard data.count <= maximumImportBytes else { throw CocoaError(.fileReadTooLarge) } + + var after = stat() + guard fstat(descriptor, &after) == 0 else { throw currentPOSIXError() } + guard before.st_dev == after.st_dev, + before.st_ino == after.st_ino, + before.st_size == after.st_size, + before.st_mtimespec.tv_sec == after.st_mtimespec.tv_sec, + before.st_mtimespec.tv_nsec == after.st_mtimespec.tv_nsec, + data.count == Int(after.st_size) else { + throw CocoaError(.fileReadCorruptFile) + } + return data + } + + private static func currentPOSIXError() -> POSIXError { + POSIXError(POSIXErrorCode(rawValue: errno) ?? .EIO) + } + + static func writeExport(_ artifact: AutomationExportArtifact, to url: URL) throws { + let fileManager = FileManager.default + let temporaryURL = url.deletingLastPathComponent() + .appendingPathComponent(".devscope-export-\(UUID().uuidString)") + guard fileManager.createFile( + atPath: temporaryURL.path, + contents: nil, + attributes: [.posixPermissions: 0o600] + ) else { + throw CocoaError(.fileWriteUnknown) + } + defer { try? fileManager.removeItem(at: temporaryURL) } + + let handle = try FileHandle(forWritingTo: temporaryURL) + do { + try handle.write(contentsOf: artifact.data) + try handle.synchronize() + try handle.close() + } catch { + try? handle.close() + throw error + } + + if fileManager.fileExists(atPath: url.path) { + _ = try fileManager.replaceItemAt( + url, + withItemAt: temporaryURL, + backupItemName: nil, + options: [.usingNewMetadataOnly] + ) + } else { + try fileManager.moveItem(at: temporaryURL, to: url) + } + try fileManager.setAttributes([.posixPermissions: 0o600], ofItemAtPath: url.path) + let attributes = try fileManager.attributesOfItem(atPath: url.path) + guard (attributes[.posixPermissions] as? NSNumber)?.intValue == 0o600 else { + throw CocoaError(.fileWriteNoPermission) + } + } + + func openImport() throws -> AutomationImportedDocument? { + let panel = NSOpenPanel() + panel.title = "Import Automation" + panel.prompt = "Preview Import" + panel.allowsMultipleSelection = false + panel.canChooseDirectories = false + panel.allowedContentTypes = [.propertyList, .plainText, .json] + guard panel.runModal() == .OK, let url = panel.url else { return nil } + return AutomationImportedDocument( + sourceURL: url, + data: try Self.validatedImportData(at: url) + ) + } + + func save(_ artifact: AutomationExportArtifact) throws -> Bool { + let panel = NSSavePanel() + panel.title = artifact.isRedacted ? "Save Redacted Automation Export" : "Save Unredacted Automation Export" + panel.nameFieldStringValue = artifact.suggestedFilename + guard panel.runModal() == .OK, let url = panel.url else { return false } + try Self.writeExport(artifact, to: url) + return true + } +} diff --git a/Sources/DevScope/App/DevScopeApp.swift b/Sources/DevScope/App/DevScopeApp.swift new file mode 100644 index 0000000..eb09dbb --- /dev/null +++ b/Sources/DevScope/App/DevScopeApp.swift @@ -0,0 +1,66 @@ +import AppKit +import SwiftUI + +@main +struct DevScopeApp: App { + @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate + @StateObject private var processStore: ProcessStore + @StateObject private var automationStore: AutomationStore + @StateObject private var automationNotifier: AutomationNotifier + + init() { + let composition = DevScopeComposition.make() + _processStore = StateObject(wrappedValue: composition.processStore) + _automationStore = StateObject(wrappedValue: composition.automationStore) + _automationNotifier = StateObject(wrappedValue: composition.automationNotifier) + } + + var body: some Scene { + Window("DevScope", id: "main") { + ContentView( + store: processStore, + automationStore: automationStore, + automationNotifier: automationNotifier + ) + .frame(minWidth: 1120, minHeight: 620) + } + .windowResizability(.contentSize) + .commands { + CommandGroup(replacing: .appSettings) { + SettingsLink { + Text("Settings...") + } + .keyboardShortcut(",", modifiers: [.command]) + } + + CommandGroup(after: .appInfo) { + Button("Refresh Current Workspace") { + NotificationCenter.default.post(name: .devScopeRefreshRequested, object: nil) + } + .keyboardShortcut("r", modifiers: [.command]) + + Button("Restore Last Copy") { + NotificationCenter.default.post(name: .devScopeRestoreLastCopyRequested, object: nil) + } + .keyboardShortcut("e", modifiers: [.command, .shift]) + } + } + + Settings { + SettingsView(automationNotifier: automationNotifier) + } + } +} + +final class AppDelegate: NSObject, NSApplicationDelegate { + func applicationDidFinishLaunching(_ notification: Notification) { + NSApp.setActivationPolicy(.regular) + NSApp.activate(ignoringOtherApps: true) + } +} + +extension Notification.Name { + static let devScopeRefreshRequested = Notification.Name("devScopeRefreshRequested") + static let devScopeRestoreLastCopyRequested = Notification.Name( + "devScopeRestoreLastCopyRequested") +} diff --git a/Sources/DevScope/App/DevScopeIntents.swift b/Sources/DevScope/App/DevScopeIntents.swift new file mode 100644 index 0000000..05ebc11 --- /dev/null +++ b/Sources/DevScope/App/DevScopeIntents.swift @@ -0,0 +1,62 @@ +import AppIntents +import AppKit +import Foundation + +struct OpenDevScopeIntent: AppIntent { + static let title: LocalizedStringResource = "Open DevScope" + static let description = IntentDescription("Open DevScope to inspect local apps, services, agents, and processes.") + static var openAppWhenRun: Bool { true } + + @available(macOS 26.0, *) + static var supportedModes: IntentModes { + .foreground(.immediate) + } + + @MainActor + func perform() async throws -> some IntentResult { + NSApp.activate(ignoringOtherApps: true) + return .result() + } +} + +struct RefreshDevScopeIntent: AppIntent { + static let title: LocalizedStringResource = "Refresh DevScope" + static let description = IntentDescription("Refresh the DevScope running activity list.") + static var openAppWhenRun: Bool { true } + + @available(macOS 26.0, *) + static var supportedModes: IntentModes { + .foreground(.immediate) + } + + @MainActor + func perform() async throws -> some IntentResult { + NSApp.activate(ignoringOtherApps: true) + NotificationCenter.default.post(name: .devScopeRefreshRequested, object: nil) + return .result() + } +} + +struct DevScopeShortcuts: AppShortcutsProvider { + static var appShortcuts: [AppShortcut] { + AppShortcut( + intent: OpenDevScopeIntent(), + phrases: [ + "Open \(.applicationName)", + "Show \(.applicationName)" + ], + shortTitle: "Open DevScope", + systemImageName: "scope" + ) + + AppShortcut( + intent: RefreshDevScopeIntent(), + phrases: [ + "Refresh \(.applicationName)", + "Scan with \(.applicationName)" + ], + shortTitle: "Refresh", + systemImageName: "arrow.clockwise" + ) + } +} diff --git a/Sources/DevScope/App/DevScopeSupport.swift b/Sources/DevScope/App/DevScopeSupport.swift new file mode 100644 index 0000000..071262b --- /dev/null +++ b/Sources/DevScope/App/DevScopeSupport.swift @@ -0,0 +1,6 @@ +import Foundation + +enum DevScopeSupport { + static let buyMeACoffeeURLString = "https://buymeacoffee.com/s1korrrr" + static let buyMeACoffeeURL = URL(string: buyMeACoffeeURLString)! +} diff --git a/Sources/DevScope/Infrastructure/AutomationManagementInfrastructure.swift b/Sources/DevScope/Infrastructure/AutomationManagementInfrastructure.swift new file mode 100644 index 0000000..e185624 --- /dev/null +++ b/Sources/DevScope/Infrastructure/AutomationManagementInfrastructure.swift @@ -0,0 +1,487 @@ +import CryptoKit +import Darwin +import DevScopeCore +import Foundation + +struct AutomationExecutorRouter: AutomationMutationApplying { + let launchd: LaunchdAutomationExecutor + let cron: CronAutomationExecutor + let legacy: LegacyLoginItemAutomationExecutor + + func apply( + _ operation: AutomationOperation, + to record: AutomationRecord, + linkedProcesses: [ClassifiedDevProcess], + proposedSourceURL: URL? + ) async throws -> AutomationExecutorResult { + switch record.sourceKind { + case .launchAgent: + try await launchd.apply( + operation, + to: record, + linkedProcesses: linkedProcesses, + proposedSourceURL: proposedSourceURL + ) + case .crontab: + try await cron.apply( + operation, + to: record, + linkedProcesses: linkedProcesses, + proposedSourceURL: proposedSourceURL + ) + case .legacyLoginItem: + try await legacy.apply( + operation, + to: record, + linkedProcesses: linkedProcesses, + proposedSourceURL: proposedSourceURL + ) + case .launchDaemon, .serviceManagement: + throw AutomationExecutorError.unsupportedSource + } + } + + func restorePreTransactionState( + _ state: AutomationPreTransactionState, + for record: AutomationRecord, + recovery: AutomationVerifiedRecoveryInput, + linkedProcesses: [ClassifiedDevProcess], + expectedAppliedState: AutomationExecutorAppliedState? + ) async throws -> AutomationExecutorResult { + switch record.sourceKind { + case .launchAgent: + try await launchd.restorePreTransactionState( + state, + for: record, + recovery: recovery, + linkedProcesses: linkedProcesses, + expectedAppliedState: expectedAppliedState + ) + case .crontab: + try await cron.restorePreTransactionState( + state, + for: record, + recovery: recovery, + linkedProcesses: linkedProcesses, + expectedAppliedState: expectedAppliedState + ) + case .legacyLoginItem: + try await legacy.restorePreTransactionState( + state, + for: record, + recovery: recovery, + linkedProcesses: linkedProcesses, + expectedAppliedState: expectedAppliedState + ) + case .launchDaemon, .serviceManagement: + throw AutomationExecutorError.unsupportedSource + } + } +} + +actor AutomationRecoverableSourceProvider { + private let runner: any AutomationCommandRunning + private let fileSystem: any AutomationFileSystem + private let legacyListing: any LegacyLoginItemListing + private let root: URL + private let currentUID: uid_t + + init( + runner: any AutomationCommandRunning, + fileSystem: any AutomationFileSystem, + legacyListing: any LegacyLoginItemListing, + root: URL, + currentUID: uid_t + ) { + self.runner = runner + self.fileSystem = fileSystem + self.legacyListing = legacyListing + self.root = root.standardizedFileURL + self.currentUID = currentUID + } + + func capture(_ record: AutomationRecord) async throws -> AutomationRecoverableSource { + guard record.ownership == .user, record.ownerUID == currentUID else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + switch record.sourceKind { + case .crontab: + return try await captureCron(record) + case .legacyLoginItem: + return try await captureLegacyLoginItem(record) + default: + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + } + + private func captureCron( + _ record: AutomationRecord + ) async throws -> AutomationRecoverableSource { + let listed = try await runner.run(AutomationCommand( + executable: "/usr/bin/crontab", + arguments: ["-l"], + environment: ["LC_ALL": "C"] + )) + guard listed.status == 0, + let checksum = CronDocumentChecksum.checksum(listed.standardOutput), + checksum == record.sourceChecksum else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + return try materialize( + data: listed.standardOutput, + checksum: checksum, + at: Self.transactionURL(for: record, root: root) + ) + } + + private func captureLegacyLoginItem( + _ record: AutomationRecord + ) async throws -> AutomationRecoverableSource { + guard let selectedPath = record.executable else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + let canonicalSelectedPath = URL(fileURLWithPath: selectedPath).standardizedFileURL.path + let items = try await legacyListing.currentUserLoginItems() + guard let descriptor = items.first(where: { + $0.name == record.label + && URL(fileURLWithPath: $0.path).standardizedFileURL.path == canonicalSelectedPath + }) else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + let data = try LegacyLoginItemRecoveryDocument.encode( + selectedRecord: record, + descriptor: descriptor, + currentUID: currentUID + ) + let checksum = LegacyLoginItemRecoveryDocument.checksum(ofEncodedDocument: data) + guard checksum == record.sourceChecksum else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + return try materialize( + data: data, + checksum: checksum, + at: Self.transactionURL(for: record, root: root) + ) + } + + private func materialize( + data: Data, + checksum: String, + at url: URL + ) throws -> AutomationRecoverableSource { + try fileSystem.createDirectory(root, permissions: 0o700) + let rootMetadata = try fileSystem.metadata(for: root) + guard rootMetadata.canonicalURL.standardizedFileURL.path == root.path, + rootMetadata.ownerUID == currentUID, + !rootMetadata.isSymbolicLink, + rootMetadata.permissions == 0o700 else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + guard let rootAuthorization = AutomationDirectoryAuthorization( + directoryURL: root, + resourceIdentifier: rootMetadata.resourceIdentifier + ) else { throw AutomationManagerConfigurationError.recoverableSourceUnavailable } + if fileSystem.itemExists(at: url) { + let metadata = try fileSystem.metadata(for: url) + guard metadata.canonicalURL.standardizedFileURL.path == url.path, + metadata.ownerUID == currentUID, + !metadata.isSymbolicLink, + metadata.permissions == 0o600 else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + if try fileSystem.read(url) != data { + let staged = try fileSystem.writeStagedFile( + nextTo: url, + data: data, + permissions: 0o600, + authorization: rootAuthorization + ) + guard let destination = AutomationFileAuthorization( + fileURL: url, + directory: rootAuthorization, + expectation: .existing(resourceIdentifier: metadata.resourceIdentifier ?? "") + ) else { throw AutomationManagerConfigurationError.recoverableSourceUnavailable } + try install(destination: destination, staged: staged) + } + } else { + let staged = try fileSystem.writeStagedFile( + nextTo: url, + data: data, + permissions: 0o600, + authorization: rootAuthorization + ) + guard let destination = AutomationFileAuthorization( + fileURL: url, + directory: rootAuthorization, + expectation: .absent + ) else { throw AutomationManagerConfigurationError.recoverableSourceUnavailable } + try install(destination: destination, staged: staged) + } + let storedData = try fileSystem.read(url) + let metadata = try fileSystem.metadata(for: url) + guard storedData == data, + metadata.canonicalURL.standardizedFileURL.path == url.path, + metadata.ownerUID == currentUID, + !metadata.isSymbolicLink, + metadata.permissions == 0o600 else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + return AutomationRecoverableSource( + transactionURL: url, + data: data, + checksum: checksum, + metadata: metadata + ) + } + + private func install( + destination: AutomationFileAuthorization, + staged: AutomationStagedFile + ) throws { + let outcome: AutomationFileMutationOutcome + do { + outcome = try fileSystem.replaceItem(at: destination, with: staged) + } catch let partial as AutomationFilePartialMutation { + throw partial + } catch { + try failAfterCleaningStaged(staged, originalError: error) + } + guard case .unchanged = outcome else { + return + } + try failAfterCleaningStaged( + staged, + originalError: AutomationManagerConfigurationError.recoverableSourceUnavailable + ) + } + + private func failAfterCleaningStaged( + _ staged: AutomationStagedFile, + originalError: Error + ) throws -> Never { + do { + guard case .committed = try fileSystem.removeItem(staged.authorization) else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + } catch { + let directoryMatches = (try? fileSystem.metadata( + for: staged.authorization.directory.directoryURL + ).resourceIdentifier) == staged.authorization.directory.resourceIdentifier + let leafMatches: Bool + if case .existing(let identity) = staged.authorization.expectation { + leafMatches = (try? fileSystem.metadata(for: staged.url).resourceIdentifier) == identity + } else { + leafMatches = false + } + guard directoryMatches && leafMatches else { throw originalError } + throw AutomationFilePartialMutation( + kind: .remove, + commitState: .unknown, + observedFiles: [staged.authorization], + recoveryHandle: staged.authorization, + resultURL: staged.url + ) + } + throw originalError + } + + nonisolated static func transactionURL( + for record: AutomationRecord, + root: URL + ) -> URL { + switch record.sourceKind { + case .crontab: + root.appending(path: "current-crontab", directoryHint: .notDirectory) + case .legacyLoginItem: + root.appending( + path: "legacy-\(rawChecksum(Data(record.id.rawValue.utf8))).json", + directoryHint: .notDirectory + ) + default: + root.appending(path: "unsupported", directoryHint: .notDirectory) + } + } + + nonisolated private static func rawChecksum(_ data: Data) -> String { + SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined() + } +} + +struct AutomationAuthorityContextBuilder: Sendable { + private let fileSystem: any AutomationFileSystem + private let currentUID: uid_t + private let launchAgentsRoot: URL + private let transactionRoot: URL + + init( + fileSystem: any AutomationFileSystem, + currentUID: uid_t, + launchAgentsRoot: URL, + transactionRoot: URL + ) { + self.fileSystem = fileSystem + self.currentUID = currentUID + self.launchAgentsRoot = launchAgentsRoot.standardizedFileURL + self.transactionRoot = transactionRoot.standardizedFileURL + } + + func context(for record: AutomationRecord) throws -> AutomationCapabilityContext { + if record.ownership != .user + || record.kind == .launchDaemon + || record.kind == .backgroundItem + || record.sourceKind == .launchDaemon + || record.sourceKind == .serviceManagement + { + return AutomationCapabilityContext( + currentUID: currentUID, + canonicalPathIsApproved: false, + sourceOwnerUID: record.ownerUID, + isSymlink: false, + isManaged: record.ownership == .managed, + implementedCapabilities: [.exportRecord] + ) + } + let sourceURL: URL + switch record.sourceKind { + case .launchAgent: + guard let directURL = record.sourceURL else { + throw AutomationManagerConfigurationError.destinationAuthorizationUnavailable + } + sourceURL = directURL.standardizedFileURL + case .crontab, .legacyLoginItem: + sourceURL = AutomationRecoverableSourceProvider.transactionURL( + for: record, + root: transactionRoot + ) + case .launchDaemon, .serviceManagement: + throw AutomationManagerConfigurationError.destinationAuthorizationUnavailable + } + return try context(for: record, destination: sourceURL) + } + + func context( + for record: AutomationRecord, + destination: URL + ) throws -> AutomationCapabilityContext { + let destination = destination.standardizedFileURL + let approvedRoot: URL + switch record.sourceKind { + case .launchAgent: + approvedRoot = launchAgentsRoot + case .crontab, .legacyLoginItem: + approvedRoot = transactionRoot + case .launchDaemon, .serviceManagement: + throw AutomationManagerConfigurationError.destinationAuthorizationUnavailable + } + let destinationExists = fileSystem.itemExists(at: destination) + let metadataURL = destinationExists + ? destination + : destination.deletingLastPathComponent().standardizedFileURL + let metadata = try fileSystem.metadata(for: metadataURL) + let canonicalPathIsApproved = AutomationPathAuthorization.isApprovedDestination( + destination, + approvedRoot: approvedRoot, + destinationExists: destinationExists, + verifiedMetadataURL: metadata.canonicalURL, + metadataIsSymbolicLink: metadata.isSymbolicLink + ) + return AutomationCapabilityContext( + currentUID: currentUID, + canonicalPathIsApproved: canonicalPathIsApproved, + sourceOwnerUID: metadata.ownerUID, + isSymlink: destinationExists ? metadata.isSymbolicLink : false, + isManaged: record.ownership == .managed, + implementedCapabilities: Self.implementedCapabilities(for: record.sourceKind) + ) + } + + private static func implementedCapabilities( + for sourceKind: AutomationSourceKind + ) -> Set { + switch sourceKind { + case .launchAgent: + [.startNow, .stopCurrentRun, .enable, .disable, .disableAndStop, + .edit, .duplicate, .importRecord, .exportRecord, .remove, .restore] + case .crontab: + [.startNow, .stopCurrentRun, .enable, .disable, .disableAndStop, + .edit, .duplicate, .importRecord, .exportRecord, .remove, .restore] + case .legacyLoginItem: + [.startNow, .enable, .disable, .exportRecord, .remove] + case .launchDaemon, .serviceManagement: + [.exportRecord] + } + } +} + +struct AutomationAuthorityCapabilityDecisionProvider: + AutomationCapabilityDecisionProviding, Sendable +{ + let authority: AutomationAuthorityContextBuilder + + func decisions( + for records: [AutomationRecord] + ) async -> [AutomationRecord.ID: AutomationCapabilityDecision] { + await Task.detached(priority: .utility) { + Dictionary(uniqueKeysWithValues: records.map { record in + do { + let context = try authority.context(for: record) + return (record.id, AutomationCapabilityPolicy.decision(for: record, context: context)) + } catch { + return ( + record.id, + AutomationCapabilityDecision( + capabilities: record.capabilities.intersection([.exportRecord]), + reason: "DevScope could not verify source ownership and path safety for management." + ) + ) + } + }) + }.value + } +} + +struct AutomationManagementDestinationProvider: + AutomationManagementDestinationProviding, Sendable +{ + let transactionRoot: URL + + func duplicateDestination(for record: AutomationRecord, label: String) -> URL? { + switch record.sourceKind { + case .launchAgent: + guard let sourceURL = record.sourceURL, + let filename = safeFilename(label, requiredExtension: "plist") else { return nil } + return sourceURL.deletingLastPathComponent().appendingPathComponent(filename) + case .crontab: + return AutomationRecoverableSourceProvider.transactionURL(for: record, root: transactionRoot) + case .legacyLoginItem, .launchDaemon, .serviceManagement: + return nil + } + } + + func importDestination(for record: AutomationRecord, suggestedFilename: String) -> URL? { + switch record.sourceKind { + case .launchAgent: + // Import replaces the selected, checksum-bound source. Creating a sibling + // requires separately inspected destination authority that the inventory + // does not publish. + return record.sourceURL?.standardizedFileURL + case .crontab: + return AutomationRecoverableSourceProvider.transactionURL(for: record, root: transactionRoot) + case .legacyLoginItem, .launchDaemon, .serviceManagement: + return nil + } + } + + private func safeFilename(_ value: String, requiredExtension: String) -> String? { + let component = URL(fileURLWithPath: value).lastPathComponent + guard component == value, + !component.isEmpty, + component != ".", + component != "..", + component.unicodeScalars.allSatisfy({ + CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "._-")).contains($0) + }) else { return nil } + return component.hasSuffix(".\(requiredExtension)") + ? component : "\(component).\(requiredExtension)" + } +} diff --git a/Sources/DevScope/Infrastructure/LaunchctlAutomationRuntimeStateProvider.swift b/Sources/DevScope/Infrastructure/LaunchctlAutomationRuntimeStateProvider.swift new file mode 100644 index 0000000..5c638c8 --- /dev/null +++ b/Sources/DevScope/Infrastructure/LaunchctlAutomationRuntimeStateProvider.swift @@ -0,0 +1,127 @@ +import DevScopeCore +import Foundation + +struct LaunchctlAutomationRuntimeStateProvider: LaunchdRuntimeStateProviding { + static let maximumConcurrentPrintQueries = 4 + + private let runner: any AutomationCommandRunning + + init(runner: any AutomationCommandRunning) { + self.runner = runner + } + + func states( + for labels: [String], + guiUID: uid_t + ) async -> [String: LaunchdRuntimeState] { + let domain = "gui/\(guiUID)" + let enabledStates: [String: AutomationEnabledState]? + do { + let result = try await runner.run( + AutomationCommand( + executable: "/bin/launchctl", + arguments: ["print-disabled", domain], + environment: ["LC_ALL": "C"] + )) + enabledStates = result.status == 0 ? Self.parseEnabledStates(result.standardOutput) : nil + } catch { + enabledStates = nil + } + + let uniqueLabels = Array(Set(labels)).sorted { + $0.utf8.lexicographicallyPrecedes($1.utf8) + } + let stateForLabel: @Sendable (String) async -> (String, LaunchdRuntimeState) = { label in + let loadState: AutomationLoadState + do { + let result = try await runner.run( + AutomationCommand( + executable: "/bin/launchctl", + arguments: ["print", "\(domain)/\(label)"], + environment: ["LC_ALL": "C"] + )) + switch LaunchctlServiceTargetClassifier.classify( + result, + label: label, + guiUID: guiUID + ) { + case .loaded: + loadState = .loaded + case .absent: + loadState = .unloaded + case .unknown: + loadState = .unknown + } + } catch { + loadState = .unknown + } + let enabledState = + enabledStates?[label] + ?? (enabledStates == nil ? .unknown : .enabled) + return ( + label, + LaunchdRuntimeState( + enabledState: enabledState, + loadState: loadState + ) + ) + } + + return await withTaskGroup( + of: (String, LaunchdRuntimeState).self, + returning: [String: LaunchdRuntimeState].self + ) { group in + let initialCount = min(Self.maximumConcurrentPrintQueries, uniqueLabels.count) + for label in uniqueLabels.prefix(initialCount) { + group.addTask { + await stateForLabel(label) + } + } + + var states: [String: LaunchdRuntimeState] = [:] + var nextIndex = initialCount + while let (label, state) = await group.next() { + states[label] = state + if nextIndex < uniqueLabels.count { + let nextLabel = uniqueLabels[nextIndex] + nextIndex += 1 + group.addTask { + await stateForLabel(nextLabel) + } + } + } + return states + } + } + + static func parseEnabledStates( + _ data: Data + ) -> [String: AutomationEnabledState]? { + guard let output = String(data: data, encoding: .utf8) else { return nil } + var states: [String: AutomationEnabledState] = [:] + for rawLine in output.split(whereSeparator: \.isNewline) { + let line = rawLine.trimmingCharacters(in: .whitespaces) + guard line.first == "\"", + let closingQuote = line.dropFirst().firstIndex(of: "\"") + else { continue } + let label = String(line[line.index(after: line.startIndex)..") else { continue } + let value = suffix[arrow.upperBound...] + .trimmingCharacters(in: .whitespacesAndNewlines) + .lowercased() + let state: AutomationEnabledState + switch value { + case "disabled", "true": + state = .disabled + case "enabled", "false": + state = .enabled + default: + continue + } + if let existing = states[label], existing != state { return nil } + states[label] = state + } + return states + } +} diff --git a/Sources/DevScope/Infrastructure/LocalAutomationFileSystem.swift b/Sources/DevScope/Infrastructure/LocalAutomationFileSystem.swift new file mode 100644 index 0000000..1272e85 --- /dev/null +++ b/Sources/DevScope/Infrastructure/LocalAutomationFileSystem.swift @@ -0,0 +1,847 @@ +import Darwin +import CryptoKit +import DevScopeCore +import Foundation + +enum LocalAutomationFileSystemFault: Hashable, Sendable { + case replaceDisplacedUnlink + case replacePostRenameVerification + case replaceRestoreCollision + case replaceSwapBackFailure + case replaceRestoredStagedUnlink + case trashFinalMove + case trashStagedIdentityMismatch + case trashRestoreCollision + case removeUnlink + case removeStagedIdentityMismatch + case removeRestoreCollision +} + +struct LocalAutomationFileSystemHooks: Sendable { + var beforeReplaceCommit: (@Sendable (URL) -> Void)? + var faults: Set + var trashDirectory: URL? + + init( + beforeReplaceCommit: (@Sendable (URL) -> Void)? = nil, + faults: Set = [], + trashDirectory: URL? = nil + ) { + self.beforeReplaceCommit = beforeReplaceCommit + self.faults = faults + self.trashDirectory = trashDirectory + } +} + +final class LocalAutomationFileSystem: AutomationFileSystem, @unchecked Sendable { + private let fileManager = FileManager.default + private let hooks: LocalAutomationFileSystemHooks + private let maxReadableBytes: Int + + init( + hooks: LocalAutomationFileSystemHooks = .init(), + maxReadableBytes: Int = 8 * 1_024 * 1_024 + ) { + self.hooks = hooks + self.maxReadableBytes = max(0, maxReadableBytes) + } + + func itemExists(at url: URL) -> Bool { + do { + let (parent, name) = try Self.openParent(of: url) + defer { _ = close(parent) } + var status = stat() + return fstatat(parent, name, &status, AT_SYMLINK_NOFOLLOW) == 0 || errno != ENOENT + } catch { + return true + } + } + + func plistURLs(in directory: URL) throws -> [URL] { + let descriptor = try Self.openDirectoryNoFollow(directory) + _ = close(descriptor) + return try fileManager.contentsOfDirectory( + at: directory, + includingPropertiesForKeys: nil, + options: [.skipsHiddenFiles] + ).filter { $0.pathExtension.caseInsensitiveCompare("plist") == .orderedSame } + .sorted { $0.path.utf8.lexicographicallyPrecedes($1.path.utf8) } + } + + func read(_ url: URL) throws -> Data { + let (parent, name) = try Self.openParent(of: url) + defer { _ = close(parent) } + let descriptor = openat(parent, name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC) + guard descriptor >= 0 else { throw Self.posixError() } + defer { _ = close(descriptor) } + var status = stat() + guard fstat(descriptor, &status) == 0, (status.st_mode & S_IFMT) == S_IFREG else { + throw POSIXError(.EPERM) + } + guard status.st_size >= 0, UInt64(status.st_size) <= UInt64(maxReadableBytes) else { + throw POSIXError(.EFBIG) + } + return try Self.readAll(descriptor, maxBytes: maxReadableBytes) + } + + func metadata(for url: URL) throws -> AutomationFileMetadata { + let path = url.standardizedFileURL.path + if path == "/" { + let descriptor = try Self.openDirectoryNoFollow(url) + defer { _ = close(descriptor) } + var status = stat() + guard fstat(descriptor, &status) == 0 else { throw Self.posixError() } + return Self.metadata(path: path, status: status, isSymbolicLink: false) + } + + let (parent, name) = try Self.openParent(of: url) + defer { _ = close(parent) } + var parentStatus = stat() + guard fstat(parent, &parentStatus) == 0 else { throw Self.posixError() } + var status = stat() + guard fstatat(parent, name, &status, AT_SYMLINK_NOFOLLOW) == 0 else { + throw Self.posixError() + } + let isSymbolicLink = (status.st_mode & S_IFMT) == S_IFLNK + return Self.metadata(path: path, status: status, isSymbolicLink: isSymbolicLink) + } + + func createDirectory(_ url: URL, permissions: Int) throws { + let path = url.standardizedFileURL.path + guard path.hasPrefix("/"), 0...0o777 ~= permissions else { throw POSIXError(.EINVAL) } + let components = path.split(separator: "/").map(String.init) + var parent = open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC) + guard parent >= 0 else { throw Self.posixError() } + defer { if parent >= 0 { _ = close(parent) } } + + var traversed = "" + for (index, component) in components.enumerated() { + traversed += "/\(component)" + let isFinal = index == components.count - 1 + var child = openat(parent, component, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC) + var created = false + if child < 0, errno == ENOENT { + guard mkdirat(parent, component, mode_t(isFinal ? permissions : 0o700)) == 0 else { + throw Self.posixError() + } + created = true + child = openat(parent, component, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC) + } + guard child >= 0 else { throw Self.posixError() } + var status = stat() + guard fstat(child, &status) == 0 else { + _ = close(child) + throw Self.posixError() + } + if isFinal { + let mode = Int(status.st_mode & 0o777) + if created { + guard fchmod(child, mode_t(permissions)) == 0 else { + _ = close(child) + throw Self.posixError() + } + guard fstat(child, &status) == 0 else { + _ = close(child) + throw Self.posixError() + } + } else if mode != permissions { + _ = close(child) + throw POSIXError(.EPERM) + } + } + _ = close(parent) + parent = child + } + } + + + func writeStagedFile( + nextTo url: URL, + data: Data, + permissions: Int, + authorization: AutomationDirectoryAuthorization + ) throws -> AutomationStagedFile { + guard 0...0o777 ~= permissions else { throw POSIXError(.EINVAL) } + let parentURL = url.deletingLastPathComponent().standardizedFileURL + return try withVerifiedParent(of: url, authorization: authorization) { parent, _ in + for _ in 0..<16 { + let name = ".devscope-\(UUID().uuidString.lowercased()).tmp" + let descriptor = openat( + parent, name, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC | O_NOFOLLOW, + mode_t(permissions) + ) + if descriptor < 0 { + if errno == EEXIST { continue } + throw Self.posixError() + } + do { + try Self.writeAll(data, to: descriptor) + var status = stat() + guard fsync(descriptor) == 0, fstat(descriptor, &status) == 0 else { + throw Self.posixError() + } + let temporaryURL = parentURL.appending(path: name, directoryHint: .notDirectory) + let resourceIdentifier = Self.resourceIdentifier(status) + guard (status.st_mode & S_IFMT) == S_IFREG, + status.st_nlink == 1, + let fileAuthorization = AutomationFileAuthorization( + fileURL: temporaryURL, + directory: authorization, + expectation: .existing(resourceIdentifier: resourceIdentifier) + ), close(descriptor) == 0 else { throw Self.posixError() } + return AutomationStagedFile( + url: temporaryURL, + authorization: fileAuthorization, + binding: AutomationStagedFileBinding( + data: data, + resourceIdentifier: resourceIdentifier, + ownerUID: status.st_uid, + permissions: Int(status.st_mode & 0o777), + linkCount: UInt64(status.st_nlink) + ) + ) + } catch { + _ = close(descriptor) + _ = unlinkat(parent, name, 0) + throw error + } + } + throw POSIXError(.EEXIST) + } + } + + + func replaceItem( + at destination: AutomationFileAuthorization, + with staged: AutomationStagedFile + ) throws -> AutomationFileMutationOutcome { + guard destination.directory == staged.authorization.directory, + destination.fileURL.deletingLastPathComponent().standardizedFileURL + == destination.directory.directoryURL, + staged.url == staged.authorization.fileURL, + case .existing(let stagedIdentity) = staged.authorization.expectation + else { throw POSIXError(.EPERM) } + + return try withVerifiedParent( + of: destination.fileURL, + authorization: destination.directory + ) { parent, destinationName in + let stagedName = staged.url.lastPathComponent + guard staged.binding.resourceIdentifier == stagedIdentity, + try Self.entryMatchesBinding( + parent: parent, + name: stagedName, + binding: staged.binding + ) + else { + throw POSIXError(.EPERM) + } + hooks.beforeReplaceCommit?(staged.url) + switch destination.expectation { + case .absent: + guard renameatx_np(parent, stagedName, parent, destinationName, UInt32(RENAME_EXCL)) == 0 + else { throw Self.posixError() } + let installedMatchesBinding = hooks.faults.contains(.replacePostRenameVerification) + ? false + : (try? Self.entryMatchesBinding( + parent: parent, + name: destinationName, + binding: staged.binding + )) == true + guard installedMatchesBinding else { + if hooks.faults.contains(.replaceRestoreCollision) { + let collision = openat( + parent, + stagedName, + O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC | O_NOFOLLOW, + mode_t(0o600) + ) + if collision >= 0 { _ = close(collision) } + } + let restored = renameatx_np( + parent, destinationName, parent, stagedName, UInt32(RENAME_EXCL) + ) == 0 + if restored, + (try? Self.entryIdentity(parent: parent, name: destinationName)) == nil { + try cleanupRestoredStaged( + parent: parent, + name: stagedName, + staged: staged + ) + return .unchanged + } + var observed: [AutomationFileAuthorization] = [] + let installedIdentity = try? Self.entryIdentity(parent: parent, name: destinationName) + let collisionIdentity = try? Self.entryIdentity(parent: parent, name: stagedName) + let installed = installedIdentity.flatMap { + AutomationFileAuthorization( + fileURL: destination.fileURL, + directory: destination.directory, + expectation: .existing(resourceIdentifier: $0) + ) + } + let collision = collisionIdentity.flatMap { + AutomationFileAuthorization( + fileURL: staged.url, + directory: destination.directory, + expectation: .existing(resourceIdentifier: $0) + ) + } + if let installed { observed.append(installed) } + if let collision { observed.append(collision) } + let installedIsRecovery = (try? Self.entryMatchesBinding( + parent: parent, + name: destinationName, + binding: staged.binding + )) == true + throw AutomationFilePartialMutation( + kind: .replace, + commitState: installedIsRecovery ? .committed : .unknown, + observedFiles: observed, + recoveryHandle: installedIsRecovery ? installed : nil, + resultURL: destination.fileURL + ) + } + case .existing(let destinationIdentity): + guard try Self.entryIdentity(parent: parent, name: destinationName) == destinationIdentity, + renameatx_np(parent, stagedName, parent, destinationName, UInt32(RENAME_SWAP)) == 0 + else { throw POSIXError(.EPERM) } + let installedMatches = (try? Self.entryMatchesBinding( + parent: parent, + name: destinationName, + binding: staged.binding + )) == true + let displacedMatches = (try? Self.entryIdentity( + parent: parent, name: stagedName + )) == destinationIdentity + guard installedMatches, displacedMatches else { + let swappedBack = !hooks.faults.contains(.replaceSwapBackFailure) + && renameatx_np( + parent, stagedName, parent, destinationName, UInt32(RENAME_SWAP) + ) == 0 + if swappedBack, + (try? Self.entryIdentity(parent: parent, name: destinationName)) + == destinationIdentity, + (try? Self.entryIdentity(parent: parent, name: stagedName)) == stagedIdentity { + try cleanupRestoredStaged( + parent: parent, + name: stagedName, + staged: staged + ) + return .unchanged + } + let destinationObserved = (try? Self.entryIdentity( + parent: parent, name: destinationName + )).flatMap { + AutomationFileAuthorization( + fileURL: destination.fileURL, + directory: destination.directory, + expectation: .existing(resourceIdentifier: $0) + ) + } + let stagedObserved = (try? Self.entryIdentity( + parent: parent, name: stagedName + )).flatMap { + AutomationFileAuthorization( + fileURL: staged.url, + directory: staged.authorization.directory, + expectation: .existing(resourceIdentifier: $0) + ) + } + let observed = [destinationObserved, stagedObserved].compactMap { $0 } + let recovery = destinationObserved?.expectation + == .existing(resourceIdentifier: destinationIdentity) + ? destinationObserved + : (stagedObserved?.expectation == .existing(resourceIdentifier: destinationIdentity) + ? stagedObserved : nil) + throw AutomationFilePartialMutation( + kind: .replace, + commitState: .unknown, + observedFiles: observed, + recoveryHandle: recovery, + resultURL: destination.fileURL + ) + } + if hooks.faults.contains(.replaceDisplacedUnlink) + || unlinkat(parent, stagedName, 0) != 0 + { + let installed = AutomationFileAuthorization( + fileURL: destination.fileURL, + directory: destination.directory, + expectation: .existing(resourceIdentifier: staged.binding.resourceIdentifier) + ) + let recovery = AutomationFileAuthorization( + fileURL: staged.url, + directory: destination.directory, + expectation: .existing(resourceIdentifier: destinationIdentity) + ) + throw AutomationFilePartialMutation( + kind: .replace, + commitState: .committed, + observedFiles: [installed, recovery].compactMap { $0 }, + recoveryHandle: recovery, + resultURL: destination.fileURL + ) + } + } + _ = fsync(parent) + let installedAuthorization = AutomationFileAuthorization( + fileURL: destination.fileURL, + directory: destination.directory, + expectation: .existing(resourceIdentifier: staged.binding.resourceIdentifier) + ) + return .committed(AutomationFileMutationReceipt( + primaryFile: installedAuthorization, + resultURL: destination.fileURL, + bindingVerified: true + )) + } + } + + private func cleanupRestoredStaged( + parent: Int32, + name: String, + staged: AutomationStagedFile + ) throws { + let exactStagedAuthorization = (try? Self.entryIdentity(parent: parent, name: name)) + == staged.binding.resourceIdentifier ? staged.authorization : nil + guard let exactStagedAuthorization else { + let observed = (try? Self.entryIdentity(parent: parent, name: name)).flatMap { + AutomationFileAuthorization( + fileURL: staged.url, + directory: staged.authorization.directory, + expectation: .existing(resourceIdentifier: $0) + ) + } + throw AutomationFilePartialMutation( + kind: .remove, + commitState: .unknown, + observedFiles: observed.map { [$0] } ?? [], + recoveryHandle: nil, + resultURL: staged.url + ) + } + guard !hooks.faults.contains(.replaceRestoredStagedUnlink), + unlinkat(parent, name, 0) == 0, + (try? Self.entryIdentity(parent: parent, name: name)) == nil + else { + throw AutomationFilePartialMutation( + kind: .remove, + commitState: .unknown, + observedFiles: [exactStagedAuthorization], + recoveryHandle: exactStagedAuthorization, + resultURL: staged.url + ) + } + _ = fsync(parent) + } + + func fileMatchesBinding( + _ file: AutomationFileAuthorization, + binding: AutomationStagedFileBinding + ) throws -> Bool { + guard case .existing(let expectedIdentity) = file.expectation, + expectedIdentity == binding.resourceIdentifier + else { return false } + return try withVerifiedParent(of: file.fileURL, authorization: file.directory) { + parent, name in + try Self.entryMatchesBinding(parent: parent, name: name, binding: binding) + } + } + + + func moveToTrash( + _ source: AutomationFileAuthorization + ) throws -> AutomationFileMutationOutcome { + guard case .existing(let expectedIdentity) = source.expectation else { + throw POSIXError(.EPERM) + } + let trash = hooks.trashDirectory ?? FileManager.default.homeDirectoryForCurrentUser + .appending(path: ".Trash", directoryHint: .isDirectory) + try createDirectory(trash, permissions: 0o700) + let trashDescriptor = try Self.openDirectoryNoFollow(trash) + defer { _ = close(trashDescriptor) } + return try withVerifiedParent(of: source.fileURL, authorization: source.directory) { + parent, name in + let stagedName = ".devscope-trash-\(UUID().uuidString.lowercased())" + guard renameatx_np(parent, name, parent, stagedName, UInt32(RENAME_EXCL)) == 0 else { + throw Self.posixError() + } + let stagedIdentityMatches = !hooks.faults.contains(.trashStagedIdentityMismatch) + && (try? Self.entryIdentity(parent: parent, name: stagedName)) == expectedIdentity + guard stagedIdentityMatches else { + if hooks.faults.contains(.trashRestoreCollision) { + let collision = openat( + parent, name, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC | O_NOFOLLOW, + mode_t(0o600) + ) + if collision >= 0 { _ = close(collision) } + } + let restored = renameatx_np( + parent, stagedName, parent, name, UInt32(RENAME_EXCL) + ) == 0 + if restored, + (try? Self.entryIdentity(parent: parent, name: name)) == expectedIdentity, + (try? Self.entryIdentity(parent: parent, name: stagedName)) == nil { + return .unchanged + } + let evidence = Self.partialMutationEvidence( + parent: parent, + source: source, + sourceName: name, + stagedName: stagedName, + expectedIdentity: expectedIdentity + ) + throw AutomationFilePartialMutation( + kind: .trash, + commitState: .unknown, + observedFiles: evidence.observed, + recoveryHandle: evidence.recovery, + resultURL: nil + ) + } + let destinationName = "\(name)-\(UUID().uuidString.lowercased())" + let movedToTrash = !hooks.faults.contains(.trashFinalMove) + && renameat(parent, stagedName, trashDescriptor, destinationName) == 0 + guard movedToTrash else { + let restored = renameatx_np( + parent, stagedName, parent, name, UInt32(RENAME_EXCL) + ) == 0 + if restored, + (try? Self.entryIdentity(parent: parent, name: name)) == expectedIdentity, + (try? Self.entryIdentity(parent: parent, name: stagedName)) == nil { + return .unchanged + } + var observed: [AutomationFileAuthorization] = [] + let sourceIdentity = try? Self.entryIdentity(parent: parent, name: name) + let stagedIdentity = try? Self.entryIdentity(parent: parent, name: stagedName) + let sourceObserved = sourceIdentity.flatMap { + AutomationFileAuthorization( + fileURL: source.fileURL, + directory: source.directory, + expectation: .existing(resourceIdentifier: $0) + ) + } + let stagedURL = source.directory.directoryURL.appending( + path: stagedName, directoryHint: .notDirectory + ) + let stagedObserved = stagedIdentity.flatMap { + AutomationFileAuthorization( + fileURL: stagedURL, + directory: source.directory, + expectation: .existing(resourceIdentifier: $0) + ) + } + if let sourceObserved { observed.append(sourceObserved) } + if let stagedObserved { observed.append(stagedObserved) } + let recovery = sourceIdentity == expectedIdentity ? sourceObserved + : (stagedIdentity == expectedIdentity ? stagedObserved : nil) + throw AutomationFilePartialMutation( + kind: .trash, + commitState: .unknown, + observedFiles: observed, + recoveryHandle: recovery, + resultURL: nil + ) + } + _ = fsync(parent) + _ = fsync(trashDescriptor) + let resultURL = trash.appending(path: destinationName, directoryHint: .notDirectory) + var trashStatus = stat() + let trashIdentity = try? Self.entryIdentity( + parent: trashDescriptor, + name: destinationName + ) + let trashAuthorization = fstat(trashDescriptor, &trashStatus) == 0 + ? AutomationDirectoryAuthorization( + directoryURL: trash, + resourceIdentifier: Self.resourceIdentifier(trashStatus) + ) : nil + let observedFile = trashAuthorization.flatMap { directory in + trashIdentity.flatMap { identity in + AutomationFileAuthorization( + fileURL: resultURL, + directory: directory, + expectation: .existing(resourceIdentifier: identity) + ) + } + } + guard trashIdentity == expectedIdentity, + let trashedFile = observedFile + else { + throw AutomationFilePartialMutation( + kind: .trash, + commitState: trashIdentity == expectedIdentity ? .committed : .unknown, + observedFiles: observedFile.map { [$0] } ?? [], + recoveryHandle: trashIdentity == expectedIdentity ? observedFile : nil, + resultURL: resultURL + ) + } + return .committed(AutomationFileMutationReceipt( + primaryFile: trashedFile, + resultURL: resultURL, + bindingVerified: true + )) + } + } + + + func removeItem( + _ source: AutomationFileAuthorization + ) throws -> AutomationFileMutationOutcome { + guard case .existing(let expectedIdentity) = source.expectation else { + throw POSIXError(.EPERM) + } + return try withVerifiedParent(of: source.fileURL, authorization: source.directory) { + parent, name in + let stagedName = ".devscope-remove-\(UUID().uuidString.lowercased())" + guard renameatx_np(parent, name, parent, stagedName, UInt32(RENAME_EXCL)) == 0 else { + throw Self.posixError() + } + let stagedIdentityMatches = !hooks.faults.contains(.removeStagedIdentityMismatch) + && (try? Self.entryIdentity(parent: parent, name: stagedName)) == expectedIdentity + guard stagedIdentityMatches else { + if hooks.faults.contains(.removeRestoreCollision) { + let collision = openat( + parent, name, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC | O_NOFOLLOW, + mode_t(0o600) + ) + if collision >= 0 { _ = close(collision) } + } + let restored = renameatx_np( + parent, stagedName, parent, name, UInt32(RENAME_EXCL) + ) == 0 + if restored, + (try? Self.entryIdentity(parent: parent, name: name)) == expectedIdentity, + (try? Self.entryIdentity(parent: parent, name: stagedName)) == nil { + return .unchanged + } + let evidence = Self.partialMutationEvidence( + parent: parent, + source: source, + sourceName: name, + stagedName: stagedName, + expectedIdentity: expectedIdentity + ) + throw AutomationFilePartialMutation( + kind: .remove, + commitState: .unknown, + observedFiles: evidence.observed, + recoveryHandle: evidence.recovery, + resultURL: nil + ) + } + if hooks.faults.contains(.removeUnlink) || unlinkat(parent, stagedName, 0) != 0 { + let restored = renameatx_np( + parent, stagedName, parent, name, UInt32(RENAME_EXCL) + ) == 0 + if restored, + (try? Self.entryIdentity(parent: parent, name: name)) == expectedIdentity, + (try? Self.entryIdentity(parent: parent, name: stagedName)) == nil { + return .unchanged + } + var observed: [AutomationFileAuthorization] = [] + let sourceIdentity = try? Self.entryIdentity(parent: parent, name: name) + let stagedIdentity = try? Self.entryIdentity(parent: parent, name: stagedName) + let sourceObserved = sourceIdentity.flatMap { + AutomationFileAuthorization( + fileURL: source.fileURL, + directory: source.directory, + expectation: .existing(resourceIdentifier: $0) + ) + } + let stagedURL = source.directory.directoryURL.appending( + path: stagedName, directoryHint: .notDirectory + ) + let stagedObserved = stagedIdentity.flatMap { + AutomationFileAuthorization( + fileURL: stagedURL, + directory: source.directory, + expectation: .existing(resourceIdentifier: $0) + ) + } + if let sourceObserved { observed.append(sourceObserved) } + if let stagedObserved { observed.append(stagedObserved) } + let recovery = sourceIdentity == expectedIdentity ? sourceObserved + : (stagedIdentity == expectedIdentity ? stagedObserved : nil) + throw AutomationFilePartialMutation( + kind: .remove, + commitState: .unknown, + observedFiles: observed, + recoveryHandle: recovery, + resultURL: nil + ) + } + _ = fsync(parent) + return .committed(AutomationFileMutationReceipt( + primaryFile: nil, + resultURL: nil, + bindingVerified: true + )) + } + } + + private func withVerifiedParent( + of url: URL, + authorization: AutomationDirectoryAuthorization, + _ body: (Int32, String) throws -> T + ) throws -> T { + let parentURL = url.deletingLastPathComponent().standardizedFileURL + guard parentURL == authorization.directoryURL, + !authorization.resourceIdentifier.isEmpty else { throw POSIXError(.EPERM) } + let parent = try Self.openDirectoryNoFollow(parentURL) + defer { _ = close(parent) } + var status = stat() + guard fstat(parent, &status) == 0, + Self.resourceIdentifier(status) == authorization.resourceIdentifier else { + throw POSIXError(.EPERM) + } + let name = url.lastPathComponent + guard !name.isEmpty, !name.contains("/") else { throw POSIXError(.EINVAL) } + return try body(parent, name) + } + + private static func partialMutationEvidence( + parent: Int32, + source: AutomationFileAuthorization, + sourceName: String, + stagedName: String, + expectedIdentity: String + ) -> (observed: [AutomationFileAuthorization], recovery: AutomationFileAuthorization?) { + let sourceIdentity = try? entryIdentity(parent: parent, name: sourceName) + let stagedIdentity = try? entryIdentity(parent: parent, name: stagedName) + let sourceObserved = sourceIdentity.flatMap { + AutomationFileAuthorization( + fileURL: source.fileURL, + directory: source.directory, + expectation: .existing(resourceIdentifier: $0) + ) + } + let stagedURL = source.directory.directoryURL.appending( + path: stagedName, directoryHint: .notDirectory + ) + let stagedObserved = stagedIdentity.flatMap { + AutomationFileAuthorization( + fileURL: stagedURL, + directory: source.directory, + expectation: .existing(resourceIdentifier: $0) + ) + } + let observed = [sourceObserved, stagedObserved].compactMap { $0 } + let recovery = sourceIdentity == expectedIdentity ? sourceObserved + : (stagedIdentity == expectedIdentity ? stagedObserved : nil) + return (observed, recovery) + } + + private static func openParent(of url: URL) throws -> (Int32, String) { + let standardized = url.standardizedFileURL + let name = standardized.lastPathComponent + guard !name.isEmpty, !name.contains("/") else { throw POSIXError(.EINVAL) } + return (try openDirectoryNoFollow(standardized.deletingLastPathComponent()), name) + } + + private static func openDirectoryNoFollow(_ url: URL) throws -> Int32 { + let path = url.standardizedFileURL.path + guard path.hasPrefix("/") else { throw POSIXError(.EINVAL) } + var descriptor = open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC) + guard descriptor >= 0 else { throw posixError() } + if path == "/" { return descriptor } + for component in path.split(separator: "/").map(String.init) { + let next = openat( + descriptor, component, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC + ) + guard next >= 0 else { + let error = posixError() + _ = close(descriptor) + throw error + } + _ = close(descriptor) + descriptor = next + } + return descriptor + } + + private static func metadata( + path: String, + status: stat, + isSymbolicLink: Bool + ) -> AutomationFileMetadata { + let canonicalURL = isSymbolicLink + ? URL(fileURLWithPath: path).resolvingSymlinksInPath().standardizedFileURL + : URL(fileURLWithPath: path).standardizedFileURL + return AutomationFileMetadata( + canonicalURL: canonicalURL, + ownerUID: status.st_uid, + isSymbolicLink: isSymbolicLink, + modificationDate: Date( + timeIntervalSince1970: TimeInterval(status.st_mtimespec.tv_sec) + + TimeInterval(status.st_mtimespec.tv_nsec) / 1_000_000_000 + ), + resourceIdentifier: resourceIdentifier(status), + permissions: Int(status.st_mode & 0o777) + ) + } + + private static func resourceIdentifier(_ status: stat) -> String { + "\(status.st_dev):\(status.st_ino)" + } + + private static func entryIdentity(parent: Int32, name: String) throws -> String { + var status = stat() + guard fstatat(parent, name, &status, AT_SYMLINK_NOFOLLOW) == 0, + (status.st_mode & S_IFMT) == S_IFREG else { throw posixError() } + return resourceIdentifier(status) + } + + private static func entryMatchesBinding( + parent: Int32, + name: String, + binding: AutomationStagedFileBinding + ) throws -> Bool { + let descriptor = openat(parent, name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC) + guard descriptor >= 0 else { throw posixError() } + defer { _ = close(descriptor) } + var status = stat() + guard fstat(descriptor, &status) == 0 else { throw posixError() } + guard (status.st_mode & S_IFMT) == S_IFREG, + resourceIdentifier(status) == binding.resourceIdentifier, + status.st_uid == binding.ownerUID, + Int(status.st_mode & 0o777) == binding.permissions, + UInt64(status.st_nlink) == binding.linkCount + else { return false } + let data = try readAll(descriptor, maxBytes: binding.data.count) + let checksum = SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined() + return data == binding.data && checksum == binding.checksum + } + + private static func readAll(_ descriptor: Int32, maxBytes: Int) throws -> Data { + var data = Data() + var buffer = [UInt8](repeating: 0, count: 64 * 1_024) + while true { + let count = Darwin.read(descriptor, &buffer, buffer.count) + if count < 0, errno == EINTR { continue } + guard count >= 0 else { throw posixError() } + if count == 0 { return data } + guard count <= maxBytes - data.count else { throw POSIXError(.EFBIG) } + data.append(buffer, count: count) + } + } + + private static func writeAll(_ data: Data, to descriptor: Int32) throws { + try data.withUnsafeBytes { rawBuffer in + guard var base = rawBuffer.baseAddress else { return } + var remaining = rawBuffer.count + while remaining > 0 { + let count = Darwin.write(descriptor, base, remaining) + if count < 0, errno == EINTR { continue } + guard count > 0 else { throw posixError() } + remaining -= count + base = base.advanced(by: count) + } + } + } + + private static func posixError() -> POSIXError { + POSIXError(POSIXErrorCode(rawValue: errno) ?? .EIO) + } +} diff --git a/Sources/DevScope/Infrastructure/SystemAutomationCommandRunner.swift b/Sources/DevScope/Infrastructure/SystemAutomationCommandRunner.swift new file mode 100644 index 0000000..58902f0 --- /dev/null +++ b/Sources/DevScope/Infrastructure/SystemAutomationCommandRunner.swift @@ -0,0 +1,329 @@ +import Darwin +import DevScopeCore +import Foundation + +enum SystemAutomationCommandError: Error { + case invalidExecutable + case launchFailed + case outputLimitExceeded + case executionTimedOut +} + +struct SystemAutomationCommandRunnerHooks: Sendable { + var afterSpawnBeforeInstall: (@Sendable (pid_t) -> Void)? + var afterInstall: (@Sendable (pid_t) -> Void)? + var afterLeaderExitObserved: (@Sendable (pid_t) -> Void)? + + init( + afterSpawnBeforeInstall: (@Sendable (pid_t) -> Void)? = nil, + afterInstall: (@Sendable (pid_t) -> Void)? = nil, + afterLeaderExitObserved: (@Sendable (pid_t) -> Void)? = nil + ) { + self.afterSpawnBeforeInstall = afterSpawnBeforeInstall + self.afterInstall = afterInstall + self.afterLeaderExitObserved = afterLeaderExitObserved + } +} + +private final class AutomationChildProcessController: @unchecked Sendable { + private let lock = NSLock() + private var processGroupID: pid_t? + private var cancellationRequested = false + private var escalationTask: Task? + + func install(processGroupID: pid_t) { + let shouldCancel = lock.withLock { + self.processGroupID = processGroupID + return cancellationRequested + } + if shouldCancel { scheduleEscalationIfNeeded(for: processGroupID) } + } + + func clear() { + lock.withLock { processGroupID = nil } + } + + func cancel() { + let group = lock.withLock { () -> pid_t? in + cancellationRequested = true + return processGroupID + } + if let group { scheduleEscalationIfNeeded(for: group) } + } + + func awaitCancellationEscalationIfNeeded() async { + let task = lock.withLock { cancellationRequested ? escalationTask : nil } + await task?.value + } + + private func scheduleEscalationIfNeeded(for group: pid_t) { + lock.withLock { + guard escalationTask == nil else { return } + terminate(group, signal: SIGTERM) + escalationTask = Task.detached { + try? await Task.sleep(for: .seconds(2)) + guard !Task.isCancelled else { return } + _ = Darwin.kill(-group, SIGKILL) + } + } + } + + private func terminate(_ group: pid_t, signal: Int32) { + guard group > 0 else { return } + _ = Darwin.kill(-group, signal) + } +} + +actor SystemAutomationCommandRunner: AutomationCommandRunning { + private static let maximumCapturedBytes = 8 * 1_024 * 1_024 + private let hooks: SystemAutomationCommandRunnerHooks + private let maximumCapturedBytes: Int + private let executionTimeout: Duration + + init( + hooks: SystemAutomationCommandRunnerHooks = .init(), + maximumCapturedBytes: Int = SystemAutomationCommandRunner.maximumCapturedBytes, + executionTimeout: Duration = .seconds(30) + ) { + self.hooks = hooks + self.maximumCapturedBytes = max(0, maximumCapturedBytes) + self.executionTimeout = executionTimeout + } + + func run(_ command: AutomationCommand) async throws -> AutomationCommandResult { + guard command.executable.hasPrefix("/"), + !command.executable.contains("\0"), + command.arguments.allSatisfy({ !$0.contains("\0") }), + command.environment.allSatisfy({ key, value in + !key.isEmpty && !key.contains("=") && !key.contains("\0") && !value.contains("\0") + }) + else { throw SystemAutomationCommandError.invalidExecutable } + + try Task.checkCancellation() + let controller = AutomationChildProcessController() + return try await withTaskCancellationHandler { + let spawned = try Self.spawn(command) + hooks.afterSpawnBeforeInstall?(spawned.pid) + controller.install(processGroupID: spawned.pid) + hooks.afterInstall?(spawned.pid) + + let outputTask = Task.detached { + try Self.drain(spawned.standardOutput, maximumCapturedBytes: self.maximumCapturedBytes) + } + let errorTask = Task.detached { + try Self.drain(spawned.standardError, maximumCapturedBytes: self.maximumCapturedBytes) + } + let leaderTask = Task.detached { + try Self.observeLeaderExitWithoutReaping(spawned.pid) + } + let timeoutTask = Task.detached { [executionTimeout] in + do { + try await Task.sleep(for: executionTimeout) + } catch { + return false + } + controller.cancel() + return true + } + + do { + try await leaderTask.value + hooks.afterLeaderExitObserved?(spawned.pid) + let output = try await outputTask.value + let error = try await errorTask.value + timeoutTask.cancel() + if await timeoutTask.value { + throw SystemAutomationCommandError.executionTimedOut + } + let status = try Self.reap(spawned.pid) + controller.clear() + try Task.checkCancellation() + return AutomationCommandResult( + status: Self.commandStatus(fromWaitStatus: status), + standardOutput: output, + standardError: error + ) + } catch { + timeoutTask.cancel() + controller.cancel() + _ = try? await outputTask.value + _ = try? await errorTask.value + await controller.awaitCancellationEscalationIfNeeded() + _ = try? Self.reap(spawned.pid) + controller.clear() + throw error + } + } onCancel: { + controller.cancel() + } + } + + private struct SpawnedCommand: Sendable { + let pid: pid_t + let standardOutput: FileHandle + let standardError: FileHandle + } + + nonisolated private static func spawn(_ command: AutomationCommand) throws -> SpawnedCommand { + var output = [Int32](repeating: -1, count: 2) + var error = [Int32](repeating: -1, count: 2) + guard output.withUnsafeMutableBufferPointer({ Darwin.pipe($0.baseAddress!) }) == 0 else { + throw SystemAutomationCommandError.launchFailed + } + guard error.withUnsafeMutableBufferPointer({ Darwin.pipe($0.baseAddress!) }) == 0 else { + close(output[0]); close(output[1]) + throw SystemAutomationCommandError.launchFailed + } + for descriptor in output + error { + let existingFlags = fcntl(descriptor, F_GETFD) + guard existingFlags >= 0, + fcntl(descriptor, F_SETFD, existingFlags | FD_CLOEXEC) == 0 + else { + for descriptor in output { close(descriptor) } + for descriptor in error { close(descriptor) } + throw SystemAutomationCommandError.launchFailed + } + } + + var actions: posix_spawn_file_actions_t? = nil + var attributes: posix_spawnattr_t? = nil + guard posix_spawn_file_actions_init(&actions) == 0 else { + for descriptor in output { close(descriptor) } + for descriptor in error { close(descriptor) } + throw SystemAutomationCommandError.launchFailed + } + defer { posix_spawn_file_actions_destroy(&actions) } + guard posix_spawnattr_init(&attributes) == 0 else { + for descriptor in output { close(descriptor) } + for descriptor in error { close(descriptor) } + throw SystemAutomationCommandError.launchFailed + } + defer { + posix_spawnattr_destroy(&attributes) + } + + var result = posix_spawn_file_actions_addopen( + &actions, STDIN_FILENO, "/dev/null", O_RDONLY, 0 + ) + if result == 0 { result = posix_spawn_file_actions_adddup2(&actions, output[1], STDOUT_FILENO) } + if result == 0 { result = posix_spawn_file_actions_adddup2(&actions, error[1], STDERR_FILENO) } + if result == 0 { result = posix_spawn_file_actions_addclose(&actions, output[0]) } + if result == 0 { result = posix_spawn_file_actions_addclose(&actions, error[0]) } + if result == 0 { result = posix_spawn_file_actions_addclose(&actions, output[1]) } + if result == 0 { result = posix_spawn_file_actions_addclose(&actions, error[1]) } + + var emptyMask = sigset_t() + var defaultSignals = sigset_t() + sigemptyset(&emptyMask) + sigemptyset(&defaultSignals) + sigaddset(&defaultSignals, SIGINT) + sigaddset(&defaultSignals, SIGHUP) + sigaddset(&defaultSignals, SIGQUIT) + sigaddset(&defaultSignals, SIGTERM) + sigaddset(&defaultSignals, SIGPIPE) + if result == 0 { result = posix_spawnattr_setsigmask(&attributes, &emptyMask) } + if result == 0 { result = posix_spawnattr_setsigdefault(&attributes, &defaultSignals) } + if result == 0 { result = posix_spawnattr_setpgroup(&attributes, 0) } + let flags = Int16( + POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF + | POSIX_SPAWN_CLOEXEC_DEFAULT + ) + if result == 0 { result = posix_spawnattr_setflags(&attributes, flags) } + guard result == 0 else { + for descriptor in output { close(descriptor) } + for descriptor in error { close(descriptor) } + throw SystemAutomationCommandError.launchFailed + } + + var environment = [ + "HOME": FileManager.default.homeDirectoryForCurrentUser.path, + "LOGNAME": NSUserName(), + "PATH": "/usr/bin:/bin:/usr/sbin:/sbin", + "USER": NSUserName(), + ] + environment.merge(command.environment) { _, explicit in explicit } + let argv = [command.executable] + command.arguments + let envp = environment.keys.sorted().map { "\($0)=\(environment[$0]!)" } + var pid: pid_t = 0 + let spawnResult = withMutableCStringArray(argv) { argvPointer in + withMutableCStringArray(envp) { environmentPointer in + posix_spawn( + &pid, + command.executable, + &actions, + &attributes, + argvPointer, + environmentPointer + ) + } + } + close(output[1]) + close(error[1]) + guard spawnResult == 0, pid > 1 else { + close(output[0]) + close(error[0]) + throw SystemAutomationCommandError.launchFailed + } + return SpawnedCommand( + pid: pid, + standardOutput: FileHandle(fileDescriptor: output[0], closeOnDealloc: true), + standardError: FileHandle(fileDescriptor: error[0], closeOnDealloc: true) + ) + } + + nonisolated private static func withMutableCStringArray( + _ strings: [String], + _ body: (UnsafeMutablePointer?>) -> Result + ) -> Result { + let pointers = strings.map { strdup($0)! } + defer { + for pointer in pointers { free(pointer) } + } + var terminated = pointers.map(Optional.some) + terminated.append(nil) + return terminated.withUnsafeMutableBufferPointer { buffer in + body(buffer.baseAddress!) + } + } + + nonisolated private static func observeLeaderExitWithoutReaping(_ pid: pid_t) throws { + var information = siginfo_t() + while true { + if waitid(P_PID, id_t(pid), &information, WEXITED | WNOWAIT) == 0 { return } + if errno != EINTR { throw SystemAutomationCommandError.launchFailed } + } + } + + nonisolated private static func reap(_ pid: pid_t) throws -> Int32 { + var status: Int32 = 0 + while true { + let result = waitpid(pid, &status, 0) + if result == pid { return status } + if result == -1, errno == EINTR { continue } + throw SystemAutomationCommandError.launchFailed + } + } + + nonisolated private static func commandStatus(fromWaitStatus status: Int32) -> Int32 { + let signal = status & 0x7f + return signal == 0 ? (status >> 8) & 0xff : 128 + signal + } + + nonisolated private static func drain( + _ handle: FileHandle, + maximumCapturedBytes: Int + ) throws -> Data { + defer { try? handle.close() } + var captured = Data() + var exceededLimit = false + while let chunk = try handle.read(upToCount: 64 * 1_024), !chunk.isEmpty { + let available = maximumCapturedBytes - captured.count + if available > 0 { + captured.append(chunk.prefix(available)) + } + if chunk.count > available { exceededLimit = true } + } + if exceededLimit { throw SystemAutomationCommandError.outputLimitExceeded } + return captured + } +} diff --git a/Sources/DevScope/Stores/AutomationNotifier.swift b/Sources/DevScope/Stores/AutomationNotifier.swift new file mode 100644 index 0000000..8685da3 --- /dev/null +++ b/Sources/DevScope/Stores/AutomationNotifier.swift @@ -0,0 +1,156 @@ +import DevScopeCore +import Foundation +import UserNotifications + +protocol AutomationNotificationCenterClient: Sendable { + func requestAuthorization() async throws -> Bool + func deliver(_ content: AutomationNotificationContent) async throws +} + +enum AutomationNotificationDeliveryState: Equatable, Sendable { + case notRequested + case requesting + case enabled + case denied + case authorizationFailed + case deliveryFailed +} + +struct SystemAutomationNotificationCenter: AutomationNotificationCenterClient { + func requestAuthorization() async throws -> Bool { + try await UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) + } + + func deliver(_ content: AutomationNotificationContent) async throws { + let notificationContent = UNMutableNotificationContent() + notificationContent.title = content.title + notificationContent.body = content.body + notificationContent.sound = .default + try await UNUserNotificationCenter.current().add(UNNotificationRequest( + identifier: UUID().uuidString, + content: notificationContent, + trigger: nil + )) + } +} + +@MainActor +final class AutomationNotifier: ObservableObject { + typealias Sleep = @Sendable (Duration) async throws -> Void + + @Published private(set) var preferences: AutomationNotificationPreferences + @Published private(set) var deliveryState: AutomationNotificationDeliveryState = .notRequested + + private let notificationCenter: any AutomationNotificationCenterClient + private let retryDelay: Duration + private let sleep: Sleep + private var policy: AutomationNotificationPolicy + private var deliveryTask: Task? + private var deliveryGeneration: UInt64 = 0 + + init( + notificationCenter: any AutomationNotificationCenterClient = SystemAutomationNotificationCenter(), + maximumRetainedEventIdentities: Int = 256, + retryDelay: Duration = .seconds(2), + sleep: @escaping Sleep = { try await Task.sleep(for: $0) } + ) { + self.notificationCenter = notificationCenter + self.retryDelay = max(.zero, retryDelay) + self.sleep = sleep + policy = AutomationNotificationPolicy( + maximumRetainedEventIdentities: maximumRetainedEventIdentities + ) + preferences = policy.preferences + } + + func setPreference( + _ preference: AutomationNotificationPreference, + isEnabled: Bool + ) async { + let action = policy.setPreference(preference, isEnabled: isEnabled) + preferences = policy.preferences + guard action == .requestAuthorization else { return } + await requestAuthorization() + } + + func synchronize(_ persisted: AutomationNotificationPreferences) async { + var shouldRequest = false + for preference in AutomationNotificationPreference.allCases { + if policy.setPreference(preference, isEnabled: persisted[preference]) == .requestAuthorization { + shouldRequest = true + } + } + preferences = policy.preferences + if shouldRequest { + await requestAuthorization() + } + } + + private func requestAuthorization() async { + deliveryState = .requesting + do { + let granted = try await notificationCenter.requestAuthorization() + policy.recordAuthorizationResult(granted: granted) + deliveryState = granted ? .enabled : .denied + } catch { + policy.recordAuthorizationResult(granted: false) + deliveryState = .authorizationFailed + } + } + + func consume(_ events: [AutomationEvent]) async { + deliveryGeneration &+= 1 + let generation = deliveryGeneration + let priorDelivery = deliveryTask + let task = Task { @MainActor [weak self] in + await priorDelivery?.value + await self?.consumeBatch(events) + } + deliveryTask = task + await task.value + if deliveryGeneration == generation { + deliveryTask = nil + } + } + + private func consumeBatch(_ events: [AutomationEvent]) async { + var attemptedDelivery = false + var deliveryFailed = false + for event in events { + guard let content = policy.notification(for: event) else { continue } + attemptedDelivery = true + if !(await deliverWithOneRetry(event, content: content)) { + deliveryFailed = true + } + } + if attemptedDelivery { + if deliveryFailed { + deliveryState = .deliveryFailed + } else if deliveryState != .deliveryFailed { + deliveryState = .enabled + } + } + } + + private func deliverWithOneRetry( + _ event: AutomationEvent, + content: AutomationNotificationContent + ) async -> Bool { + do { + try await notificationCenter.deliver(content) + return true + } catch { + policy.recordDeliveryFailure(for: event) + } + + do { + try await sleep(retryDelay) + guard let retryContent = policy.notification(for: event) else { return false } + try await notificationCenter.deliver(retryContent) + return true + } catch { + policy.recordDeliveryFailure(for: event) + return false + } + } +} diff --git a/Sources/DevScope/Stores/AutomationStore.swift b/Sources/DevScope/Stores/AutomationStore.swift new file mode 100644 index 0000000..703c47b --- /dev/null +++ b/Sources/DevScope/Stores/AutomationStore.swift @@ -0,0 +1,345 @@ +import DevScopeCore +import Foundation + +struct AutomationPendingOperation: Equatable, Sendable { + let recordID: AutomationRecord.ID + let operation: AutomationOperation + let startedAt: Date +} + +struct AutomationCapabilitySnapshot: Equatable, Sendable { + static let empty = AutomationCapabilitySnapshot( + inventoryGeneration: 0, + decisionsByRecordID: [:] + ) + + let inventoryGeneration: UInt64 + let decisionsByRecordID: [AutomationRecord.ID: AutomationCapabilityDecision] +} + +protocol AutomationCapabilityDecisionProviding: Sendable { + func decisions( + for records: [AutomationRecord] + ) async -> [AutomationRecord.ID: AutomationCapabilityDecision] +} + +protocol AutomationManagementDestinationProviding: Sendable { + func duplicateDestination(for record: AutomationRecord, label: String) -> URL? + func importDestination(for record: AutomationRecord, suggestedFilename: String) -> URL? +} + +private struct UnavailableAutomationManagementDestinationProvider: + AutomationManagementDestinationProviding +{ + func duplicateDestination(for record: AutomationRecord, label: String) -> URL? { nil } + func importDestination(for record: AutomationRecord, suggestedFilename: String) -> URL? { nil } +} + +private struct UnavailableAutomationCapabilityDecisionProvider: + AutomationCapabilityDecisionProviding +{ + func decisions( + for records: [AutomationRecord] + ) async -> [AutomationRecord.ID: AutomationCapabilityDecision] { + [:] + } +} + +protocol AutomationInventoryRefreshing: Sendable { + func refresh(force: Bool) async -> AutomationInventorySnapshot + func refreshAfterCurrent() async -> AutomationInventorySnapshot +} + +extension AutomationInventoryService: AutomationInventoryRefreshing {} + +protocol AutomationManaging: Sendable { + func perform( + _ operation: AutomationOperation, + record: AutomationRecord, + expectedChecksum: String?, + linkedProcesses: [ClassifiedDevProcess] + ) async -> AutomationOperationResult + + func restorationManifests() async -> [AutomationBackup] +} + +extension AutomationManager: AutomationManaging {} + +extension AutomationManaging { + func restorationManifests() async -> [AutomationBackup] { [] } +} + +@MainActor +final class AutomationStore: ObservableObject { + typealias Sleep = @Sendable (Duration) async throws -> Void + + private enum RefreshMode: Int { + case automatic + case force + case afterCurrent + + func merged(with other: RefreshMode) -> RefreshMode { + rawValue >= other.rawValue ? self : other + } + } + + @Published private(set) var snapshot = ProcessStoreLiveSnapshot.emptyAutomationInventory + @Published private(set) var isRefreshing = false + @Published private(set) var pendingOperation: AutomationPendingOperation? + @Published private(set) var operationResult: AutomationOperationResult? + @Published private(set) var operationResultRecordID: AutomationRecord.ID? + @Published private(set) var capabilitySnapshot = AutomationCapabilitySnapshot.empty + @Published private(set) var backups: [AutomationBackup] = [] + @Published var selectedRecordID: AutomationRecord.ID? + + private let inventoryService: any AutomationInventoryRefreshing + private let manager: any AutomationManaging + private let capabilityDecisionProvider: any AutomationCapabilityDecisionProviding + private let destinationProvider: any AutomationManagementDestinationProviding + private let sleep: Sleep + private let initialRefreshDelay: Duration + private let automaticRefreshInterval: Duration + private let now: @Sendable () -> Date + private var lifecycleTask: Task? + private var operationTask: Task? + private var activeRefreshTask: Task? + private var activeRefreshWaiters: [CheckedContinuation] = [] + private var pendingRefreshMode: RefreshMode? + private var pendingRefreshWaiters: [CheckedContinuation] = [] + private var refreshGeneration: UInt64 = 0 + private var operationGeneration: UInt64 = 0 + + init( + inventoryService: any AutomationInventoryRefreshing, + manager: any AutomationManaging, + capabilityDecisionProvider: any AutomationCapabilityDecisionProviding = + UnavailableAutomationCapabilityDecisionProvider(), + destinationProvider: any AutomationManagementDestinationProviding = + UnavailableAutomationManagementDestinationProvider(), + initialRefreshDelay: Duration = .milliseconds(350), + automaticRefreshInterval: Duration = .seconds(60), + sleep: @escaping Sleep = { try await Task.sleep(for: $0) }, + now: @escaping @Sendable () -> Date = Date.init + ) { + self.inventoryService = inventoryService + self.manager = manager + self.capabilityDecisionProvider = capabilityDecisionProvider + self.destinationProvider = destinationProvider + self.initialRefreshDelay = max(.zero, initialRefreshDelay) + self.automaticRefreshInterval = max(.seconds(60), automaticRefreshInterval) + self.sleep = sleep + self.now = now + } + + deinit { + lifecycleTask?.cancel() + operationTask?.cancel() + activeRefreshTask?.cancel() + for waiter in activeRefreshWaiters + pendingRefreshWaiters { waiter.resume() } + } + + func start() { + guard lifecycleTask == nil else { return } + let delay = initialRefreshDelay + let interval = automaticRefreshInterval + let sleep = sleep + lifecycleTask = Task { [weak self] in + do { + try await sleep(delay) + try Task.checkCancellation() + self?.refreshAutomatically() + while !Task.isCancelled { + try await sleep(interval) + try Task.checkCancellation() + self?.refreshAutomatically() + } + } catch is CancellationError { + return + } catch { + return + } + } + } + + func stop() { + lifecycleTask?.cancel() + lifecycleTask = nil + refreshGeneration &+= 1 + activeRefreshTask?.cancel() + activeRefreshTask = nil + pendingRefreshMode = nil + for waiter in activeRefreshWaiters + pendingRefreshWaiters { waiter.resume() } + activeRefreshWaiters.removeAll() + pendingRefreshWaiters.removeAll() + isRefreshing = false + } + + func refresh() { + requestRefresh(mode: .force) + } + + func dismissOperationResult() { + operationResult = nil + operationResultRecordID = nil + } + + func duplicateDestination(for record: AutomationRecord, label: String) -> URL? { + destinationProvider.duplicateDestination(for: record, label: label) + } + + func importDestination(for record: AutomationRecord, suggestedFilename: String) -> URL? { + destinationProvider.importDestination(for: record, suggestedFilename: suggestedFilename) + } + + func perform( + _ operation: AutomationOperation, + record: AutomationRecord, + expectedChecksum: String?, + linkedProcesses: [ClassifiedDevProcess] + ) { + guard pendingOperation == nil else { return } + let pending = AutomationPendingOperation( + recordID: record.id, + operation: operation, + startedAt: now() + ) + pendingOperation = pending + operationGeneration &+= 1 + let generation = operationGeneration + let manager = manager + operationTask = Task { [weak self] in + let result = await manager.perform( + operation, + record: record, + expectedChecksum: expectedChecksum, + linkedProcesses: linkedProcesses + ) + guard let self, + !Task.isCancelled, + operationGeneration == generation, + pendingOperation == pending else { return } + operationResult = result + operationResultRecordID = record.id + // AutomationManager returns only after its authoritative refresh. Read + // that cached generation instead of forcing every source to scan again. + await waitForRefresh(mode: .automatic) + guard !Task.isCancelled, + operationGeneration == generation, + pendingOperation == pending else { return } + pendingOperation = nil + operationTask = nil + } + } + + private func refreshAutomatically() { + requestRefresh(mode: .automatic) + } + + private func requestRefresh(mode: RefreshMode) { + enqueueRefresh(mode: mode, waiter: nil) + } + + private func waitForRefresh(mode: RefreshMode) async { + await withCheckedContinuation { continuation in + enqueueRefresh(mode: mode, waiter: continuation) + } + } + + private func enqueueRefresh( + mode: RefreshMode, + waiter: CheckedContinuation? + ) { + guard activeRefreshTask == nil else { + pendingRefreshMode = pendingRefreshMode?.merged(with: mode) ?? mode + if let waiter { pendingRefreshWaiters.append(waiter) } + return + } + startRefresh(mode: mode, waiters: waiter.map { [$0] } ?? []) + } + + private func startRefresh( + mode: RefreshMode, + waiters: [CheckedContinuation] + ) { + precondition(activeRefreshTask == nil) + refreshGeneration &+= 1 + let generation = refreshGeneration + let service = inventoryService + let capabilityDecisionProvider = capabilityDecisionProvider + let manager = manager + activeRefreshWaiters = waiters + isRefreshing = true + activeRefreshTask = Task { [weak self] in + let refreshed: AutomationInventorySnapshot + switch mode { + case .automatic: + refreshed = await service.refresh(force: false) + case .force: + refreshed = await service.refresh(force: true) + case .afterCurrent: + refreshed = await service.refreshAfterCurrent() + } + let decisions = await capabilityDecisionProvider.decisions(for: refreshed.records) + let backups = await manager.restorationManifests() + self?.completeRefresh( + generation: generation, + snapshot: refreshed, + capabilityDecisions: decisions, + backups: backups + ) + } + } + + private func completeRefresh( + generation: UInt64, + snapshot refreshed: AutomationInventorySnapshot, + capabilityDecisions: [AutomationRecord.ID: AutomationCapabilityDecision], + backups refreshedBackups: [AutomationBackup] + ) { + guard generation == refreshGeneration else { return } + activeRefreshTask = nil + let completedWaiters = activeRefreshWaiters + activeRefreshWaiters.removeAll() + + // Every ID-validated completion is a coherent publication point. A queued + // refresh may replace it later, but must not hide post-operation evidence + // from the waiter that requested this generation. + snapshot = refreshed + capabilitySnapshot = AutomationCapabilitySnapshot( + inventoryGeneration: refreshed.generation, + decisionsByRecordID: completedCapabilityDecisions( + for: refreshed.records, + provided: capabilityDecisions + ) + ) + backups = refreshedBackups + for waiter in completedWaiters { waiter.resume() } + + if let pendingMode = pendingRefreshMode { + let waiters = pendingRefreshWaiters + self.pendingRefreshMode = nil + pendingRefreshWaiters.removeAll() + startRefresh(mode: pendingMode, waiters: waiters) + } else { + isRefreshing = false + } + } + + private func completedCapabilityDecisions( + for records: [AutomationRecord], + provided: [AutomationRecord.ID: AutomationCapabilityDecision] + ) -> [AutomationRecord.ID: AutomationCapabilityDecision] { + Dictionary(uniqueKeysWithValues: records.map { record in + if let decision = provided[record.id] { + return (record.id, decision) + } + return ( + record.id, + AutomationCapabilityDecision( + capabilities: record.capabilities.intersection([.exportRecord]), + reason: "DevScope could not verify source ownership and path safety for management." + ) + ) + }) + } +} diff --git a/Sources/DevScope/Stores/FoundationModelGenerationOptions.swift b/Sources/DevScope/Stores/FoundationModelGenerationOptions.swift new file mode 100644 index 0000000..4ac866e --- /dev/null +++ b/Sources/DevScope/Stores/FoundationModelGenerationOptions.swift @@ -0,0 +1,22 @@ +#if canImport(FoundationModels) + import FoundationModels + + @available(macOS 26.0, *) + extension GenerationOptions { + static func devScopeGreedy(maximumResponseTokens: Int) -> Self { + #if compiler(>=6.4) + Self( + samplingMode: .greedy, + temperature: 0.0, + maximumResponseTokens: maximumResponseTokens + ) + #else + Self( + sampling: .greedy, + temperature: 0.0, + maximumResponseTokens: maximumResponseTokens + ) + #endif + } + } +#endif diff --git a/Sources/DevScope/Stores/ProcessNameEnhancer.swift b/Sources/DevScope/Stores/ProcessNameEnhancer.swift new file mode 100644 index 0000000..395b899 --- /dev/null +++ b/Sources/DevScope/Stores/ProcessNameEnhancer.swift @@ -0,0 +1,151 @@ +import DevScopeCore +import Foundation + +#if canImport(FoundationModels) +import FoundationModels +#endif + +actor ProcessNameEnhancer { + private var cache: [ProcessCacheIdentity: String] = [:] + + func enhancedName(for item: ClassifiedDevProcess) async -> String? { + guard shouldEnhance(item) else { + return nil + } + let identity = ProcessCacheIdentity(process: item.process) + + if let cached = cache[identity] { + if Self.isSafeDisplayName(cached, fallback: fallbackCandidate(for: item)) { + return cached + } + cache[identity] = nil + } + + guard let name = await generateName(for: item) else { + return nil + } + + cache[identity] = name + return name + } + + func retainOnly(identities: Set) { + cache = cache.filter { identities.contains($0.key) } + } + + func isAvailable() -> Bool { + #if canImport(FoundationModels) + if #available(macOS 26.0, *) { + return SystemLanguageModel.default.isAvailable + } + #endif + return false + } + + private func fallbackCandidate(for item: ClassifiedDevProcess) -> String { + item.classification.displayName + } + + private func prompt(for item: ClassifiedDevProcess) -> String { + """ + Rename this local development process for a macOS process monitor. + Return only a short human-readable label, 2 to 5 words, no quotes, no punctuation unless needed. + Prefer product/workload words like "Uvicorn API", "Next dev server", "Node MCP server", "Python research loop". + Preserve known product names such as Ollama, Redis, Postgres, Docker, and LM Studio. + Do not expose full paths. + + Runtime: \(item.classification.kind.rawValue) + Current label: \(fallbackCandidate(for: item)) + Project: \(ProcessPresentation.projectName(for: item) ?? "Unknown") + Executable: \(item.process.executableName) + Command: \(ProcessPresentation.redactedCommand(item.process.command)) + """ + } + + nonisolated static func isSafeDisplayName(_ candidate: String, fallback: String) -> Bool { + let cleaned = candidate + .replacingOccurrences(of: "\n", with: " ") + .trimmingCharacters(in: .whitespacesAndNewlines) + let lowered = cleaned.lowercased() + let blockedFragments = [ + "name convention", + "product name", + "return only", + "human-readable", + "macos process monitor" + ] + let blockedExactNames = [ + "bash", + "go", + "java", + "javascript", + "node", + "python", + "rust", + "shell", + "swift", + "typescript", + "zsh" + ] + + guard cleaned.count >= 4, + cleaned.count <= 42, + cleaned.split(separator: " ").count <= 6, + !cleaned.contains("/"), + !cleaned.contains(","), + !blockedExactNames.contains(lowered), + !blockedFragments.contains(where: { lowered.contains($0) }) else { + return false + } + + return cleaned.localizedCaseInsensitiveCompare(fallback) != .orderedSame + } + + private func sanitize(_ candidate: String, fallback: String) -> String? { + let cleaned = candidate + .replacingOccurrences(of: "\n", with: " ") + .trimmingCharacters(in: .whitespacesAndNewlines) + + guard Self.isSafeDisplayName(cleaned, fallback: fallback) else { + return nil + } + + return cleaned + } + + private func generateName(for item: ClassifiedDevProcess) async -> String? { + #if canImport(FoundationModels) + if #available(macOS 26.0, *) { + let model = SystemLanguageModel.default + guard model.isAvailable else { + return nil + } + + do { + let session = LanguageModelSession( + model: model, + instructions: "You create concise labels for local development processes. Be literal, conservative, and never invent a product name that is not implied by the command." + ) + let response = try await session.respond( + to: prompt(for: item), + options: .devScopeGreedy(maximumResponseTokens: 12) + ) + return sanitize(response.content, fallback: fallbackCandidate(for: item)) + } catch { + return nil + } + } + #endif + + return nil + } + + private func shouldEnhance(_ item: ClassifiedDevProcess) -> Bool { + switch item.classification.kind { + case .ai, .browser, .macApp, .backgroundAgent, .systemService, .shell, .other: + return false + case .javascript, .python, .swift, .rust, .go, .flutter, .java, .database, .container, .webServer, .mcp: + return true + } + } +} diff --git a/Sources/DevScope/Stores/ProcessSnapshotWorker.swift b/Sources/DevScope/Stores/ProcessSnapshotWorker.swift new file mode 100644 index 0000000..3ea4198 --- /dev/null +++ b/Sources/DevScope/Stores/ProcessSnapshotWorker.swift @@ -0,0 +1,18 @@ +import DevScopeCore +import Foundation + +actor ProcessSnapshotWorker { + private var builder = ProcessSnapshotBuilder() + + func invalidateWorkspaceFacts() { + builder.invalidateWorkspaceFacts() + } + + func build( + processes: [DevProcess], + now: Date, + graceInterval: TimeInterval + ) -> DevProcessSnapshot { + builder.build(processes: processes, now: now, graceInterval: graceInterval) + } +} diff --git a/Sources/DevScope/Stores/ProcessStore.swift b/Sources/DevScope/Stores/ProcessStore.swift new file mode 100644 index 0000000..646a44c --- /dev/null +++ b/Sources/DevScope/Stores/ProcessStore.swift @@ -0,0 +1,701 @@ +import DevScopeCore +import Foundation + +struct ProcessActionFeedback: Identifiable, Equatable { + enum Kind: Equatable { + case success + case warning + case error + case info + } + + let id = UUID() + let title: String + let detail: String + let symbolName: String + let kind: Kind +} + +private enum SignalVerificationContext { + case complete + case partial(failedTarget: ProcessIdentity, reason: String) +} + +private struct PendingSignalVerification { + let targets: [ProcessIdentity] + let signalName: String + let context: SignalVerificationContext +} + +@MainActor +final class ProcessStore: ObservableObject { + @Published private(set) var liveSnapshot = ProcessStoreLiveSnapshot() + @Published private(set) var enhancedNames: [ProcessCacheIdentity: String] = [:] + @Published private(set) var workflowNotes: [String: WorkflowAINote] = [:] + @Published private(set) var isRefreshing = false + @Published private(set) var isEnhancingNames = false + @Published private(set) var isEnhancingWorkflows = false + @Published private(set) var actionFeedback: ProcessActionFeedback? + @Published private(set) var automationEvents: [AutomationEvent] = [] + + var processes: [DevProcess] { liveSnapshot.processes } + var classifiedProcesses: [ClassifiedDevProcess] { liveSnapshot.classifiedProcesses } + var workflows: [DevWorkflow] { liveSnapshot.workflows } + var dashboardMetricHistory: [DevProcessMetricSample] { liveSnapshot.dashboardMetricHistory } + var liveProcessIDs: Set { liveSnapshot.liveProcessIDs } + var statusMessage: String { + get { liveSnapshot.statusMessage } + set { liveSnapshot.statusMessage = newValue } + } + var lastRefresh: Date? { liveSnapshot.lastRefresh } + + private let scanner: ProcessProviding + private let gpuMetricProvider: GPUMetricProviding + private let killer: ProcessKiller + private let nameEnhancer = ProcessNameEnhancer() + private let workflowEnhancer = WorkflowIntelligenceEnhancer() + private let snapshotWorker = ProcessSnapshotWorker() + private var processMetricHistory = ProcessMetricHistoryStore(limit: 120) + private var automaticRefreshTask: Task? + private var feedbackDismissalTask: Task? + private var pendingSignalVerifications: [PendingSignalVerification] = [] + private var automationContext = AutomationPresentationContext( + inventory: ProcessStoreLiveSnapshot.emptyAutomationInventory, + longRunningThreshold: AutomationPresentationContext.defaultLongRunningThreshold + ) + private var automationContextVersion: UInt64 = 0 + private var automationEventDetector = AutomationEventDetector() + private var previousAutomationPresentation: AutomationPresentationSnapshot? + private var resetAutomationEventBaselineOnNextScan = false + private var isScanInFlight = false + private var manualRefreshPending = false + private var lastCurrentDirectoryRefresh = Date.distantPast + private var lastNameEnhancement = Date.distantPast + private var lastWorkflowEnhancement = Date.distantPast + private var lastGPUMetricRefresh = Date.distantPast + private var cachedGPUMetric: DevGPUMetric? + private let currentDirectoryRefreshInterval: TimeInterval = 120 + private let gpuMetricRefreshInterval: TimeInterval = 10 + private let automaticRefreshInterval: Duration = .seconds(2) + private let nameEnhancementInterval: TimeInterval = 300 + private let workflowEnhancementInterval: TimeInterval = 300 + private let missingProcessGraceInterval: TimeInterval = 2.5 + private let maxDashboardMetricSamples = 180 + var usesAppleNaming = true + + init( + scanner: ProcessProviding = SystemProcessScanner(), + gpuMetricProvider: GPUMetricProviding = SystemGPUMetricProvider(), + killer: ProcessKiller = ProcessKiller() + ) { + self.scanner = scanner + self.gpuMetricProvider = gpuMetricProvider + self.killer = killer + } + + deinit { + automaticRefreshTask?.cancel() + feedbackDismissalTask?.cancel() + } + + func startRealtimeUpdates() { + guard automaticRefreshTask == nil else { + return + } + + automaticRefreshTask = Task { [weak self] in + try? await Task.sleep(for: .milliseconds(350)) + guard !Task.isCancelled else { + return + } + self?.refresh() + + while !Task.isCancelled { + try? await Task.sleep(for: self?.automaticRefreshInterval ?? .seconds(2)) + guard !Task.isCancelled else { + return + } + self?.refresh(isAutomatic: true) + } + } + } + + func stopRealtimeUpdates() { + automaticRefreshTask?.cancel() + automaticRefreshTask = nil + } + + func updateAutomationContext( + inventory: AutomationInventorySnapshot, + longRunningThreshold: TimeInterval + ) { + let nextContext = AutomationPresentationContext( + inventory: inventory, + longRunningThreshold: longRunningThreshold + ) + guard nextContext != automationContext else { return } + if nextContext.longRunningThreshold != automationContext.longRunningThreshold { + resetAutomationEventBaselineOnNextScan = true + } + automationContext = nextContext + automationContextVersion &+= 1 + + let projection = nextContext.build( + processes: liveSnapshot.processes, + now: liveSnapshot.lastRefresh ?? Date() + ) + liveSnapshot = liveSnapshot.replacingAutomationProjection(projection) + } + + func refresh(isAutomatic: Bool = false) { + guard !isScanInFlight else { + if !isAutomatic { + manualRefreshPending = true + isRefreshing = true + } + return + } + + let scanner = scanner + let gpuMetricProvider = gpuMetricProvider + let existingProcesses = processes + let capturedAutomationContext = automationContext + let capturedAutomationContextVersion = automationContextVersion + let includeCurrentDirectories = shouldRefreshCurrentDirectories(isAutomatic: isAutomatic) + let includeGPUMetric = shouldRefreshGPUMetric(isAutomatic: isAutomatic) + isScanInFlight = true + if !isAutomatic { + isRefreshing = true + } + + Task { @MainActor in + do { + if includeCurrentDirectories { + await snapshotWorker.invalidateWorkspaceFacts() + } + let scanResult = try await Task.detached(priority: .userInitiated) { + let processes = try scanner.snapshot(includeCurrentDirectories: includeCurrentDirectories) + let gpuMetric = includeGPUMetric ? try? gpuMetricProvider.snapshot() : nil + return (processes: processes, gpuMetric: gpuMetric) + }.value + if includeGPUMetric { + cachedGPUMetric = scanResult.gpuMetric + lastGPUMetricRefresh = Date() + } + let effectiveGPUMetric = includeGPUMetric ? scanResult.gpuMetric : cachedGPUMetric + let mergedSnapshot = mergeCurrentDirectories( + into: scanResult.processes, + from: existingProcesses, + includeCurrentDirectories: includeCurrentDirectories + ) + let timestamp = Date() + let presentationSnapshot = await snapshotWorker.build( + processes: mergedSnapshot, + now: timestamp, + graceInterval: missingProcessGraceInterval + ) + processMetricHistory.record( + processes: mergedSnapshot, + gpuMetric: effectiveGPUMetric, + timestamp: timestamp + ) + let nextDashboardHistory = updatedDashboardMetricHistory( + current: liveSnapshot.dashboardMetricHistory, + processes: mergedSnapshot, + gpuMetric: effectiveGPUMetric, + timestamp: timestamp + ) + let newestAutomationContext = capturedAutomationContextVersion == automationContextVersion + ? capturedAutomationContext + : automationContext + let automationPresentation = newestAutomationContext.build( + processes: presentationSnapshot.processes, + now: timestamp + ) + automationEvents = automationEventDetector.events( + previous: resetAutomationEventBaselineOnNextScan + ? nil : previousAutomationPresentation, + current: automationPresentation, + now: timestamp + ) + previousAutomationPresentation = automationPresentation + resetAutomationEventBaselineOnNextScan = false + liveSnapshot = ProcessStoreLiveSnapshot( + processes: presentationSnapshot.processes, + classifiedProcesses: presentationSnapshot.classified, + workflows: presentationSnapshot.workflows, + dashboardMetricHistory: nextDashboardHistory, + liveProcessIDs: presentationSnapshot.liveProcessIDs, + automationInventory: automationPresentation.inventory, + automationLinksByProcessID: automationPresentation.linksByProcessID, + allAutomationLinksByProcessID: automationPresentation.allLinksByProcessID, + longRunningProcessIDs: automationPresentation.longRunningProcessIDs, + statusMessage: "\(presentationSnapshot.liveProcessIDs.count) running items found", + lastRefresh: timestamp + ) + let enhanceableIdentities = Set( + presentationSnapshot.classified + .filter(isEnhanceableForNaming) + .map { ProcessCacheIdentity(process: $0.process) } + ) + enhancedNames = enhancedNames.filter { enhanceableIdentities.contains($0.key) } + workflowNotes = workflowNotes.filter { entry in + presentationSnapshot.workflows.contains { $0.id == entry.key } + } + if includeCurrentDirectories { + lastCurrentDirectoryRefresh = timestamp + } + if usesAppleNaming && shouldEnhanceNames(isAutomatic: isAutomatic) { + let liveItems = presentationSnapshot.classified.filter { + presentationSnapshot.liveProcessIDs.contains($0.process.pid) + } + enhanceVisibleNamesIfAvailable(items: liveItems) + } + if usesAppleNaming && shouldEnhanceWorkflows(isAutomatic: isAutomatic) { + enhanceWorkflowNotesIfAvailable( + workflows: presentationSnapshot.workflows, + items: presentationSnapshot.classified + ) + } + resolvePendingSignalVerifications() + } catch { + statusMessage = error.localizedDescription + } + isScanInFlight = false + if manualRefreshPending { + manualRefreshPending = false + refresh() + } else if !isAutomatic { + isRefreshing = false + } + } + } + + func displayName(for item: ClassifiedDevProcess) -> String { + guard usesAppleNaming, + let enhancedName = enhancedNames[ProcessCacheIdentity(process: item.process)], + ProcessNameEnhancer.isSafeDisplayName(enhancedName, fallback: item.classification.displayName) else { + return item.classification.displayName + } + + return enhancedName + } + + func metricHistory(for processID: Int32) -> [DevProcessMetricSample] { + processMetricHistory.history(for: processID) + } + + func familySummary(for process: DevProcess) -> ProcessFamilySummary { + ProcessPresentation.familySummary(for: process, in: processes) + } + + func isProcessLive(pid: Int32) -> Bool { + liveProcessIDs.contains(pid) + } + + private func enhanceVisibleNamesIfAvailable(items classifiedItems: [ClassifiedDevProcess]) { + guard !isEnhancingNames else { + return + } + + let items = Array(classifiedItems.filter(isEnhanceableForNaming).prefix(8)) + guard !items.isEmpty else { + return + } + + let enhancer = nameEnhancer + isEnhancingNames = true + + Task { @MainActor in + lastNameEnhancement = Date() + guard await enhancer.isAvailable() else { + isEnhancingNames = false + return + } + + await enhancer.retainOnly( + identities: Set(items.map { ProcessCacheIdentity(process: $0.process) }) + ) + var names: [ProcessCacheIdentity: String] = [:] + + for item in items { + if let enhancedName = await enhancer.enhancedName(for: item) { + names[ProcessCacheIdentity(process: item.process)] = enhancedName + } + } + + enhancedNames.merge(names) { _, new in new } + isEnhancingNames = false + } + } + + private func enhanceWorkflowNotesIfAvailable(workflows: [DevWorkflow], items: [ClassifiedDevProcess]) { + guard !isEnhancingWorkflows, !workflows.isEmpty else { + return + } + + let enhancer = workflowEnhancer + isEnhancingWorkflows = true + + Task { @MainActor in + lastWorkflowEnhancement = Date() + let notes = await enhancer.notes(for: workflows, items: items) + if !notes.isEmpty { + workflowNotes.merge(notes) { _, new in new } + } + isEnhancingWorkflows = false + } + } + + func actionDecision(for item: ClassifiedDevProcess) -> ProcessActionDecision { + ProcessActionPolicy.decision( + for: item, + currentProcessID: Int32(ProcessInfo.processInfo.processIdentifier) + ) + } + + private func allowSignal(_ item: ClassifiedDevProcess) -> Bool { + let decision = actionDecision(for: item) + guard let reason = decision.reason else { return true } + statusMessage = reason + presentFeedback( + title: "Process protected", + detail: reason, + symbolName: "lock.shield.fill", + kind: .warning + ) + return false + } + + func terminate(_ item: ClassifiedDevProcess) { + guard allowSignal(item) else { return } + let process = item.process + do { + let targets = try killer.terminate( + item, + currentProcessID: Int32(ProcessInfo.processInfo.processIdentifier) + ) + statusMessage = "Sent TERM to PID \(process.pid)" + presentFeedback( + title: "TERM sent", + detail: "\(process.executable) · PID \(process.pid)", + symbolName: "xmark.circle", + kind: .info + ) + scheduleSignalVerification(targets: targets, signalName: "TERM") + } catch { + handleSignalExecutionError(error, signalName: "TERM") + } + } + + func forceTerminate(_ item: ClassifiedDevProcess) { + guard allowSignal(item) else { return } + let process = item.process + do { + let targets = try killer.forceTerminate( + item, + currentProcessID: Int32(ProcessInfo.processInfo.processIdentifier) + ) + statusMessage = "Sent KILL to PID \(process.pid)" + presentFeedback( + title: "KILL sent", + detail: "\(process.executable) · PID \(process.pid)", + symbolName: "bolt.fill", + kind: .warning + ) + scheduleSignalVerification(targets: targets, signalName: "KILL") + } catch { + handleSignalExecutionError(error, signalName: "KILL") + } + } + + func terminateTree(root item: ClassifiedDevProcess) { + guard allowSignal(item) else { return } + do { + let targets = try killer.terminateTree( + root: item, + processes: processes, + classifiedProcesses: classifiedProcesses, + currentProcessID: Int32(ProcessInfo.processInfo.processIdentifier) + ) + statusMessage = "Sent TERM to \(targets.count) processes" + presentFeedback( + title: "TERM tree sent", + detail: "\(targets.count) process\(targets.count == 1 ? "" : "es") signaled", + symbolName: "point.3.connected.trianglepath.dotted", + kind: .info + ) + scheduleSignalVerification(targets: targets, signalName: "TERM") + } catch { + handleSignalExecutionError(error, signalName: "TERM") + } + } + + func forceTerminateTree(root item: ClassifiedDevProcess) { + guard allowSignal(item) else { return } + do { + let targets = try killer.forceTerminateTree( + root: item, + processes: processes, + classifiedProcesses: classifiedProcesses, + currentProcessID: Int32(ProcessInfo.processInfo.processIdentifier) + ) + statusMessage = "Sent KILL to \(targets.count) processes" + presentFeedback( + title: "KILL tree sent", + detail: "\(targets.count) process\(targets.count == 1 ? "" : "es") signaled", + symbolName: "bolt.horizontal.fill", + kind: .warning + ) + scheduleSignalVerification(targets: targets, signalName: "KILL") + } catch { + handleSignalExecutionError(error, signalName: "KILL") + } + } + + func presentFeedback( + title: String, + detail: String, + symbolName: String, + kind: ProcessActionFeedback.Kind = .info + ) { + actionFeedback = ProcessActionFeedback( + title: title, + detail: detail, + symbolName: symbolName, + kind: kind + ) + + feedbackDismissalTask?.cancel() + feedbackDismissalTask = Task { [weak self] in + try? await Task.sleep(for: .seconds(5)) + guard !Task.isCancelled else { + return + } + await MainActor.run { + self?.actionFeedback = nil + } + } + } + + private func scheduleSignalVerification( + targets: [ProcessIdentity], + signalName: String, + context: SignalVerificationContext = .complete + ) { + var seenPIDs = Set() + let targets = targets.filter { seenPIDs.insert($0.pid).inserted } + guard !targets.isEmpty else { return } + + Task { @MainActor [weak self] in + try? await Task.sleep(for: .milliseconds(700)) + guard !Task.isCancelled, let self else { return } + pendingSignalVerifications.append( + PendingSignalVerification( + targets: targets, + signalName: signalName, + context: context + ) + ) + refresh() + } + } + + private func resolvePendingSignalVerifications() { + guard !pendingSignalVerifications.isEmpty else { return } + let verifications = pendingSignalVerifications + pendingSignalVerifications.removeAll() + let currentProcesses = processes.filter { liveProcessIDs.contains($0.pid) } + + for verification in verifications { + let observation = ProcessSignalObservation.observe( + targets: verification.targets, + in: currentProcesses + ) + switch verification.context { + case .complete: + presentCompleteSignalVerification( + observation, + signalName: verification.signalName + ) + case let .partial(failedTarget, reason): + let presentation = ProcessPartialSignalVerificationPresentation.make( + signalName: verification.signalName, + observation: observation, + failedTarget: failedTarget, + reason: reason + ) + statusMessage = presentation.statusMessage + presentFeedback( + title: presentation.title, + detail: presentation.detail, + symbolName: presentation.symbolName, + kind: .warning + ) + } + } + } + + private func presentCompleteSignalVerification( + _ observation: ProcessSignalObservationSummary, + signalName: String + ) { + let targetPIDs = observation.observations.map(\.target.pid) + if !observation.unverifiable.isEmpty { + let unverifiablePIDs = observation.unverifiable.map(\.pid).sorted() + let stillRunningPIDs = observation.stillRunning.map(\.pid).sorted() + var details = [ + "Could not verify birth identity for: \(unverifiablePIDs.map(String.init).joined(separator: ", "))" + ] + if !stillRunningPIDs.isEmpty { + details.append("Still running: \(stillRunningPIDs.map(String.init).joined(separator: ", "))") + } + statusMessage = "\(signalName) result could not be verified" + presentFeedback( + title: "Signal result not verified", + detail: details.joined(separator: ". "), + symbolName: "questionmark.circle.fill", + kind: .warning + ) + } else if observation.verifiesAllTargetsStopped { + let detail = targetPIDs.count == 1 + ? "PID \(targetPIDs[0]) exited" + : "\(targetPIDs.count) processes exited" + statusMessage = "\(signalName) completed" + presentFeedback( + title: "Process stopped", + detail: detail, + symbolName: "checkmark.circle.fill", + kind: .success + ) + } else { + let stillRunning = observation.stillRunning + statusMessage = "\(stillRunning.count) process\(stillRunning.count == 1 ? "" : "es") still running" + presentFeedback( + title: "\(signalName) still pending", + detail: "Still running: \(stillRunning.map(\.pid).sorted().map(String.init).joined(separator: ", "))", + symbolName: "exclamationmark.triangle.fill", + kind: .warning + ) + } + } + + private func presentSignalError(_ error: Error) { + presentFeedback( + title: "Signal failed", + detail: error.localizedDescription, + symbolName: "xmark.octagon.fill", + kind: .error + ) + } + + private func handleSignalExecutionError(_ error: Error, signalName: String) { + guard let failure = error as? ProcessSignalExecutionFailure, + !failure.signaledIdentities.isEmpty else { + let underlyingError = (error as? ProcessSignalExecutionFailure)?.underlyingError ?? error + statusMessage = underlyingError.localizedDescription + presentSignalError(underlyingError) + return + } + + let signaledCount = failure.signaledIdentities.count + scheduleSignalVerification( + targets: failure.signaledIdentities, + signalName: signalName, + context: .partial( + failedTarget: failure.failedTarget, + reason: failure.underlyingError.localizedDescription + ) + ) + statusMessage = "\(signalName) partially completed: \(signaledCount) signaled; PID \(failure.failedTarget.pid) failed" + presentFeedback( + title: "\(signalName) partially completed", + detail: "\(failure.localizedDescription). Earlier signals remain in effect.", + symbolName: "exclamationmark.triangle.fill", + kind: .warning + ) + } + + private func shouldRefreshCurrentDirectories(isAutomatic: Bool) -> Bool { + !isAutomatic || lastRefresh == nil || Date().timeIntervalSince(lastCurrentDirectoryRefresh) >= currentDirectoryRefreshInterval + } + + private func shouldRefreshGPUMetric(isAutomatic: Bool) -> Bool { + !isAutomatic || lastRefresh == nil || Date().timeIntervalSince(lastGPUMetricRefresh) >= gpuMetricRefreshInterval + } + + private func shouldEnhanceNames(isAutomatic: Bool) -> Bool { + !isAutomatic || Date().timeIntervalSince(lastNameEnhancement) >= nameEnhancementInterval + } + + private func shouldEnhanceWorkflows(isAutomatic: Bool) -> Bool { + !isAutomatic || Date().timeIntervalSince(lastWorkflowEnhancement) >= workflowEnhancementInterval + } + + private func isEnhanceableForNaming(_ item: ClassifiedDevProcess) -> Bool { + item.classification.kind != .ai && item.classification.kind != .other && item.classification.kind != .shell + } + + private func updatedDashboardMetricHistory( + current: [DevProcessMetricSample], + processes: [DevProcess], + gpuMetric: DevGPUMetric?, + timestamp: Date + ) -> [DevProcessMetricSample] { + var updated = current + let usage = processes.compactMap(\.resourceUsage) + let totalCPU = usage.reduce(0) { $0 + $1.cpuPercent } + let totalMemory = usage.reduce(Int64(0)) { $0 + $1.residentMemoryBytes } + + updated.append( + DevProcessMetricSample( + timestamp: timestamp, + cpuPercent: totalCPU, + residentMemoryBytes: totalMemory, + gpuPercent: gpuMetric?.utilizationPercent + ) + ) + + if updated.count > maxDashboardMetricSamples { + updated.removeFirst(updated.count - maxDashboardMetricSamples) + } + + return updated + } + + private func mergeCurrentDirectories( + into snapshot: [DevProcess], + from existingProcesses: [DevProcess], + includeCurrentDirectories: Bool + ) -> [DevProcess] { + guard !includeCurrentDirectories else { + return snapshot + } + + let existingByPID = Dictionary(uniqueKeysWithValues: existingProcesses.map { ($0.pid, $0) }) + return snapshot.map { process in + guard process.currentDirectory == nil, + let previous = existingByPID[process.pid], + let currentDirectory = ProcessScanner.carriedCurrentDirectory( + from: previous, + to: process + ) else { + return process + } + + return DevProcess( + pid: process.pid, + parentPID: process.parentPID, + executable: process.executable, + command: process.command, + argumentVector: process.argumentVector, + currentDirectory: currentDirectory, + resourceUsage: process.resourceUsage, + birthToken: process.birthToken, + bundleIdentifier: process.bundleIdentifier, + launchLabel: process.launchLabel + ) + } + } + +} diff --git a/Sources/DevScope/Stores/ProcessStoreLiveSnapshot.swift b/Sources/DevScope/Stores/ProcessStoreLiveSnapshot.swift new file mode 100644 index 0000000..da2ab4a --- /dev/null +++ b/Sources/DevScope/Stores/ProcessStoreLiveSnapshot.swift @@ -0,0 +1,34 @@ +import DevScopeCore +import Foundation + +struct ProcessStoreLiveSnapshot { + static let emptyAutomationInventory = AutomationInventorySnapshot( + generation: 0, + records: [], + health: [:], + refreshedAt: Date(timeIntervalSince1970: 0) + ) + + var processes: [DevProcess] = [] + var classifiedProcesses: [ClassifiedDevProcess] = [] + var workflows: [DevWorkflow] = [] + var dashboardMetricHistory: [DevProcessMetricSample] = [] + var liveProcessIDs: Set = [] + var automationInventory = Self.emptyAutomationInventory + var automationLinksByProcessID: [Int32: AutomationProcessLink] = [:] + var allAutomationLinksByProcessID: [Int32: [AutomationProcessLink]] = [:] + var longRunningProcessIDs: Set = [] + var statusMessage = "Ready" + var lastRefresh: Date? + + func replacingAutomationProjection( + _ projection: AutomationPresentationSnapshot + ) -> ProcessStoreLiveSnapshot { + var copy = self + copy.automationInventory = projection.inventory + copy.automationLinksByProcessID = projection.linksByProcessID + copy.allAutomationLinksByProcessID = projection.allLinksByProcessID + copy.longRunningProcessIDs = projection.longRunningProcessIDs + return copy + } +} diff --git a/Sources/DevScope/Stores/WorkflowIntelligenceEnhancer.swift b/Sources/DevScope/Stores/WorkflowIntelligenceEnhancer.swift new file mode 100644 index 0000000..2a1d4ca --- /dev/null +++ b/Sources/DevScope/Stores/WorkflowIntelligenceEnhancer.swift @@ -0,0 +1,180 @@ +import DevScopeCore +import Foundation + +#if canImport(FoundationModels) +import FoundationModels +#endif + +struct WorkflowAINote: Equatable { + let workflowID: String + let text: String +} + +struct WorkflowNoteCacheKey: Equatable, Sendable { + private struct ProcessFacts: Equatable, Sendable { + let identity: ProcessCacheIdentity + let kind: DevRuntimeKind + let displayName: String + let projectName: String? + let tags: [String] + let cpuPercent: Double? + let residentMemoryBytes: Int64? + let redactedCommand: String + } + + private let workflowID: String + private let title: String + private let kind: DevWorkflowKind + private let processIDs: [Int32] + private let primaryProject: String? + private let tags: [String] + private let totalCPU: Double + private let totalMemoryBytes: Int64 + private let risk: DevWorkflowRisk + private let summary: String + private let processFacts: [ProcessFacts] + + init(workflow: DevWorkflow, items: [ClassifiedDevProcess]) { + workflowID = workflow.id + title = workflow.title + kind = workflow.kind + processIDs = workflow.processIDs + primaryProject = workflow.primaryProject + tags = workflow.tags.map(\.title) + totalCPU = workflow.totalCPU + totalMemoryBytes = workflow.totalMemoryBytes + risk = workflow.risk + summary = workflow.summary + processFacts = items.map { item in + ProcessFacts( + identity: ProcessCacheIdentity(process: item.process), + kind: item.classification.kind, + displayName: item.classification.displayName, + projectName: ProcessPresentation.projectName(for: item), + tags: item.classification.tags.map(\.title), + cpuPercent: item.process.resourceUsage?.cpuPercent, + residentMemoryBytes: item.process.resourceUsage?.residentMemoryBytes, + redactedCommand: ProcessPresentation.redactedCommand(item.process.command) + ) + } + } +} + +actor WorkflowIntelligenceEnhancer { + private struct CacheEntry { + let key: WorkflowNoteCacheKey + let note: WorkflowAINote + } + + private var cache: [String: CacheEntry] = [:] + + func notes(for workflows: [DevWorkflow], items: [ClassifiedDevProcess]) async -> [String: WorkflowAINote] { + guard await isAvailable() else { + return [:] + } + + let itemsByPID = Dictionary(uniqueKeysWithValues: items.map { ($0.process.pid, $0) }) + var notes: [String: WorkflowAINote] = [:] + + for workflow in workflows.prefix(4) { + let relatedItems = workflow.processIDs.compactMap { itemsByPID[$0] } + let cacheKey = WorkflowNoteCacheKey(workflow: workflow, items: relatedItems) + if let cached = cache[workflow.id], cached.key == cacheKey { + notes[workflow.id] = cached.note + continue + } + + guard let note = await generateNote(for: workflow, items: relatedItems) else { + continue + } + + cache[workflow.id] = CacheEntry(key: cacheKey, note: note) + notes[workflow.id] = note + } + + cache = cache.filter { entry in + workflows.contains { $0.id == entry.key } + } + + return notes + } + + func isAvailable() async -> Bool { + #if canImport(FoundationModels) + if #available(macOS 26.0, *) { + return SystemLanguageModel.default.isAvailable + } + #endif + return false + } + + private func prompt(for workflow: DevWorkflow, items: [ClassifiedDevProcess]) -> String { + let processFacts = items.prefix(10).map { item in + let tags = item.classification.tags.map(\.title).joined(separator: ", ") + let project = workflow.primaryProject ?? ProcessPresentation.projectName(for: item) ?? "Unknown" + return """ + - PID \(item.process.pid), \(item.classification.kind.rawValue), label: \(item.classification.displayName), project: \(project), CPU: \(item.process.resourceUsage?.cpuPercent ?? 0), memory bytes: \(item.process.resourceUsage?.residentMemoryBytes ?? 0), tags: \(tags.isEmpty ? "none" : tags), command: \(ProcessPresentation.redactedCommand(item.process.command)) + """ + } + .joined(separator: "\n") + + return """ + Create one concise operator note for this local developer workflow. + Return only one sentence, max 24 words. + Do not invent facts. Do not mention private paths. Do not suggest force kill. + + Workflow: \(workflow.title) + Kind: \(workflow.kind.rawValue) + Summary: \(workflow.summary) + Total CPU: \(workflow.totalCPU) + Total memory bytes: \(workflow.totalMemoryBytes) + Processes: + \(processFacts) + """ + } + + private func generateNote(for workflow: DevWorkflow, items: [ClassifiedDevProcess]) async -> WorkflowAINote? { + #if canImport(FoundationModels) + if #available(macOS 26.0, *) { + let model = SystemLanguageModel.default + guard model.isAvailable else { + return nil + } + + do { + let session = LanguageModelSession( + model: model, + instructions: "You summarize local development process workflows. Be factual, conservative, and concise." + ) + let response = try await session.respond( + to: prompt(for: workflow, items: items), + options: .devScopeGreedy(maximumResponseTokens: 40) + ) + return sanitize(response.content, workflowID: workflow.id) + } catch { + return nil + } + } + #endif + + return nil + } + + private func sanitize(_ text: String, workflowID: String) -> WorkflowAINote? { + let cleaned = text + .replacingOccurrences(of: "\n", with: " ") + .trimmingCharacters(in: .whitespacesAndNewlines) + let lowered = cleaned.lowercased() + + guard cleaned.count >= 8, + cleaned.count <= 180, + !cleaned.contains("/Users/"), + !cleaned.contains("force kill"), + !lowered.contains("i think"), + !lowered.contains("probably") else { + return nil + } + + return WorkflowAINote(workflowID: workflowID, text: cleaned) + } +} diff --git a/Sources/DevScope/Views/ApplicationFamilyListView.swift b/Sources/DevScope/Views/ApplicationFamilyListView.swift new file mode 100644 index 0000000..649f217 --- /dev/null +++ b/Sources/DevScope/Views/ApplicationFamilyListView.swift @@ -0,0 +1,213 @@ +import DevScopeCore +import SwiftUI + +struct ApplicationFamilyListView: View { + @Environment(\.accessibilityReduceMotion) private var reduceMotion + let families: [ProcessApplicationFamily] + @Binding var selection: Int32? + @Binding var expandedFamilyIDs: Set + let displayName: (ClassifiedDevProcess) -> String + + var body: some View { + ZStack { + VStack(spacing: 0) { + header + + ScrollView { + LazyVStack(spacing: 0) { + ForEach(families) { family in + familySection(family) + } + } + } + } + + if families.isEmpty { + ContentUnavailableView( + "No matching applications", + systemImage: "macwindow.on.rectangle", + description: Text( + "Try a different search or open an application with visible OS processes." + ) + ) + } + } + } + + private var header: some View { + HStack(spacing: 0) { + Text("Application family") + .frame(maxWidth: .infinity, alignment: .leading) + Text("OS processes") + .frame(width: 96, alignment: .trailing) + Text("CPU") + .frame(width: 76, alignment: .trailing) + Text("Memory") + .frame(width: 96, alignment: .trailing) + } + .font(.caption.weight(.semibold)) + .padding(.horizontal, 12) + .padding(.vertical, 8) + .background(.ultraThinMaterial) + .overlay(alignment: .bottom) { Divider() } + } + + @ViewBuilder + private func familySection(_ family: ProcessApplicationFamily) -> some View { + let isExpanded = expandedFamilyIDs.contains(family.id) + + Button { + withAnimation(reduceMotion ? nil : .snappy(duration: 0.18)) { + if isExpanded { + expandedFamilyIDs.remove(family.id) + } else { + expandedFamilyIDs.insert(family.id) + } + } + } label: { + HStack(spacing: 0) { + HStack(spacing: 10) { + Image(systemName: isExpanded ? "chevron.down" : "chevron.right") + .font(.caption.weight(.semibold)) + .foregroundStyle(.secondary) + .frame(width: 12) + + Image(systemName: "macwindow.on.rectangle") + .font(.system(size: 15, weight: .semibold)) + .foregroundStyle(DevScopePalette.accent) + .frame(width: 28, height: 28) + .background( + DevScopePalette.accent.opacity(0.14), + in: RoundedRectangle(cornerRadius: 8, style: .continuous) + ) + + VStack(alignment: .leading, spacing: 2) { + Text(family.title) + .font(.callout.weight(.semibold)) + .lineLimit(1) + Text(roleSummary(family)) + .font(.caption) + .foregroundStyle(.secondary) + .lineLimit(1) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + + Text("\(family.members.count)") + .font(.callout.monospacedDigit().weight(.semibold)) + .frame(width: 96, alignment: .trailing) + Text(ProcessMetricFormat.cpu(family.totalCPU)) + .font(.callout.monospacedDigit()) + .frame(width: 76, alignment: .trailing) + Text(ProcessMetricFormat.memory(family.totalMemoryBytes)) + .font(.callout.monospacedDigit()) + .frame(width: 96, alignment: .trailing) + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .accessibilityLabel(family.title) + .accessibilityValue( + "\(family.members.count) OS processes, \(roleSummary(family)), \(isExpanded ? "expanded" : "collapsed")" + ) + .accessibilityHint(isExpanded ? "Collapses this application family." : "Shows its OS processes.") + .help(isExpanded ? "Hide application processes" : "Show application processes") + + if isExpanded { + ForEach(family.members) { member in + memberRow(member) + } + .transition(.opacity) + } + + Divider() + } + + private func memberRow(_ member: ApplicationProcessMember) -> some View { + let item = member.item + let isSelected = selection == item.process.pid + + return Button { + selection = item.process.pid + } label: { + HStack(spacing: 0) { + HStack(spacing: 8) { + Color.clear + .frame(width: CGFloat(min(member.depth, 8)) * 14) + + Image(systemName: member.role.symbolName) + .font(.caption.weight(.semibold)) + .foregroundStyle(roleColor(member.role)) + .frame(width: 18) + + VStack(alignment: .leading, spacing: 2) { + Text(displayName(item)) + .font(.callout.weight(isSelected ? .semibold : .regular)) + .lineLimit(1) + HStack(spacing: 6) { + Label(member.role.title, systemImage: member.role.symbolName) + .font(.caption2.weight(.semibold)) + .foregroundStyle(roleColor(member.role)) + Text("PID \(item.process.pid)") + .font(.caption.monospacedDigit()) + .foregroundStyle(.secondary) + } + } + } + .frame(maxWidth: .infinity, alignment: .leading) + + Text("") + .frame(width: 96) + Text(ProcessMetricFormat.cpu(item.process.resourceUsage?.cpuPercent)) + .font(.callout.monospacedDigit()) + .frame(width: 76, alignment: .trailing) + Text(ProcessMetricFormat.memory(item.process.resourceUsage?.residentMemoryBytes)) + .font(.callout.monospacedDigit()) + .frame(width: 96, alignment: .trailing) + } + .padding(.leading, 24) + .padding(.trailing, 12) + .padding(.vertical, 6) + .contentShape(Rectangle()) + .background(isSelected ? DevScopePalette.accent.opacity(0.18) : Color.clear) + .overlay(alignment: .leading) { + if isSelected { + Rectangle() + .fill(DevScopePalette.accent) + .frame(width: 3) + } + } + } + .buttonStyle(.plain) + .accessibilityLabel("\(displayName(item)), \(member.role.title), PID \(item.process.pid)") + .accessibilityValue( + "\(ProcessMetricFormat.cpu(item.process.resourceUsage?.cpuPercent)), \(ProcessMetricFormat.memory(item.process.resourceUsage?.residentMemoryBytes))" + ) + .accessibilityAddTraits(isSelected ? .isSelected : []) + } + + private func roleSummary(_ family: ProcessApplicationFamily) -> String { + [ + countLabel(family.applicationCount, singular: "app", plural: "apps"), + countLabel(family.helperCount, singular: "helper", plural: "helpers"), + countLabel(family.workerCount, singular: "worker", plural: "workers"), + ].joined(separator: " · ") + } + + private func countLabel(_ count: Int, singular: String, plural: String) -> String { + "\(count) \(count == 1 ? singular : plural)" + } + + private func roleColor(_ role: ApplicationProcessRole) -> Color { + switch role { + case .application: + DevScopePalette.accent + case .helper: + .secondary + case .worker: + .orange + } + } +} diff --git a/Sources/DevScope/Views/AutomationApplicationIconStore.swift b/Sources/DevScope/Views/AutomationApplicationIconStore.swift new file mode 100644 index 0000000..4dcb3a4 --- /dev/null +++ b/Sources/DevScope/Views/AutomationApplicationIconStore.swift @@ -0,0 +1,158 @@ +import AppKit +import Combine +import DevScopeCore +import Foundation + +@MainActor +protocol AutomationApplicationLocating: AnyObject { + func applicationURL(forBundleIdentifier identifier: String) -> URL? + func icon(forApplicationAt url: URL) -> NSImage? +} + +@MainActor +final class WorkspaceAutomationApplicationLocator: AutomationApplicationLocating { + func applicationURL(forBundleIdentifier identifier: String) -> URL? { + NSWorkspace.shared.urlForApplication(withBundleIdentifier: identifier) + } + + func icon(forApplicationAt url: URL) -> NSImage? { + guard url.pathExtension.localizedCaseInsensitiveCompare("app") == .orderedSame else { + return nil + } + return NSWorkspace.shared.icon(forFile: url.path) + } +} + +struct AutomationApplicationIconKey: Hashable, Sendable { + let bundleIdentifier: String? + let sourcePath: String? + let executablePath: String? +} + +@MainActor +final class AutomationApplicationIconStore: ObservableObject { + private enum Resolution { + case image(NSImage) + case missing + + var icon: NSImage? { + guard case .image(let image) = self else { + return nil + } + return image + } + } + + private let locator: AutomationApplicationLocating + private let capacity: Int + private var cache: [AutomationApplicationIconKey: Resolution] = [:] + private var insertionOrder: [AutomationApplicationIconKey] = [] + private var pending: [AutomationApplicationIconKey: Task] = [:] + + private(set) var revision = 0 + + init( + locator: AutomationApplicationLocating = WorkspaceAutomationApplicationLocator(), + capacity: Int = 256 + ) { + self.locator = locator + self.capacity = max(1, capacity) + } + + func key(for record: AutomationRecord) -> AutomationApplicationIconKey { + AutomationApplicationIconKey( + bundleIdentifier: record.providerBundleIdentifier, + sourcePath: record.sourceURL?.standardizedFileURL.path, + executablePath: record.executable.map { URL(fileURLWithPath: $0).standardizedFileURL.path } + ) + } + + func icon(for record: AutomationRecord) -> NSImage? { + guard case .image(let image) = cache[key(for: record)] else { + return nil + } + return image + } + + @discardableResult + func resolve(_ record: AutomationRecord) async -> NSImage? { + let cacheKey = key(for: record) + if let cached = cache[cacheKey] { + return cached.icon + } + + let resolutionTask: Task + if let inFlight = pending[cacheKey] { + resolutionTask = inFlight + } else { + let created = Task { @MainActor in + await Task.yield() + return resolveNow(record) + } + pending[cacheKey] = created + resolutionTask = created + } + + let resolution = await resolutionTask.value + complete(resolution, for: cacheKey) + return resolution.icon + } + + private func resolveNow(_ record: AutomationRecord) -> Resolution { + if let identifier = record.providerBundleIdentifier, + let applicationURL = locator.applicationURL(forBundleIdentifier: identifier), + let image = locator.icon(forApplicationAt: applicationURL) { + return .image(image) + } + + if let sourceURL = record.sourceURL, + sourceURL.pathExtension.localizedCaseInsensitiveCompare("app") == .orderedSame, + let image = locator.icon(forApplicationAt: sourceURL) { + return .image(image) + } + + if let applicationURL = applicationBundleURL(containing: record.executable), + let image = locator.icon(forApplicationAt: applicationURL) { + return .image(image) + } + + return .missing + } + + private func applicationBundleURL(containing path: String?) -> URL? { + guard let path else { + return nil + } + + var candidate = URL(fileURLWithPath: path).standardizedFileURL + while candidate.path != "/" { + if candidate.pathExtension.localizedCaseInsensitiveCompare("app") == .orderedSame { + return candidate + } + candidate.deleteLastPathComponent() + } + return nil + } + + private func evictIfNeeded() { + while cache.count > capacity, + let index = insertionOrder.firstIndex(where: { pending[$0] == nil }) { + cache.removeValue(forKey: insertionOrder.remove(at: index)) + } + } + + private func complete( + _ resolution: Resolution, + for cacheKey: AutomationApplicationIconKey + ) { + pending.removeValue(forKey: cacheKey) + guard cache[cacheKey] == nil else { + return + } + cache[cacheKey] = resolution + insertionOrder.removeAll { $0 == cacheKey } + insertionOrder.append(cacheKey) + evictIfNeeded() + revision &+= 1 + } +} diff --git a/Sources/DevScope/Views/AutomationConfirmationView.swift b/Sources/DevScope/Views/AutomationConfirmationView.swift new file mode 100644 index 0000000..f4e8264 --- /dev/null +++ b/Sources/DevScope/Views/AutomationConfirmationView.swift @@ -0,0 +1,62 @@ +import DevScopeCore +import SwiftUI + +struct AutomationConfirmationView: View { + @Environment(\.dismiss) private var dismiss + let action: AutomationManagementAction + let record: AutomationRecord + let policy: AutomationConfirmationPolicy + let confirm: () -> Void + @State private var enteredLabel = "" + + var body: some View { + VStack(alignment: .leading, spacing: 16) { + Label(policy.title, systemImage: action.isDestructive ? "exclamationmark.triangle" : "checkmark.shield") + .font(.title3.weight(.semibold)) + .foregroundStyle(action.isDestructive ? Color.orange : DevScopePalette.accent) + + Text(policy.consequence) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + + if let command = policy.displayedCommand { + VStack(alignment: .leading, spacing: 6) { + Text("Command") + .font(.caption.weight(.semibold)) + .foregroundStyle(.secondary) + Text(command) + .font(.system(.callout, design: .monospaced)) + .textSelection(.enabled) + .padding(10) + .frame(maxWidth: .infinity, alignment: .leading) + .background(.quaternary.opacity(0.35), in: RoundedRectangle(cornerRadius: 8)) + } + } + + if let requiredLabel = policy.requiredLabel { + VStack(alignment: .leading, spacing: 6) { + Text("Enter \(requiredLabel) to confirm") + .font(.callout.weight(.medium)) + TextField("Automation label", text: $enteredLabel) + .textFieldStyle(.roundedBorder) + .accessibilityHint("The Remove button is enabled only when this exactly matches the automation label.") + } + } + + HStack { + Spacer() + Button("Cancel") { dismiss() } + .keyboardShortcut(.cancelAction) + Button(action.title, role: action.isDestructive ? .destructive : nil) { + confirm() + dismiss() + } + .disabled(!policy.isSatisfiedByLabel(enteredLabel)) + .keyboardShortcut(action.isDestructive ? nil : .defaultAction) + } + } + .padding(20) + .frame(width: 470) + .accessibilityElement(children: .contain) + } +} diff --git a/Sources/DevScope/Views/AutomationDetailView.swift b/Sources/DevScope/Views/AutomationDetailView.swift new file mode 100644 index 0000000..b66c06f --- /dev/null +++ b/Sources/DevScope/Views/AutomationDetailView.swift @@ -0,0 +1,592 @@ +import DevScopeCore +import SwiftUI + +struct AutomationDetailView: View { + let record: AutomationRecord? + let iconStore: AutomationApplicationIconStore + let decision: AutomationCapabilityDecision? + let linkedProcesses: [ClassifiedDevProcess] + let sourceHealth: AutomationSourceHealth? + let backups: [AutomationBackup] + let pendingOperation: AutomationPendingOperation? + let operationResult: AutomationOperationResult? + let perform: (AutomationManagementAction) -> Void + let dismissResult: () -> Void + let saveExport: (AutomationExportArtifact) -> Void + let openLoginItemsSettings: () -> Void + + var body: some View { + Group { + if let record { + VStack(spacing: 0) { + AutomationManagementBar( + record: record, + decision: decision, + backups: backups, + pendingOperation: pendingOperation, + operationResult: operationResult, + perform: perform, + dismissResult: dismissResult, + saveExport: saveExport, + openLoginItemsSettings: openLoginItemsSettings + ) + + Divider() + + ScrollView { + VStack(alignment: .leading, spacing: 18) { + header(record) + healthSection(record) + overview(record) + trigger(record) + command(record) + currentProcesses + source(record) + validation(record) + operationResultSection + history(record) + } + .padding(16) + } + } + } else { + ContentUnavailableView( + "Select an automation", + systemImage: "gearshape.2", + description: Text("Choose a definition to inspect its trigger, source, live processes, validation, and supported management controls.") + ) + } + } + } + + private func header(_ record: AutomationRecord) -> some View { + HStack(alignment: .top, spacing: 12) { + AutomationIdentityView(record: record, store: iconStore, size: 48) + VStack(alignment: .leading, spacing: 6) { + Text(record.displayName) + .font(.title3.weight(.semibold)) + .fixedSize(horizontal: false, vertical: true) + Text(record.label) + .font(.caption.monospaced()) + .foregroundStyle(.secondary) + .textSelection(.enabled) + .fixedSize(horizontal: false, vertical: true) + Label(record.state.displayTitle, systemImage: record.state.symbolName) + .font(.caption.weight(.semibold)) + .foregroundStyle(record.state.tint) + .fixedSize(horizontal: false, vertical: true) + } + } + } + + @ViewBuilder + private func healthSection(_ record: AutomationRecord) -> some View { + if let sourceHealth, sourceHealth.state != .healthy { + AutomationInspectorSection(title: "Source Status", systemImage: "exclamationmark.triangle") { + Text(sourceHealth.message ?? "\(record.sourceKind.displayTitle) could not be fully inspected.") + .font(.callout) + .foregroundStyle(.orange) + .fixedSize(horizontal: false, vertical: true) + } + } + } + + private func overview(_ record: AutomationRecord) -> some View { + AutomationInspectorSection(title: "Overview", systemImage: "info.circle") { + inspectorGrid([ + ("Kind", record.kind.displayTitle), + ("State", record.state.displayTitle), + ("Ownership", record.ownership.displayTitle), + ("Enabled", record.enabledState.rawValue.capitalized), + ("Loaded", record.loadState.rawValue.capitalized), + ("Approval", record.approvalState.rawValue.capitalized), + ]) + if let provider = record.providerBundleIdentifier { + field("Owning Application", provider) + } + ForEach(record.evidence, id: \.self) { evidence in + Label("\(evidence.source): \(evidence.detail)", systemImage: evidence.strength == .strong ? "checkmark.seal" : "questionmark.circle") + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } + } + } + + private func trigger(_ record: AutomationRecord) -> some View { + AutomationInspectorSection(title: "Trigger", systemImage: "calendar.badge.clock") { + field("Schedule", record.schedule.summary) + ForEach(Array(record.schedule.triggers.enumerated()), id: \.offset) { _, trigger in + Text(String(describing: trigger)) + .font(.caption.monospaced()) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } + } + } + + private func command(_ record: AutomationRecord) -> some View { + AutomationInspectorSection(title: "Command", systemImage: "terminal") { + field("Executable", record.executable ?? "Unavailable") + if !record.arguments.isEmpty { + field("Arguments", ProcessPresentation.redactedCommand(record.arguments.joined(separator: " "))) + } + field("Working Directory", record.workingDirectory ?? "Not configured") + if !record.environment.isEmpty { + field("Environment Keys", record.environment.keys.sorted().joined(separator: ", ")) + } + } + } + + private var currentProcesses: some View { + AutomationInspectorSection(title: "Current Processes", systemImage: "waveform.path.ecg") { + if linkedProcesses.isEmpty { + Text("No strongly linked process is currently running.") + .font(.callout) + .foregroundStyle(.secondary) + } else { + ForEach(linkedProcesses, id: \.process.pid) { item in + HStack { + Text(item.classification.displayName).lineLimit(1) + Spacer() + Text("PID \(item.process.pid)").font(.caption.monospacedDigit()).foregroundStyle(.secondary) + } + } + } + } + } + + private func source(_ record: AutomationRecord) -> some View { + AutomationInspectorSection(title: "Source", systemImage: "doc.text") { + field("Provider", record.sourceKind.displayTitle) + field("Path", record.sourceURL?.path ?? "Not exposed by macOS") + field("Checksum", record.sourceChecksum ?? "Unavailable") + } + } + + private func validation(_ record: AutomationRecord) -> some View { + AutomationInspectorSection(title: "Validation", systemImage: "checkmark.shield") { + if record.validationFindings.isEmpty { + Label("No validation findings", systemImage: "checkmark.circle") + .font(.callout) + .foregroundStyle(.green) + } else { + ForEach(record.validationFindings, id: \.self) { finding in + Label(finding, systemImage: "exclamationmark.circle") + .font(.callout) + .foregroundStyle(.orange) + .fixedSize(horizontal: false, vertical: true) + } + } + } + } + + @ViewBuilder + private var operationResultSection: some View { + if let operationResult { + let result = AutomationManagementPresentation.result(operationResult) + let recoveryHandles = recoveryHandles(from: operationResult) + AutomationInspectorSection( + title: "Operation Result", + systemImage: result.isFailure ? "exclamationmark.circle" : "checkmark.circle" + ) { + VStack(alignment: .leading, spacing: 6) { + Text(result.detail) + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + resultEvidence("Applied", values: result.appliedEvidence) + resultEvidence("Verified", values: result.verificationEvidence) + resultEvidence("Rollback", values: [result.rollbackEvidence]) + if let backup = result.backupEvidence { + resultEvidence("Recovery Backup", values: [backup]) + } + resultEvidence("File Mutation", values: result.mutationEvidence) + if let recovery = result.recoveryGuidance { + Text(recovery).font(.caption).foregroundStyle(.orange).textSelection(.enabled) + } + if !recoveryHandles.isEmpty { + VStack(alignment: .leading, spacing: 8) { + Text("Recovery Locations") + .font(.caption2.weight(.semibold)) + .foregroundStyle(.secondary) + ForEach(recoveryHandles, id: \.fileURL.path) { handle in + VStack(alignment: .leading, spacing: 5) { + Text(handle.fileURL.path) + .font(.caption.monospaced()) + .textSelection(.enabled) + .fixedSize(horizontal: false, vertical: true) + HStack(spacing: 8) { + Button("Reveal in Finder") { + NSWorkspace.shared.activateFileViewerSelecting([handle.fileURL]) + } + Button("Copy Path") { + copyRecoveryPath(handle.fileURL) + } + } + .controlSize(.small) + } + } + } + .padding(.top, 4) + } + } + .padding(10) + .background(.quaternary.opacity(0.25), in: RoundedRectangle(cornerRadius: 10)) + } + } + } + + private func history(_ record: AutomationRecord) -> some View { + AutomationInspectorSection(title: "History & Recovery", systemImage: "clock.arrow.circlepath") { + let matching = backups.filter { $0.recordID == record.id }.sorted { $0.createdAt > $1.createdAt } + if matching.isEmpty { + Text("No verified restoration backup is available for this automation.") + .font(.callout) + .foregroundStyle(.secondary) + } else { + ForEach(matching) { backup in + let restoreAvailable = pendingOperation == nil + && decision?.capabilities.contains(.restore) == true + let restoreHelp = restoreAvailable + ? "Restore the verified backup from \(backup.createdAt.formatted(date: .abbreviated, time: .shortened)) after conflict and integrity checks" + : pendingOperation != nil + ? "Wait for the current automation operation to finish" + : decision?.reason ?? "DevScope cannot verify that this backup may be restored safely" + HStack { + VStack(alignment: .leading, spacing: 2) { + Text(backup.createdAt.formatted(date: .abbreviated, time: .shortened)) + Text("Verified backup · \(backup.checksum.prefix(12))") + .font(.caption.monospaced()) + .foregroundStyle(.secondary) + } + Spacer() + Button("Restore") { perform(.restore(backup.id)) } + .disabled(!restoreAvailable) + .help(restoreHelp) + .accessibilityLabel("Restore backup from \(backup.createdAt.formatted(date: .abbreviated, time: .shortened))") + .accessibilityHint(restoreHelp) + } + } + } + } + } + + @ViewBuilder + private func resultEvidence(_ title: String, values: [String]) -> some View { + if !values.isEmpty { + VStack(alignment: .leading, spacing: 2) { + Text(title) + .font(.caption2.weight(.semibold)) + .foregroundStyle(.secondary) + ForEach(Array(values.enumerated()), id: \.offset) { _, value in + Text(value) + .font(.caption.monospaced()) + .textSelection(.enabled) + .fixedSize(horizontal: false, vertical: true) + } + } + .padding(.top, 2) + } + } + + private func recoveryHandles(from result: AutomationOperationResult) -> [AutomationFileAuthorization] { + var handles: [AutomationFileAuthorization] = [] + for evidence in [result.fileMutationEvidence, result.rollbackFileMutationEvidence].compactMap({ $0 }) { + for handle in evidence.recoveryHandles where !handles.contains(where: { + $0.fileURL.standardizedFileURL == handle.fileURL.standardizedFileURL + }) { + handles.append(handle) + } + } + return handles + } + + private func copyRecoveryPath(_ url: URL) { + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(url.standardizedFileURL.path, forType: .string) + } + + private func inspectorGrid(_ entries: [(String, String)]) -> some View { + LazyVGrid(columns: [GridItem(.adaptive(minimum: 72), spacing: 8)], spacing: 8) { + ForEach(Array(entries.enumerated()), id: \.offset) { _, entry in + VStack(alignment: .leading, spacing: 2) { + Text(entry.0).font(.caption2).foregroundStyle(.secondary) + Text(entry.1) + .font(.caption.weight(.semibold)) + .fixedSize(horizontal: false, vertical: true) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(8) + .background(.quaternary.opacity(0.25), in: RoundedRectangle(cornerRadius: 8)) + } + } + } + + private func field(_ label: String, _ value: String) -> some View { + VStack(alignment: .leading, spacing: 3) { + Text(label).font(.caption2.weight(.semibold)).foregroundStyle(.secondary) + Text(value).font(.callout).textSelection(.enabled).fixedSize(horizontal: false, vertical: true) + } + } +} + +private struct AutomationManagementBar: View { + let record: AutomationRecord + let decision: AutomationCapabilityDecision? + let backups: [AutomationBackup] + let pendingOperation: AutomationPendingOperation? + let operationResult: AutomationOperationResult? + let perform: (AutomationManagementAction) -> Void + let dismissResult: () -> Void + let saveExport: (AutomationExportArtifact) -> Void + let openLoginItemsSettings: () -> Void + + private var resolvedDecision: AutomationCapabilityDecision { + decision ?? AutomationCapabilityDecision( + capabilities: [], + reason: "DevScope has not verified source ownership and path safety yet." + ) + } + + private var actions: [AutomationManagementAction] { + AutomationManagementPresentation.actions( + decision: resolvedDecision, + backups: backups, + record: record + ) + } + + private var pinnedActions: [AutomationManagementAction] { + actions.filter { $0.placement == .pinned } + } + + private var overflowActions: [AutomationManagementAction] { + actions.filter { $0.placement == .overflow } + } + + private var operationIsPending: Bool { + pendingOperation != nil + } + + private var pendingHelp: String { + operationIsPending + ? "Wait for the current automation operation to finish" + : "" + } + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + HStack(spacing: 8) { + Label("Management", systemImage: "slider.horizontal.3") + .font(.subheadline.weight(.semibold)) + .accessibilityAddTraits(.isHeader) + + Spacer(minLength: 8) + + if let pendingOperation, pendingOperation.recordID == record.id { + HStack(spacing: 6) { + ProgressView().controlSize(.small) + Text("\(pendingOperation.operation.displayTitle) in progress") + .font(.caption.weight(.medium)) + .lineLimit(1) + } + .foregroundStyle(.secondary) + } else { + Label(record.state.displayTitle, systemImage: record.state.symbolName) + .font(.caption.weight(.semibold)) + .foregroundStyle(record.state.tint) + .lineLimit(1) + } + } + + if hasVisibleControl { + LazyVGrid( + columns: [GridItem(.adaptive(minimum: 112, maximum: 180), spacing: 8)], + spacing: 8 + ) { + ForEach(pinnedActions, id: \.self) { action in + managementButton(action) + } + + if !overflowActions.isEmpty { + Menu { + ForEach(overflowActions, id: \.self) { action in + let helpText = AutomationManagementPresentation.helpText( + for: action, + record: record + ) + Button(role: action.isDestructive ? .destructive : nil) { + perform(action) + } label: { + Label(action.title, systemImage: action.systemImage) + } + .accessibilityHint(helpText) + .help(helpText) + } + } label: { + Label("More Actions", systemImage: "ellipsis.circle") + .lineLimit(1) + .frame(maxWidth: .infinity) + } + .menuStyle(.button) + .tint(.accentColor) + .controlSize(.small) + .disabled(operationIsPending) + .accessibilityLabel("More automation actions") + .accessibilityHint( + operationIsPending + ? pendingHelp + : "Edit, duplicate, import, export, or remove this automation when available." + ) + .help(operationIsPending ? pendingHelp : "More automation actions") + } + + if record.sourceKind == .serviceManagement { + Button(action: openLoginItemsSettings) { + Label("System Settings", systemImage: "gearshape") + .lineLimit(1) + .frame(maxWidth: .infinity) + } + .buttonStyle(.bordered) + .tint(.accentColor) + .controlSize(.small) + .disabled(operationIsPending) + .accessibilityHint( + "Opens Login Items & Extensions, where macOS manages this background item." + ) + .help("Open Login Items & Extensions in System Settings") + } + } + } + + if let reason = resolvedDecision.reason { + Text(reason) + .font(.caption) + .foregroundStyle(.secondary) + .lineLimit(2) + .fixedSize(horizontal: false, vertical: true) + } else if !hasVisibleControl { + Text("No management operation is available for this source.") + .font(.caption) + .foregroundStyle(.secondary) + } + + if let operationResult { + operationStatus(operationResult) + } + } + .padding(.horizontal, 12) + .padding(.vertical, 10) + .background(.bar) + .accessibilityIdentifier("automation-management-bar") + } + + private var hasVisibleControl: Bool { + !pinnedActions.isEmpty + || !overflowActions.isEmpty + || record.sourceKind == .serviceManagement + } + + private func managementButton(_ action: AutomationManagementAction) -> some View { + let helpText = AutomationManagementPresentation.helpText(for: action, record: record) + return Button(role: action.isDestructive ? .destructive : nil) { + perform(action) + } label: { + Label(action.title, systemImage: action.systemImage) + .lineLimit(1) + .minimumScaleFactor(0.8) + .frame(maxWidth: .infinity) + } + .buttonStyle(AutomationManagementActionButtonStyle(tint: actionTint(action))) + .controlSize(.small) + .disabled(operationIsPending) + .accessibilityHint(operationIsPending ? pendingHelp : helpText) + .help(operationIsPending ? pendingHelp : helpText) + } + + private func actionTint(_ action: AutomationManagementAction) -> Color { + switch action.emphasis { + case .neutral: .accentColor + case .positive: .green + case .caution: .orange + case .destructive: .red + } + } + + private func operationStatus(_ operationResult: AutomationOperationResult) -> some View { + let result = AutomationManagementPresentation.result(operationResult) + return HStack(spacing: 8) { + Label( + result.title, + systemImage: result.isFailure ? "exclamationmark.circle.fill" : "checkmark.circle.fill" + ) + .font(.caption.weight(.semibold)) + .foregroundStyle(result.isFailure ? Color.red : Color.green) + .lineLimit(1) + + Spacer(minLength: 4) + + if let artifact = operationResult.exportArtifact { + Button(artifact.isRedacted ? "Save Redacted" : "Save Unredacted") { + saveExport(artifact) + } + .buttonStyle(.bordered) + .controlSize(.small) + .accessibilityHint( + "Opens a save panel. DevScope does not place export contents in operation status or logs." + ) + .help("Save the completed automation export") + } + + Button(action: dismissResult) { + Image(systemName: "xmark.circle.fill") + } + .buttonStyle(.plain) + .foregroundStyle(.secondary) + .accessibilityLabel("Dismiss operation result") + .help("Dismiss operation result") + } + .padding(.horizontal, 8) + .padding(.vertical, 6) + .background(.quaternary.opacity(0.3), in: RoundedRectangle(cornerRadius: 8)) + } +} + +private struct AutomationManagementActionButtonStyle: ButtonStyle { + @Environment(\.isEnabled) private var isEnabled + + let tint: Color + + func makeBody(configuration: Configuration) -> some View { + configuration.label + .font(.caption.weight(.medium)) + .foregroundStyle(tint) + .padding(.horizontal, 9) + .padding(.vertical, 5) + .background( + tint.opacity(configuration.isPressed ? 0.22 : 0.12), + in: RoundedRectangle(cornerRadius: 6, style: .continuous) + ) + .overlay { + RoundedRectangle(cornerRadius: 6, style: .continuous) + .stroke(tint.opacity(isEnabled ? 0.34 : 0.16), lineWidth: 1) + } + .contentShape(RoundedRectangle(cornerRadius: 6, style: .continuous)) + .opacity(isEnabled ? 1 : 0.55) + } +} + +private struct AutomationInspectorSection: View { + let title: String + let systemImage: String + @ViewBuilder let content: Content + + var body: some View { + VStack(alignment: .leading, spacing: 10) { + Label(title, systemImage: systemImage).font(.subheadline.weight(.semibold)) + content + } + .frame(maxWidth: .infinity, alignment: .leading) + } +} diff --git a/Sources/DevScope/Views/AutomationEditorView.swift b/Sources/DevScope/Views/AutomationEditorView.swift new file mode 100644 index 0000000..8521db6 --- /dev/null +++ b/Sources/DevScope/Views/AutomationEditorView.swift @@ -0,0 +1,202 @@ +import DevScopeCore +import Foundation +import SwiftUI + +struct AutomationEditorView: View { + enum Purpose { + case edit + case duplicate + } + + @Environment(\.dismiss) private var dismiss + let record: AutomationRecord + let purpose: Purpose + let duplicateDestination: (String) -> URL? + let apply: (AutomationEditPayload) -> Void + + @State private var label: String + @State private var executable: String + @State private var argumentsText: String + @State private var environmentText: String + @State private var scheduleText: String + @State private var workingDirectory: String + @State private var rawText = "" + @State private var usesRawRepresentation = false + + init( + record: AutomationRecord, + purpose: Purpose, + duplicateDestination: @escaping (String) -> URL?, + apply: @escaping (AutomationEditPayload) -> Void + ) { + self.record = record + self.purpose = purpose + self.duplicateDestination = duplicateDestination + self.apply = apply + _label = State(initialValue: purpose == .duplicate ? "\(record.label).copy" : record.label) + _executable = State(initialValue: record.executable ?? "") + _argumentsText = State(initialValue: record.arguments.joined(separator: "\n")) + _environmentText = State(initialValue: AutomationEditorPresentation.environmentText(for: record.environment)) + _scheduleText = State(initialValue: AutomationEditorPresentation.scheduleText(for: record.schedule)) + _workingDirectory = State(initialValue: record.workingDirectory ?? "") + } + + var body: some View { + VStack(alignment: .leading, spacing: 16) { + HStack { + VStack(alignment: .leading, spacing: 3) { + Text(purpose == .edit ? "Edit Automation" : "Duplicate Automation") + .font(.title2.weight(.semibold)) + Text("Changes are validated again by the transaction manager before any source is written.") + .font(.caption) + .foregroundStyle(.secondary) + } + Spacer() + Toggle("Advanced Raw", isOn: $usesRawRepresentation) + .toggleStyle(.switch) + } + + if usesRawRepresentation { + HSplitView { + reviewedFields + .frame(minWidth: 360, idealWidth: 430) + VStack(alignment: .leading, spacing: 8) { + Text("Complete Raw Source") + .font(.headline) + Text("Paste the complete property list or current-user crontab. The reviewed fields must describe the same command, environment, schedule, and working directory.") + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + TextEditor(text: $rawText) + .font(.system(.callout, design: .monospaced)) + .frame(minWidth: 360, minHeight: 360) + .overlay { + RoundedRectangle(cornerRadius: 8).stroke(.quaternary) + } + .accessibilityLabel("Complete raw automation source") + } + .padding(12) + } + } else { + reviewedFields + } + + if let validationMessage { + Label(validationMessage, systemImage: "exclamationmark.circle") + .font(.caption) + .foregroundStyle(.orange) + } + + HStack { + Spacer() + Button("Cancel") { dismiss() } + .keyboardShortcut(.cancelAction) + Button(purpose == .edit ? "Apply" : "Create Copy") { + guard let payload else { return } + apply(payload) + dismiss() + } + .buttonStyle(.borderedProminent) + .disabled(payload == nil) + .keyboardShortcut(.defaultAction) + } + } + .padding(20) + .frame(minWidth: usesRawRepresentation ? 900 : 680, minHeight: 620) + } + + private var reviewedFields: some View { + Form { + TextField("Label", text: $label) + TextField("Executable", text: $executable) + TextField("Working Directory", text: $workingDirectory) + editorField("Arguments (one per line)", text: $argumentsText, minHeight: 80) + editorField("Environment (one KEY=VALUE per line)", text: $environmentText, minHeight: 80) + editorField( + "Schedule (run-at-load, keep-alive, interval SECONDS, calendar DESCRIPTION, cron EXPRESSION, or on-demand)", + text: $scheduleText, + minHeight: 80 + ) + } + .formStyle(.grouped) + } + + private func editorField( + _ title: String, + text: Binding, + minHeight: CGFloat + ) -> some View { + VStack(alignment: .leading, spacing: 5) { + Text(title) + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + TextEditor(text: text) + .font(.system(.callout, design: .monospaced)) + .frame(minHeight: minHeight) + } + } + + private var payload: AutomationEditPayload? { + let trimmedLabel = label.trimmingCharacters(in: .whitespacesAndNewlines) + let trimmedExecutable = executable.trimmingCharacters(in: .whitespacesAndNewlines) + guard validationMessage == nil, + let environment, + let schedule else { return nil } + return AutomationEditPayload( + label: trimmedLabel, + executable: trimmedExecutable, + arguments: argumentsText.split(whereSeparator: \.isNewline).map(String.init), + environment: environment, + workingDirectory: workingDirectory.trimmingCharacters(in: .whitespacesAndNewlines).nilIfEmpty, + schedule: schedule, + rawRepresentation: rawData, + destination: resolvedDestination, + expectedDestinationChecksum: purpose == .duplicate ? nil : record.sourceChecksum + ) + } + + private var validationMessage: String? { + guard let environment else { + return "Environment entries must use unique non-empty KEY=VALUE lines." + } + guard let schedule else { + return "Enter at least one valid schedule line using the documented schedule syntax." + } + return AutomationEditorPresentation.validationMessage( + record: record, + purposeIsDuplicate: purpose == .duplicate, + label: label.trimmingCharacters(in: .whitespacesAndNewlines), + executable: executable.trimmingCharacters(in: .whitespacesAndNewlines), + arguments: argumentsText.split(whereSeparator: \.isNewline).map(String.init), + environment: environment, + schedule: schedule, + usesRawRepresentation: usesRawRepresentation, + rawData: rawData, + duplicateDestination: resolvedDestination, + workingDirectory: workingDirectory.trimmingCharacters(in: .whitespacesAndNewlines).nilIfEmpty + ) + } + + private var environment: [String: String]? { + AutomationEditorPresentation.environment(from: environmentText) + } + + private var schedule: AutomationSchedule? { + AutomationEditorPresentation.schedule(from: scheduleText) + } + + private var rawData: Data? { + usesRawRepresentation ? Data(rawText.utf8) : nil + } + + private var resolvedDestination: URL? { + purpose == .duplicate + ? duplicateDestination(label.trimmingCharacters(in: .whitespacesAndNewlines)) + : record.sourceURL + } +} + +private extension String { + var nilIfEmpty: String? { isEmpty ? nil : self } +} diff --git a/Sources/DevScope/Views/AutomationIdentityView.swift b/Sources/DevScope/Views/AutomationIdentityView.swift new file mode 100644 index 0000000..873018c --- /dev/null +++ b/Sources/DevScope/Views/AutomationIdentityView.swift @@ -0,0 +1,62 @@ +import DevScopeCore +import SwiftUI + +struct AutomationIdentityView: View { + let record: AutomationRecord + let store: AutomationApplicationIconStore + let size: CGFloat + @State private var resolvedIcon: ResolvedIcon? + + var body: some View { + Group { + if let icon = displayedIcon { + Image(nsImage: icon) + .resizable() + .scaledToFit() + } else { + let fallback = AutomationVisualIdentity.fallback(for: record.kind) + ZStack { + RoundedRectangle(cornerRadius: size * 0.28, style: .continuous) + .fill(fallback.color.opacity(0.16)) + Image(systemName: fallback.symbolName) + .font(.system(size: size * 0.46, weight: .semibold)) + .foregroundStyle(fallback.color) + } + } + } + .frame(width: size, height: size) + .accessibilityElement(children: .ignore) + .accessibilityLabel(accessibilityLabel) + .task(id: cacheKey) { + let requestedKey = cacheKey + guard let icon = await store.resolve(record), !Task.isCancelled else { + return + } + resolvedIcon = ResolvedIcon(key: requestedKey, image: icon) + } + } + + private var accessibilityLabel: String { + if displayedIcon != nil { + return record.providerBundleIdentifier.map { "Owning application \($0)" } + ?? "Application icon for \(record.displayName)" + } + return AutomationVisualIdentity.fallback(for: record.kind).accessibilityTitle + } + + private var cacheKey: AutomationApplicationIconKey { + store.key(for: record) + } + + private var displayedIcon: NSImage? { + if resolvedIcon?.key == cacheKey { + return resolvedIcon?.image + } + return store.icon(for: record) + } +} + +private struct ResolvedIcon { + let key: AutomationApplicationIconKey + let image: NSImage +} diff --git a/Sources/DevScope/Views/AutomationRailView.swift b/Sources/DevScope/Views/AutomationRailView.swift new file mode 100644 index 0000000..bcb4451 --- /dev/null +++ b/Sources/DevScope/Views/AutomationRailView.swift @@ -0,0 +1,155 @@ +import DevScopeCore +import SwiftUI + +struct AutomationRailView: View { + @Binding var source: AutomationSourceFilter + @Binding var state: AutomationState? + @Binding var ownership: AutomationOwnershipFilter + let counts: [String: Int] + + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 18) { + filterSection("Sources") { + filterButton( + "All", + symbol: "tray.full", + tint: DevScopePalette.accent, + key: "source.all", + selected: source == .all + ) { + source = .all + } + filterButton( + "LaunchAgents", + symbol: "bolt.horizontal", + tint: AutomationVisualIdentity.fallback(for: .launchAgent).color, + key: "source.launchd", + selected: source == .launchd + ) { + source = .launchd + } + filterButton( + "Login Items", + symbol: "person.crop.circle.badge.clock", + tint: AutomationVisualIdentity.fallback(for: .loginItem).color, + key: "source.login", + selected: source == .loginItems + ) { + source = .loginItems + } + filterButton( + "Scheduled", + symbol: "calendar.badge.clock", + tint: AutomationVisualIdentity.fallback(for: .cron).color, + key: "source.scheduled", + selected: source == .scheduled + ) { + source = .scheduled + } + } + + filterSection("State") { + filterButton( + "All", + symbol: "circle.grid.2x2", + tint: DevScopePalette.accent, + key: "state.all", + selected: state == nil + ) { state = nil } + ForEach(AutomationState.allCases, id: \.self) { value in + filterButton( + value.displayTitle, + symbol: value.symbolName, + tint: AutomationVisualIdentity.state(for: value).color, + key: "state.\(value.rawValue)", + selected: state == value + ) { + state = value + } + } + } + + filterSection("Ownership") { + filterButton( + "All", + symbol: "person.2", + tint: DevScopePalette.accent, + key: "owner.all", + selected: ownership == .all + ) { ownership = .all } + filterButton( + "User", + symbol: "person.crop.circle", + tint: .blue, + key: "owner.user", + selected: ownership == .user + ) { ownership = .user } + filterButton( + "Third Party", + symbol: "shippingbox", + tint: .purple, + key: "owner.third", + selected: ownership == .thirdParty + ) { ownership = .thirdParty } + filterButton( + "Apple System", + symbol: "apple.logo", + tint: .secondary, + key: "owner.apple", + selected: ownership == .appleSystem + ) { ownership = .appleSystem } + } + } + .padding(12) + } + .background(.ultraThinMaterial) + .accessibilityLabel("Automation filters") + } + + private func filterSection( + _ title: String, + @ViewBuilder content: () -> Content + ) -> some View { + VStack(alignment: .leading, spacing: 6) { + Text(title.uppercased()) + .font(.caption2.weight(.semibold)) + .foregroundStyle(.secondary) + .padding(.horizontal, 8) + content() + } + } + + private func filterButton( + _ title: String, + symbol: String, + tint: Color, + key: String, + selected: Bool, + action: @escaping () -> Void + ) -> some View { + Button(action: action) { + HStack(spacing: 8) { + Image(systemName: symbol) + .frame(width: 18) + .foregroundStyle(tint) + Text(title) + .lineLimit(1) + Spacer(minLength: 4) + if let count = counts[key] { + Text("\(count)") + .font(.caption.monospacedDigit()) + .foregroundStyle(.secondary) + } + } + .padding(.horizontal, 8) + .padding(.vertical, 6) + .background(selected ? DevScopePalette.accent.opacity(0.16) : .clear, in: RoundedRectangle(cornerRadius: 8)) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .accessibilityAddTraits(selected ? .isSelected : []) + .accessibilityHint("Filters the automation inventory by \(title).") + .help("Filter automations by \(title)") + } +} diff --git a/Sources/DevScope/Views/AutomationTableView.swift b/Sources/DevScope/Views/AutomationTableView.swift new file mode 100644 index 0000000..81ec656 --- /dev/null +++ b/Sources/DevScope/Views/AutomationTableView.swift @@ -0,0 +1,234 @@ +import DevScopeCore +import SwiftUI + +struct AutomationTableRow: Identifiable, Equatable, Sendable { + let record: AutomationRecord + let linkedProcessCount: Int + + var id: AutomationRecord.ID { record.id } +} + +enum AutomationTableMetrics { + static let automationColumnWidth: CGFloat = 220 + static let kindColumnWidth: CGFloat = 100 + static let triggerColumnWidth: CGFloat = 145 + static let stateColumnWidth: CGFloat = 90 + static let ownerColumnWidth: CGFloat = 90 + static let runsColumnWidth: CGFloat = 55 + static let horizontalPadding: CGFloat = 12 + static let contentWidth = automationColumnWidth + + kindColumnWidth + + triggerColumnWidth + + stateColumnWidth + + ownerColumnWidth + + runsColumnWidth + + (horizontalPadding * 2) +} + +struct AutomationTableView: View { + let rows: [AutomationTableRow] + let sourceHealthHasFailure: Bool + @Binding var selection: AutomationRecord.ID? + @Binding var sort: AutomationTableSortState + let iconStore: AutomationApplicationIconStore + @Environment(\.controlActiveState) private var controlActiveState + @FocusState private var isFocused: Bool + @State private var hoveredID: AutomationRecord.ID? + @State private var verticalScrollTargetID: AutomationRecord.ID? + + var body: some View { + ZStack { + ScrollView(.horizontal) { + VStack(spacing: 0) { + header + + ScrollView(.vertical) { + LazyVStack(spacing: 0) { + ForEach(Array(rows.enumerated()), id: \.element.id) { index, row in + rowButton(row, index: index) + .id(row.id) + } + } + .scrollTargetLayout() + } + .scrollPosition(id: $verticalScrollTargetID, anchor: .center) + } + .frame( + minWidth: AutomationTableMetrics.contentWidth, + maxWidth: .infinity, + alignment: .topLeading + ) + } + + if rows.isEmpty { + ContentUnavailableView( + sourceHealthHasFailure ? "Automation sources unavailable" : "No matching automations", + systemImage: sourceHealthHasFailure ? "exclamationmark.triangle" : "gearshape.2", + description: Text(sourceHealthHasFailure + ? "Review the source-specific status above. Healthy sources remain available." + : "Adjust search or filters. Idle definitions remain visible in this workspace.") + ) + } + } + .focusable() + .focused($isFocused) + .focusEffectDisabled() + .onKeyPress(.upArrow) { + move(.previous) + return .handled + } + .onKeyPress(.downArrow) { + move(.next) + return .handled + } + } + + private var header: some View { + HStack(spacing: 0) { + sortableHeader("Automation", column: .automation) + .frame(width: AutomationTableMetrics.automationColumnWidth, alignment: .leading) + sortableHeader("Kind", column: .kind) + .frame(width: AutomationTableMetrics.kindColumnWidth, alignment: .leading) + sortableHeader("Trigger", column: .trigger) + .frame(width: AutomationTableMetrics.triggerColumnWidth, alignment: .leading) + sortableHeader("State", column: .state) + .frame(width: AutomationTableMetrics.stateColumnWidth, alignment: .leading) + sortableHeader("Owner", column: .owner) + .frame(width: AutomationTableMetrics.ownerColumnWidth, alignment: .leading) + sortableHeader("Runs", column: .runs, contentAlignment: .trailing) + .frame(width: AutomationTableMetrics.runsColumnWidth, alignment: .trailing) + } + .padding(.horizontal, AutomationTableMetrics.horizontalPadding) + .padding(.vertical, 8) + .background(.ultraThinMaterial) + .overlay(alignment: .bottom) { Divider() } + } + + private func rowButton(_ row: AutomationTableRow, index: Int) -> some View { + Button { + selection = row.id + isFocused = true + } label: { + HStack(spacing: 0) { + AutomationCell(record: row.record, iconStore: iconStore) + .frame(width: AutomationTableMetrics.automationColumnWidth, alignment: .leading) + Text(row.record.kind.displayTitle) + .foregroundStyle(DevScopePalette.color(for: row.record.kind)) + .frame(width: AutomationTableMetrics.kindColumnWidth, alignment: .leading) + Text(row.record.schedule.summary) + .frame(width: AutomationTableMetrics.triggerColumnWidth, alignment: .leading) + AutomationStateLabelView(state: row.record.state) + .frame(width: AutomationTableMetrics.stateColumnWidth, alignment: .leading) + Text(row.record.ownership.displayTitle) + .frame(width: AutomationTableMetrics.ownerColumnWidth, alignment: .leading) + Text("\(row.linkedProcessCount)") + .monospacedDigit() + .frame(width: AutomationTableMetrics.runsColumnWidth, alignment: .trailing) + } + .font(.callout) + .padding(.horizontal, AutomationTableMetrics.horizontalPadding) + .padding(.vertical, 4) + .contentShape(Rectangle()) + .background(background(row.id, index: index)) + .overlay(alignment: .leading) { + if selection == row.id { + Rectangle().fill(DevScopePalette.accent).frame(width: 3) + } + } + .foregroundStyle(.primary) + } + .buttonStyle(.plain) + .onHover { hovering in + hoveredID = hovering ? row.id : (hoveredID == row.id ? nil : hoveredID) + } + .accessibilityElement(children: .combine) + .accessibilityLabel("\(row.record.displayName), \(row.record.kind.displayTitle), \(row.record.state.displayTitle)") + .accessibilityValue("\(row.linkedProcessCount) linked process\(row.linkedProcessCount == 1 ? "" : "es")") + .accessibilityAddTraits(selection == row.id ? .isSelected : []) + } + + private func background(_ id: AutomationRecord.ID, index: Int) -> Color { + if selection == id { + return DevScopePalette.accent.opacity(controlActiveState == .inactive ? 0.10 : 0.20) + } + if hoveredID == id { return Color.primary.opacity(0.055) } + return index.isMultiple(of: 2) ? Color.white.opacity(0.025) : .clear + } + + private func sortableHeader( + _ title: String, + column: AutomationTableSortColumn, + contentAlignment: Alignment = .leading + ) -> some View { + SortableTableHeader( + title: title, + column: column, + activeColumn: sort.column, + direction: sort.direction, + contentAlignment: contentAlignment, + activate: { sort.activate($0) } + ) + } + + private func move(_ direction: InterfaceSelectionDirection) { + let ids = rows.map(\.id) + guard !ids.isEmpty else { selection = nil; return } + let index = selection.flatMap(ids.firstIndex(of:)) + let nextIndex: Int + switch (direction, index) { + case (.previous, nil): nextIndex = ids.count - 1 + case (.next, nil): nextIndex = 0 + case (.previous, let index?): nextIndex = max(0, index - 1) + case (.next, let index?): nextIndex = min(ids.count - 1, index + 1) + } + let next = ids[nextIndex] + selection = next + verticalScrollTargetID = next + } +} + +private struct AutomationCell: View { + let record: AutomationRecord + let iconStore: AutomationApplicationIconStore + + var body: some View { + HStack(spacing: 10) { + AutomationIdentityView(record: record, store: iconStore, size: 28) + + VStack(alignment: .leading, spacing: 2) { + Text(record.displayName) + .font(.callout.weight(.semibold)) + .lineLimit(1) + .truncationMode(.tail) + Text(contextLabel) + .font(.caption) + .foregroundStyle(.secondary) + .lineLimit(1) + .truncationMode(.middle) + } + } + .padding(.vertical, 5) + } + + private var contextLabel: String { + guard let providerBundleIdentifier = record.providerBundleIdentifier else { + return record.label + } + return "\(record.label) · \(providerBundleIdentifier)" + } +} + +private struct AutomationStateLabelView: View { + let state: AutomationState + + var body: some View { + let presentation = AutomationStateLabelPresentation(state: state) + HStack(spacing: 4) { + Image(systemName: presentation.symbolName) + Text(presentation.title) + } + .foregroundStyle(presentation.colorToken.color) + .accessibilityElement(children: .ignore) + .accessibilityLabel(presentation.title) + } +} diff --git a/Sources/DevScope/Views/AutomationVisualIdentity.swift b/Sources/DevScope/Views/AutomationVisualIdentity.swift new file mode 100644 index 0000000..8a539c4 --- /dev/null +++ b/Sources/DevScope/Views/AutomationVisualIdentity.swift @@ -0,0 +1,108 @@ +import DevScopeCore + +enum AutomationColorToken: Equatable, Sendable { + case accent + case blue + case indigo + case cyan + case purple + case green + case orange + case red + case secondary +} + +struct AutomationVisualIdentity: Equatable, Sendable { + let symbolName: String + let colorToken: AutomationColorToken + let accessibilityTitle: String + + static func fallback(for kind: AutomationKind) -> Self { + switch kind { + case .launchAgent: + Self( + symbolName: "bolt.horizontal", + colorToken: .blue, + accessibilityTitle: "LaunchAgent" + ) + case .launchDaemon: + Self( + symbolName: "bolt.horizontal.circle", + colorToken: .indigo, + accessibilityTitle: "LaunchDaemon" + ) + case .loginItem: + Self( + symbolName: "person.crop.circle.badge.clock", + colorToken: .cyan, + accessibilityTitle: "Login item" + ) + case .backgroundItem: + Self( + symbolName: "person.crop.circle.badge.clock", + colorToken: .cyan, + accessibilityTitle: "Background item" + ) + case .cron: + Self( + symbolName: "calendar.badge.clock", + colorToken: .purple, + accessibilityTitle: "Scheduled automation" + ) + } + } + + static func state(for state: AutomationState) -> Self { + switch state { + case .running: + Self( + symbolName: "play.circle.fill", + colorToken: .green, + accessibilityTitle: "Running" + ) + case .idle: + Self( + symbolName: "pause.circle", + colorToken: .secondary, + accessibilityTitle: "Idle" + ) + case .disabled: + Self( + symbolName: "nosign", + colorToken: .secondary, + accessibilityTitle: "Disabled" + ) + case .needsApproval: + Self( + symbolName: "person.badge.clock", + colorToken: .orange, + accessibilityTitle: "Needs Approval" + ) + case .invalid: + Self( + symbolName: "exclamationmark.triangle", + colorToken: .red, + accessibilityTitle: "Invalid" + ) + case .unresolved: + Self( + symbolName: "questionmark.circle", + colorToken: .orange, + accessibilityTitle: "Unresolved" + ) + } + } +} + +struct AutomationStateLabelPresentation: Equatable, Sendable { + let title: String + let symbolName: String + let colorToken: AutomationColorToken + + init(state: AutomationState) { + let identity = AutomationVisualIdentity.state(for: state) + title = identity.accessibilityTitle + symbolName = identity.symbolName + colorToken = identity.colorToken + } +} diff --git a/Sources/DevScope/Views/AutomationWorkspaceLayoutPolicy.swift b/Sources/DevScope/Views/AutomationWorkspaceLayoutPolicy.swift new file mode 100644 index 0000000..d41f3a0 --- /dev/null +++ b/Sources/DevScope/Views/AutomationWorkspaceLayoutPolicy.swift @@ -0,0 +1,50 @@ +import CoreGraphics + +struct AutomationWorkspacePaneConstraints: Equatable, Sendable { + let railMinimum: CGFloat + let railPreferred: CGFloat + let tableMinimum: CGFloat + let tablePreferred: CGFloat + let detailMinimum: CGFloat + let detailPreferred: CGFloat + let tablePriority: Double + let detailPriority: Double + + var minimumTotal: CGFloat { + railMinimum + tableMinimum + detailMinimum + } +} + +enum AutomationWorkspaceLayoutPolicy { + static func constraints(availableWidth: CGFloat) -> AutomationWorkspacePaneConstraints { + let usableWidth = max(0, availableWidth - 18) + let minimumScale = min(1, usableWidth / 780) + let railMinimum = 150 * minimumScale + let tableMinimum = 360 * minimumScale + let detailMinimum = 270 * minimumScale + + let railPreferred: CGFloat + let tablePreferred: CGFloat + let detailPreferred: CGFloat + if usableWidth < 810 { + railPreferred = railMinimum + tablePreferred = tableMinimum + detailPreferred = detailMinimum + } else { + railPreferred = min(280, max(170, usableWidth * 0.18)) + detailPreferred = min(440, max(280, usableWidth * 0.30)) + tablePreferred = usableWidth - railPreferred - detailPreferred + } + + return AutomationWorkspacePaneConstraints( + railMinimum: railMinimum, + railPreferred: railPreferred, + tableMinimum: tableMinimum, + tablePreferred: tablePreferred, + detailMinimum: detailMinimum, + detailPreferred: detailPreferred, + tablePriority: 1, + detailPriority: 1 + ) + } +} diff --git a/Sources/DevScope/Views/AutomationWorkspaceView.swift b/Sources/DevScope/Views/AutomationWorkspaceView.swift new file mode 100644 index 0000000..9fc7df2 --- /dev/null +++ b/Sources/DevScope/Views/AutomationWorkspaceView.swift @@ -0,0 +1,461 @@ +import AppKit +import DevScopeCore +import ServiceManagement +import SwiftUI + +struct AutomationWorkspaceView: View { + @ObservedObject var store: AutomationStore + let inventory: AutomationInventorySnapshot + let processes: [ClassifiedDevProcess] + let linksByProcessID: [Int32: [AutomationProcessLink]] + @Binding var searchText: String + @Binding var sourceFilter: AutomationSourceFilter + @Binding var stateFilter: AutomationState? + @Binding var ownershipFilter: AutomationOwnershipFilter + let includeAppleSystemServices: Bool + let documentPanel: AutomationDocumentPanel + + @State private var pendingConfirmation: PendingAutomationConfirmation? + @State private var pendingEditor: PendingAutomationEditor? + @State private var pendingImport: PendingAutomationImport? + @State private var documentError: String? + @State private var automationSort = AutomationTableSortState() + @StateObject private var applicationIconStore = AutomationApplicationIconStore() + + private var filteredRecords: [AutomationRecord] { + AutomationPresentation.filtered( + inventory.records, + source: sourceFilter, + state: stateFilter, + ownership: ownershipFilter, + searchText: searchText, + includeAppleSystemServices: includeAppleSystemServices + ) + } + + private var eligibleRecords: [AutomationRecord] { + inventory.records.filter { includeAppleSystemServices || $0.ownership != .appleSystem } + } + + private var inventoryCount: AutomationInventoryCountPresentation { + AutomationPresentation.inventoryCount( + visibleCount: filteredRecords.count, + eligibleCount: eligibleRecords.count, + totalCount: inventory.records.count + ) + } + + private var selectedRecord: AutomationRecord? { + guard let id = store.selectedRecordID else { return nil } + return inventory.records.first { $0.id == id } + } + + private var tableRows: [AutomationTableRow] { + let linkCounts = Dictionary( + grouping: linksByProcessID.values.flatMap { $0 }, + by: \.recordID + ).mapValues(\.count) + return automationSort.sorted(filteredRecords.map { + AutomationTableRow(record: $0, linkedProcessCount: linkCounts[$0.id, default: 0]) + }) + } + + var body: some View { + VStack(spacing: 10) { + controlStrip + healthBar + GeometryReader { proxy in + let pane = AutomationWorkspaceLayoutPolicy.constraints(availableWidth: proxy.size.width) + + HSplitView { + AutomationRailView( + source: $sourceFilter, + state: $stateFilter, + ownership: $ownershipFilter, + counts: railCounts + ) + .frame(minWidth: pane.railMinimum, idealWidth: pane.railPreferred) + + AutomationTableView( + rows: tableRows, + sourceHealthHasFailure: hasSourceFailure, + selection: $store.selectedRecordID, + sort: $automationSort, + iconStore: applicationIconStore + ) + .frame(minWidth: pane.tableMinimum, idealWidth: pane.tablePreferred) + .layoutPriority(pane.tablePriority) + .transaction { $0.animation = nil } + + AutomationDetailView( + record: selectedRecord, + iconStore: applicationIconStore, + decision: selectedRecord.flatMap { store.capabilitySnapshot.decisionsByRecordID[$0.id] }, + linkedProcesses: selectedRecord.map(linkedProcesses(for:)) ?? [], + sourceHealth: selectedRecord.flatMap { inventory.health[$0.sourceKind] }, + backups: store.backups, + pendingOperation: store.pendingOperation, + operationResult: store.operationResultRecordID == selectedRecord?.id ? store.operationResult : nil, + perform: begin, + dismissResult: store.dismissOperationResult, + saveExport: saveExport, + openLoginItemsSettings: openLoginItemsSettings + ) + .frame(minWidth: pane.detailMinimum, idealWidth: pane.detailPreferred) + .layoutPriority(pane.detailPriority) + } + .softPanel(cornerRadius: 18) + } + } + .sheet(item: $pendingEditor) { pending in + AutomationEditorView( + record: pending.record, + purpose: pending.purpose, + duplicateDestination: { store.duplicateDestination(for: pending.record, label: $0) } + ) { payload in + let operation: AutomationOperation = pending.purpose == .edit ? .edit(payload) : .duplicate(payload) + store.perform(operation, record: pending.record, expectedChecksum: pending.record.sourceChecksum, linkedProcesses: linkedProcesses(for: pending.record)) + } + } + .sheet(item: $pendingConfirmation) { pending in + if let policy = AutomationManagementPresentation.confirmation(for: pending.action, record: pending.record, backups: pending.backups) { + AutomationConfirmationView(action: pending.action, record: pending.record, policy: policy) { + performConfirmed(pending.action, record: pending.record) + } + } + } + .sheet(item: $pendingImport) { pending in + AutomationImportPreviewView(pending: pending) { + store.perform( + .importRecord(AutomationImportPayload( + destination: pending.preview.destination, + data: pending.document.data, + expectedKind: pending.preview.expectedKind, + expectedDestinationChecksum: pending.record.sourceChecksum + )), + record: pending.record, + expectedChecksum: pending.record.sourceChecksum, + linkedProcesses: linkedProcesses(for: pending.record) + ) + } + } + .alert("Document operation failed", isPresented: Binding( + get: { documentError != nil }, + set: { if !$0 { documentError = nil } } + )) { + Button("OK", role: .cancel) { documentError = nil } + } message: { + Text(documentError ?? "The document could not be opened or saved.") + } + .onChange(of: filteredRecords.map(\.id)) { _, ids in + store.selectedRecordID = AutomationPresentation.resolvedSelection( + current: store.selectedRecordID, + visibleIDs: ids + ) + } + } + + private var controlStrip: some View { + HStack(spacing: 10) { + Image(systemName: "magnifyingglass").foregroundStyle(.tertiary) + TextField("Search label, command, path, schedule, or owning app", text: $searchText) + .textFieldStyle(.plain) + if !searchText.isEmpty { + Button { searchText = "" } label: { Image(systemName: "xmark.circle.fill") } + .buttonStyle(.plain) + .accessibilityLabel("Clear automation search") + .help("Clear automation search") + } + Spacer() + VStack(alignment: .trailing, spacing: 1) { + Text(inventoryCount.primaryText) + .font(.callout.monospacedDigit()) + if let contextText = inventoryCount.contextText { + Text(contextText) + .font(.caption2.monospacedDigit()) + } + } + .foregroundStyle(.secondary) + .accessibilityElement(children: .ignore) + .accessibilityLabel(inventoryCount.accessibilityLabel) + .help(inventoryCount.accessibilityLabel) + if store.isRefreshing { + ProgressView() + .controlSize(.small) + .accessibilityLabel("Refreshing automation inventory") + } + } + .padding(.horizontal, 12) + .padding(.vertical, 9) + .background(.bar, in: RoundedRectangle(cornerRadius: 12)) + } + + @ViewBuilder private var healthBar: some View { + let unhealthy = inventory.health.values.filter { $0.state != .healthy }.sorted { $0.kind.rawValue < $1.kind.rawValue } + if !unhealthy.isEmpty { + VStack(alignment: .leading, spacing: 4) { + ForEach(unhealthy, id: \.kind) { health in + Label("\(health.kind.displayTitle): \(health.message ?? health.state.displayTitle)", systemImage: "exclamationmark.triangle") + .font(.caption) + .foregroundStyle(health.state == .permissionRequired ? .orange : .secondary) + } + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + .frame(maxWidth: .infinity, alignment: .leading) + .background(.orange.opacity(0.08), in: RoundedRectangle(cornerRadius: 10)) + } + } + + private var hasSourceFailure: Bool { + inventory.health.values.contains { $0.state != .healthy } + } + + private var railCounts: [String: Int] { + let records = eligibleRecords + var counts = ["source.all": records.count, "state.all": records.count, "owner.all": records.count] + counts["source.launchd"] = records.filter { $0.sourceKind == .launchAgent || $0.sourceKind == .launchDaemon }.count + counts["source.login"] = records.filter { $0.sourceKind == .serviceManagement || $0.sourceKind == .legacyLoginItem }.count + counts["source.scheduled"] = records.filter { $0.sourceKind == .crontab }.count + for state in AutomationState.allCases { counts["state.\(state.rawValue)"] = records.filter { $0.state == state }.count } + counts["owner.user"] = records.filter { $0.ownership == .user }.count + counts["owner.third"] = records.filter { $0.ownership == .thirdPartySystem || $0.ownership == .managed }.count + counts["owner.apple"] = records.filter { $0.ownership == .appleSystem }.count + return counts + } + + private func linkedProcesses(for record: AutomationRecord) -> [ClassifiedDevProcess] { + let identities = AutomationPresentation.uniquelyStrongLinkedProcessIdentities( + for: record.id, + linksByProcessID: linksByProcessID + ) + return processes.filter { identities.contains(ProcessIdentity(process: $0.process)) } + } + + private func begin(_ action: AutomationManagementAction) { + guard let record = selectedRecord else { return } + switch action { + case .edit: pendingEditor = PendingAutomationEditor(record: record, purpose: .edit) + case .duplicate: pendingEditor = PendingAutomationEditor(record: record, purpose: .duplicate) + case .importRecord: + beginImport(record) + default: + if AutomationManagementPresentation.confirmation(for: action, record: record, backups: store.backups) != nil { + pendingConfirmation = PendingAutomationConfirmation( + record: record, + action: action, + backups: store.backups.filter { $0.recordID == record.id } + ) + } else { + performConfirmed(action, record: record) + } + } + } + + private func performConfirmed(_ action: AutomationManagementAction, record: AutomationRecord) { + guard let operation = AutomationManagementPresentation.confirmedOperation(for: action, record: record) else { return } + store.perform(operation, record: record, expectedChecksum: record.sourceChecksum, linkedProcesses: linkedProcesses(for: record)) + } + + private func openLoginItemsSettings() { + if #available(macOS 13.0, *) { + SMAppService.openSystemSettingsLoginItems() + } else if let url = URL(string: "x-apple.systempreferences:com.apple.LoginItems-Settings.extension") { + NSWorkspace.shared.open(url) + } + } + + private func beginImport(_ record: AutomationRecord) { + do { + guard let document = try documentPanel.openImport() else { return } + guard let destination = store.importDestination( + for: record, + suggestedFilename: document.sourceURL.lastPathComponent + ) else { + documentError = "DevScope could not resolve an approved import destination for this automation source." + return + } + pendingImport = PendingAutomationImport( + document: document, + record: record, + preview: AutomationImportPresentation( + data: document.data, + expectedRecord: record, + destination: destination + ) + ) + } catch { + documentError = ProcessPresentation.redactedCommand(error.localizedDescription) + } + } + + private func saveExport(_ artifact: AutomationExportArtifact) { + do { + _ = try documentPanel.save(artifact) + } catch { + documentError = "The export could not be saved. \(ProcessPresentation.redactedCommand(error.localizedDescription))" + } + } + +} + +private struct PendingAutomationConfirmation: Identifiable { + let record: AutomationRecord + let action: AutomationManagementAction + let backups: [AutomationBackup] + var id: String { "\(record.id.rawValue)-\(action.id)" } +} + +private struct PendingAutomationEditor: Identifiable { + let record: AutomationRecord + let purpose: AutomationEditorView.Purpose + var id: String { "\(record.id.rawValue)-\(purpose.id)" } +} + +private struct PendingAutomationImport: Identifiable { + let document: AutomationImportedDocument + let record: AutomationRecord + let preview: AutomationImportPresentation + var id: UUID { document.id } +} + +private struct AutomationImportPreviewView: View { + @Environment(\.dismiss) private var dismiss + let pending: PendingAutomationImport + let apply: () -> Void + + var body: some View { + VStack(alignment: .leading, spacing: 16) { + Text("Preview Import").font(.title2.weight(.semibold)) + LabeledContent("File", value: pending.document.sourceURL.lastPathComponent) + LabeledContent("Expected Kind", value: pending.preview.expectedKind.displayTitle) + LabeledContent("Detected Content", value: pending.preview.summary) + LabeledContent("Replaces", value: pending.preview.targetLabel) + LabeledContent("Target Ownership", value: pending.preview.targetOwnership) + VStack(alignment: .leading, spacing: 4) { + Text("Approved Destination").font(.caption.weight(.semibold)).foregroundStyle(.secondary) + Text(pending.preview.destination.path) + .font(.callout.monospaced()) + .textSelection(.enabled) + } + if let message = pending.preview.validationMessage { + Label(message, systemImage: "exclamationmark.triangle") + .foregroundStyle(.orange) + } else { + Label(pending.preview.consequence, systemImage: "exclamationmark.triangle") + .font(.caption) + .foregroundStyle(.orange) + Label("The transaction manager will revalidate kind, destination, ownership, and checksum before replacing the selected definition.", systemImage: "checkmark.shield") + .font(.caption) + .foregroundStyle(.secondary) + } + HStack { + Spacer() + Button("Cancel") { dismiss() }.keyboardShortcut(.cancelAction) + Button("Replace Definition", role: .destructive) { + apply() + dismiss() + } + .disabled(!pending.preview.canApply) + .help(pending.preview.canApply + ? "Replace the selected checksum-bound automation definition after manager validation" + : pending.preview.validationMessage ?? "The import cannot be applied") + .accessibilityHint("Replaces the selected automation source; it does not create an unrelated new definition.") + } + } + .padding(20) + .frame(width: 560) + } +} + +extension AutomationEditorView.Purpose: Identifiable { + var id: String { self == .edit ? "edit" : "duplicate" } +} + +extension AutomationManagementAction: Identifiable { + public var id: String { + switch self { + case .restore(let id): "restore-\(id.rawValue.uuidString)" + default: title + } + } +} + +extension AutomationKind { + var displayTitle: String { + switch self { + case .launchAgent: "LaunchAgent" + case .launchDaemon: "LaunchDaemon" + case .loginItem: "Login Item" + case .backgroundItem: "Background Item" + case .cron: "Scheduled" + } + } + var symbolName: String { + AutomationVisualIdentity.fallback(for: self).symbolName + } +} + +extension AutomationSourceKind { + var displayTitle: String { + switch self { + case .launchAgent: "User LaunchAgents" + case .launchDaemon: "LaunchDaemons" + case .serviceManagement: "Background Items" + case .legacyLoginItem: "Login Items" + case .crontab: "Current-user crontab" + } + } +} + +extension AutomationState { + var displayTitle: String { + AutomationStateLabelPresentation(state: self).title + } + var symbolName: String { + AutomationVisualIdentity.state(for: self).symbolName + } + var tint: Color { + AutomationVisualIdentity.state(for: self).color + } +} + +extension AutomationOwnership { + var displayTitle: String { + switch self { + case .user: "User" + case .thirdPartySystem: "Third Party" + case .appleSystem: "Apple System" + case .managed: "Managed" + } + } +} + +extension AutomationSourceHealthState { + var displayTitle: String { + switch self { + case .healthy: "Healthy" + case .partial: "Partial" + case .failed: "Failed" + case .permissionRequired: "Permission required" + } + } +} + +extension AutomationOperation { + var displayTitle: String { + switch self { + case .startNow, .confirmedRunToCompletion: "Start Now" + case .stopCurrentRun: "Stop Current Run" + case .enable: "Enable" + case .disable: "Disable" + case .disableAndStop: "Disable and Stop" + case .edit: "Edit" + case .duplicate: "Duplicate" + case .importRecord: "Import" + case .exportRecord: "Export" + case .remove: "Remove" + case .restore: "Restore" + } + } +} diff --git a/Sources/DevScope/Views/ContentView.swift b/Sources/DevScope/Views/ContentView.swift new file mode 100644 index 0000000..c9c6cee --- /dev/null +++ b/Sources/DevScope/Views/ContentView.swift @@ -0,0 +1,1873 @@ +import AppKit +import DevScopeCore +import SwiftUI + +struct ContentView: View { + @Environment(\.accessibilityReduceMotion) private var reduceMotion + @ObservedObject private var store: ProcessStore + @ObservedObject private var automationStore: AutomationStore + private let automationNotifier: AutomationNotifier + @State private var searchText = "" + @State private var automationSearchText = "" + @State private var automationSourceFilter = AutomationSourceFilter.all + @State private var automationStateFilter: AutomationState? + @State private var automationOwnershipFilter = AutomationOwnershipFilter.all + @State private var activityTypeFilter = AutomationActivityTypeFilter.all + @State private var selectedProcessID: Int32? + @State private var retainedSelectedProcess: ClassifiedDevProcess? + @State private var pendingKillAction: KillAction? + @State private var isSponsorPresented = false + @State private var processSort = ProcessTableSortState() + @State private var lastCopiedPayload: PersistedCopyPayload? + @State private var expandedApplicationFamilyIDs: Set = [] + @AppStorage(DevScopeSettingsKey.useAppleNaming) private var useAppleNaming = true + @AppStorage(DevScopeSettingsKey.showProcessGraphs) private var showProcessGraphs = true + @AppStorage(DevScopeSettingsKey.showLiveNotch) private var showLiveNotch = true + @AppStorage(DevScopeSettingsKey.selectedCategoryID) private var selectedCategoryID = + ProcessPresentation.allCategoryID + @AppStorage(DevScopeSettingsKey.activityScope) private var activityScopeRawValue = + ProcessActivityScope.applications.rawValue + @AppStorage(DevScopeSettingsKey.isRailCollapsed) private var isRailCollapsed = false + @AppStorage(DevScopeSettingsKey.isLiveActivityExpanded) private var isLiveActivityExpanded = false + @AppStorage(DevScopeSettingsKey.liveActivityPreferredHeight) + private var liveActivityPreferredHeight = LiveActivityLayoutPolicy.defaultHeight + @AppStorage(DevScopeSettingsKey.selectedWorkspaceMode) private var workspaceModeRaw = + DevScopeWorkspaceMode.processes.rawValue + @AppStorage(DevScopeSettingsKey.includeAppleSystemServices) private var includeAppleSystemServices = false + @AppStorage("favoriteProcessKeys") private var favoriteProcessKeysRaw = "" + @AppStorage("watchedProcessKeys") private var watchedProcessKeysRaw = "" + private let persistentCopyStore = PersistentCopyStore() + private let automationDocumentPanel = AutomationDocumentPanel() + + init( + store: ProcessStore, + automationStore: AutomationStore, + automationNotifier: AutomationNotifier + ) { + self.store = store + self.automationStore = automationStore + self.automationNotifier = automationNotifier + } + + private var allProcesses: [ClassifiedDevProcess] { + store.classifiedProcesses + } + + private var activityScope: ProcessActivityScope { + ProcessActivityScope(rawValue: activityScopeRawValue) ?? .applications + } + + private var activityScopeBinding: Binding { + Binding( + get: { activityScope }, + set: { activityScopeRawValue = $0.rawValue } + ) + } + + private var activityFilteredProcesses: [ClassifiedDevProcess] { + allProcesses.filter(matchesActivityType) + } + + private var filteredProcesses: [ClassifiedDevProcess] { + let filtered: [ClassifiedDevProcess] + if let selectedWorkflow { + let processIDs = Set(selectedWorkflow.processIDs) + filtered = ProcessPresentation.filtered( + allProcesses.filter { processIDs.contains($0.process.pid) }, + categoryID: ProcessPresentation.allCategoryID, + searchText: searchText, + favoriteKeys: favoriteProcessKeys, + watchedKeys: watchedProcessKeys + ) + } else { + filtered = ProcessPresentation.filtered( + allProcesses, + categoryID: selectedCategoryID, + searchText: searchText, + favoriteKeys: favoriteProcessKeys, + watchedKeys: watchedProcessKeys + ) + } + return filtered.filter(matchesActivityType) + } + + private var intelligenceWorkflows: [DevWorkflow] { + store.workflows + } + + private var processRows: [ProcessTableRow] { + filteredProcesses + .enumerated() + .map { offset, item in + return ProcessTableRow( + item: item, + title: store.displayName(for: item), + isFavorite: ProcessPresentation.isSaved(item, in: favoriteProcessKeys), + isWatched: ProcessPresentation.isSaved(item, in: watchedProcessKeys), + automationBadges: AutomationPresentation.badges( + isAutomated: store.liveSnapshot.automationLinksByProcessID[item.process.pid] != nil, + isLongRunning: store.liveSnapshot.longRunningProcessIDs.contains(item.process.pid), + elapsed: item.process.resourceUsage?.elapsedTime ?? "-" + ), + snapshotOrder: offset + ) + } + .sorted(using: stableSortOrder) + } + + private var applicationFamilies: [ProcessApplicationFamily] { + ProcessScopePresentation.applicationFamilies( + for: activityFilteredProcesses, + searchText: searchText + ) + } + + private var hierarchyNodes: [ProcessHierarchyNode] { + ProcessScopePresentation.hierarchy( + for: activityFilteredProcesses, + searchText: searchText + ) + } + + private var activeVisibleProcesses: [ClassifiedDevProcess] { + switch activityScope { + case .applications: + processes(withIDs: applicationFamilies.flatMap(\.processIDs)) + case .processes, .workflows: + filteredProcesses + case .hierarchy: + processes(withIDs: ProcessScopePresentation.flattened(hierarchyNodes).map(\.id)) + } + } + + private var stableSortOrder: [KeyPathComparator] { + processSort.comparators + [ + KeyPathComparator(\ProcessTableRow.stableOrderKey, order: .forward) + ] + } + + private var categories: [DevProcessCategory] { + ProcessPresentation.categories( + for: allProcesses, + favoriteKeys: favoriteProcessKeys, + watchedKeys: watchedProcessKeys + ) + } + + private var selectedCategoryTitle: String { + if let selectedWorkflow { + return selectedWorkflow.title + } + + return categories.first { $0.id == selectedCategoryID }?.title ?? "All" + } + + private var activeScopeTitle: String { + switch activityScope { + case .applications, .hierarchy: + activityScope.title + case .processes, .workflows: + selectedCategoryTitle + } + } + + private var selectedWorkflow: DevWorkflow? { + intelligenceWorkflows.first { $0.id == selectedCategoryID } + } + + private var processEmptyTitle: String { + if store.statusMessage.localizedCaseInsensitiveContains("operation not permitted") + || store.statusMessage.localizedCaseInsensitiveContains("blocked by macOS") + || store.statusMessage.localizedCaseInsensitiveContains("native process") + { + return "Process scanning blocked" + } + + return "No matching running items" + } + + private var processEmptyDescription: String { + if processEmptyTitle == "Process scanning blocked" { + return + "This sandboxed build cannot inspect local process metadata. Open Settings > Access for permissions, diagnostics, and distribution guidance." + } + + return + "Refresh after opening an app, starting a service, running a script, or launching a build watcher." + } + + private var selectedProcess: ClassifiedDevProcess? { + guard let selectedProcessID else { + return nil + } + + return allProcesses.first { $0.process.pid == selectedProcessID } + } + + private var selectedProcessExistsInSnapshot: Bool { + guard let selectedProcessID else { + return false + } + + return allProcesses.contains { $0.process.pid == selectedProcessID } + } + + private var selectedProcessForDetail: ClassifiedDevProcess? { + if let selectedProcess { + return selectedProcess + } + + return selectedProcessExistsInSnapshot ? nil : retainedSelectedProcess + } + + private var selectedProcessIsEnded: Bool { + guard let selectedProcessID else { + return false + } + + return !store.isProcessLive(pid: selectedProcessID) + } + + private var selectedProcessIsFavorite: Bool { + guard let selectedProcessForDetail else { + return false + } + return ProcessPresentation.isSaved(selectedProcessForDetail, in: favoriteProcessKeys) + } + + private var selectedProcessIsWatched: Bool { + guard let selectedProcessForDetail else { + return false + } + return ProcessPresentation.isSaved(selectedProcessForDetail, in: watchedProcessKeys) + } + + private var selectedProcessMetricHistory: [DevProcessMetricSample] { + guard let selectedProcess = selectedProcessForDetail else { + return [] + } + + return store.metricHistory(for: selectedProcess.process.pid) + } + + private var selectedProcessFamilySummary: ProcessFamilySummary? { + guard let selectedProcess = selectedProcessForDetail else { + return nil + } + + return store.familySummary(for: selectedProcess.process) + } + + private var selectedProcessWorkflow: DevWorkflow? { + guard let selectedProcessForDetail else { + return nil + } + + return ProcessIntelligence.workflow( + containing: selectedProcessForDetail, in: intelligenceWorkflows) + } + + private var selectedProcessActionDecision: ProcessActionDecision? { + selectedProcessForDetail.map(store.actionDecision(for:)) + } + + private var selectedProcessInsight: DevProcessInsight? { + guard let selectedProcessForDetail else { + return nil + } + + return ProcessIntelligence.insight( + for: selectedProcessForDetail, + workflow: selectedProcessWorkflow, + familySummary: selectedProcessFamilySummary, + metricHistory: selectedProcessMetricHistory, + actionDecision: selectedProcessActionDecision ?? .allowed + ) + } + + private var selectedWorkflowNote: String? { + guard let selectedProcessWorkflow else { + return nil + } + + return store.workflowNotes[selectedProcessWorkflow.id]?.text + } + + private var dashboardStats: ProcessDashboardStats { + ProcessPresentation.dashboardStats( + visibleItems: activeVisibleProcesses, + totalItems: allProcesses, + dashboardMetricHistory: store.dashboardMetricHistory + ) + } + + var body: some View { + DevScopeGlassContainer { + ZStack(alignment: .top) { + mainWorkspace + if showLiveNotch { + LiveNotchPanelPresenter( + stats: dashboardStats, + metricHistory: store.dashboardMetricHistory, + selectedScope: activeScopeTitle, + isRefreshing: store.isRefreshing, + lastRefresh: store.lastRefresh, + refreshAction: { store.refresh() }, + displayName: store.displayName(for:) + ) + .frame(width: 0, height: 0) + .zIndex(3) + } + } + .padding(12) + } + .background(DevScopeBackground()) + .overlay(alignment: .topTrailing) { + if let feedback = store.actionFeedback { + ActionFeedbackBanner(feedback: feedback) + .padding(.top, 12) + .padding(.trailing, 14) + .transition(feedbackTransition) + } + } + .toolbar { + ToolbarItemGroup(placement: .primaryAction) { + if workspaceMode == .processes { + Button(action: exportVisibleProcesses) { Image(systemName: "tablecells") } + .accessibilityLabel("Copy visible rows") + .accessibilityHint("Copies the currently visible running items as redacted TSV.") + .help("Copy visible rows as redacted TSV") + + Button(action: restoreLastCopiedPayload) { Image(systemName: "clipboard") } + .disabled(lastCopiedPayload == nil) + .accessibilityLabel("Restore last copy") + .accessibilityHint("Restores the last command or export saved by DevScope to the clipboard.") + .help(lastCopiedPayload.map { "Restore last copy: \($0.label)" } ?? "No saved copy") + + Button(action: { store.refresh() }) { Image(systemName: "arrow.clockwise") } + .accessibilityLabel("Refresh running items") + .accessibilityHint("Scans local apps, services, agents, and processes now.") + .help("Refresh running items") + .keyboardShortcut("r", modifiers: [.command]) + } else { + Button(action: { automationStore.refresh() }) { Image(systemName: "arrow.clockwise") } + .disabled(automationStore.isRefreshing) + .accessibilityLabel("Refresh automations") + .accessibilityHint("Refreshes configured automation sources without changing them.") + .help("Refresh automation inventory") + .keyboardShortcut("r", modifiers: [.command]) + } + } + } + .onChange(of: categories) { _, categories in + if !categories.contains(where: { $0.id == selectedCategoryID }) + && !intelligenceWorkflows.contains(where: { $0.id == selectedCategoryID }) + { + selectedCategoryID = ProcessPresentation.allCategoryID + } + } + .onChange(of: intelligenceWorkflows) { _, workflows in + if !categories.contains(where: { $0.id == selectedCategoryID }) + && !workflows.contains(where: { $0.id == selectedCategoryID }) + { + selectedCategoryID = ProcessPresentation.allCategoryID + } + } + .onChange(of: activityScopeRawValue) { _, _ in + alignSelectionWithActivityScope() + } + .onChange(of: selectedProcessID) { _, _ in + scheduleRememberSelectedProcess() + } + .onChange(of: allProcesses) { _, _ in + if selectedProcessID != nil { + scheduleRememberSelectedProcess() + } + } + .onChange(of: useAppleNaming) { _, value in + store.usesAppleNaming = value + } + .onChange(of: store.actionFeedback) { _, feedback in + guard let feedback else { + return + } + announceFeedback(feedback) + } + .task { + store.usesAppleNaming = useAppleNaming + migrateLegacySavedIdentityKeys() + loadLastCopiedPayload() + store.startRealtimeUpdates() + } + .onDisappear { + store.stopRealtimeUpdates() + } + .onReceive(NotificationCenter.default.publisher(for: .devScopeRefreshRequested)) { _ in + if workspaceMode == .processes { + store.refresh() + } else { + automationStore.refresh() + } + } + .onReceive(NotificationCenter.default.publisher(for: .devScopeRestoreLastCopyRequested)) { _ in + restoreLastCopiedPayload() + } + .sheet(isPresented: $isSponsorPresented) { + SponsorSheetView() + } + .confirmationDialog( + pendingKillAction?.title ?? "Terminate process?", + isPresented: pendingKillBinding, + titleVisibility: .visible + ) { + if let action = pendingKillAction { + Button(action.buttonTitle, role: .destructive) { + switch action { + case .single(let process): + store.terminate(process) + case .tree(let process): + store.terminateTree(root: process) + case .forceSingle(let process): + store.forceTerminate(process) + case .forceTree(let process): + store.forceTerminateTree(root: process) + } + pendingKillAction = nil + } + } + + Button("Cancel", role: .cancel) { + pendingKillAction = nil + } + } message: { + if let action = pendingKillAction { + let descendants = store.familySummary(for: action.process.process).descendantCount + Text(action.consequence(descendantCount: descendants)) + } + } + .modifier(AutomationIntegrationModifier( + processStore: store, + automationStore: automationStore, + automationNotifier: automationNotifier + )) + } + + private var mainWorkspace: some View { + VStack(spacing: 10) { + Picker("Workspace", selection: workspaceModeBinding) { + Text("Processes").tag(DevScopeWorkspaceMode.processes) + Text("Automations").tag(DevScopeWorkspaceMode.automations) + } + .pickerStyle(.segmented) + .frame(maxWidth: 320) + .accessibilityHint("Switches between live running processes and configured automations without clearing either selection.") + + ZStack { + if workspaceMode == .processes { + processModeWorkspace + } + + AutomationWorkspaceView( + store: automationStore, + inventory: store.liveSnapshot.automationInventory, + processes: store.classifiedProcesses, + linksByProcessID: store.liveSnapshot.allAutomationLinksByProcessID, + searchText: $automationSearchText, + sourceFilter: $automationSourceFilter, + stateFilter: $automationStateFilter, + ownershipFilter: $automationOwnershipFilter, + includeAppleSystemServices: includeAppleSystemServices, + documentPanel: automationDocumentPanel + ) + .workspaceLayer(isActive: workspaceMode == .automations) + } + } + } + + private var processModeWorkspace: some View { + HSplitView { + CategoryRail( + selectedCategoryID: $selectedCategoryID, + activityScope: activityScopeBinding, + workflows: intelligenceWorkflows, + categories: categories, + isCollapsed: $isRailCollapsed, + isEnhancingWorkflows: store.isEnhancingWorkflows, + sponsorAction: { isSponsorPresented = true } + ) + .frame( + minWidth: isRailCollapsed ? 58 : 190, + idealWidth: isRailCollapsed ? 64 : 225, + maxWidth: isRailCollapsed ? 74 : 320 + ) + + VStack(spacing: 12) { + ControlStrip( + searchText: $searchText, + activityScope: activityScopeBinding, + selectedCategoryTitle: activeScopeTitle, + visibleProcessCount: activeVisibleProcesses.count, + totalProcessCount: allProcesses.count, + applicationCount: applicationFamilies.count, + activityTypeFilter: $activityTypeFilter + ) + selectedProcessCommandBar + monitoringWorkspace + } + .frame(minWidth: 620) + } + } + + @ViewBuilder private var selectedProcessCommandBar: some View { + if let selectedProcessForDetail { + ProcessCommandBar( + process: selectedProcessForDetail, + displayName: store.displayName(for: selectedProcessForDetail), + isEnded: selectedProcessIsEnded, + actionDecision: selectedProcessActionDecision ?? .allowed, + isFavorite: selectedProcessIsFavorite, + isWatched: selectedProcessIsWatched, + favoriteAction: toggleSelectedFavorite, + watchAction: toggleSelectedWatch, + exportAction: exportSelectedProcess, + copyCommandAction: copySelectedCommand, + openFolderAction: openSelectedFolder, + killAction: { pendingKillAction = .single(selectedProcessForDetail) }, + killTreeAction: { pendingKillAction = .tree(selectedProcessForDetail) }, + forceKillAction: { pendingKillAction = .forceSingle(selectedProcessForDetail) }, + forceKillTreeAction: { pendingKillAction = .forceTree(selectedProcessForDetail) } + ) + } + } + + @ViewBuilder + private var monitoringWorkspace: some View { + if isLiveActivityExpanded { + GeometryReader { proxy in + let resolvedHeight = LiveActivityLayoutPolicy.resolvedHeight( + preferredHeight: liveActivityPreferredHeight, + workspaceHeight: proxy.size.height + ) + + VSplitView { + processWorkspace + + liveActivityDock + .frame( + minHeight: LiveActivityLayoutPolicy.minimumHeight, + idealHeight: resolvedHeight, + maxHeight: LiveActivityLayoutPolicy.maximumHeight + ) + .background { + GeometryReader { dockProxy in + Color.clear.preference( + key: LiveActivityHeightPreferenceKey.self, + value: dockProxy.size.height + ) + } + } + } + .onPreferenceChange(LiveActivityHeightPreferenceKey.self) { measuredHeight in + let updatedHeight = LiveActivityLayoutPolicy.updatedPreferredHeight( + currentPreferredHeight: liveActivityPreferredHeight, + measuredHeight: measuredHeight, + workspaceHeight: proxy.size.height + ) + guard updatedHeight != liveActivityPreferredHeight else { return } + liveActivityPreferredHeight = updatedHeight + } + } + } else { + VStack(spacing: 12) { + processWorkspace + .layoutPriority(1) + + liveActivityDock + .frame(height: 60) + } + } + } + + private var processWorkspace: some View { + HSplitView { + activityBrowser + .frame(minWidth: 460) + .transaction { transaction in + transaction.animation = nil + } + + ProcessDetailView( + process: selectedProcessForDetail, + displayName: selectedProcessForDetail.map(store.displayName(for:)), + metricHistory: selectedProcessMetricHistory, + familySummary: selectedProcessFamilySummary, + showsMetricHistory: showProcessGraphs, + isEnded: selectedProcessIsEnded, + isFavorite: selectedProcessIsFavorite, + isWatched: selectedProcessIsWatched, + workflow: selectedProcessWorkflow, + processInsight: selectedProcessInsight, + workflowNote: selectedWorkflowNote, + automationLink: selectedProcessForDetail.flatMap { store.liveSnapshot.automationLinksByProcessID[$0.process.pid] }, + automationRecord: selectedProcessForDetail + .flatMap { store.liveSnapshot.automationLinksByProcessID[$0.process.pid] } + .flatMap { link in store.liveSnapshot.automationInventory.records.first { $0.id == link.recordID } }, + showInAutomations: { recordID in + automationStore.selectedRecordID = recordID + workspaceModeRaw = DevScopeWorkspaceMode.automations.rawValue + } + ) + .frame(minWidth: 300, idealWidth: 360) + } + .softPanel(cornerRadius: 18) + .frame(minHeight: 360) + } + + @ViewBuilder + private var activityBrowser: some View { + switch activityScope { + case .applications: + ApplicationFamilyListView( + families: applicationFamilies, + selection: $selectedProcessID, + expandedFamilyIDs: $expandedApplicationFamilyIDs, + displayName: store.displayName(for:) + ) + case .processes, .workflows: + ProcessTableView( + rows: processRows, + emptyTitle: processEmptyTitle, + emptyDescription: processEmptyDescription, + selection: $selectedProcessID, + sort: $processSort, + copyCommand: copyCommand + ) + case .hierarchy: + ProcessHierarchyView( + nodes: hierarchyNodes, + selection: $selectedProcessID, + displayName: store.displayName(for:) + ) + } + } + + private var liveActivityDock: some View { + LiveActivitiesDockView( + stats: dashboardStats, + metricHistory: store.dashboardMetricHistory, + isRefreshing: store.isRefreshing, + lastRefresh: store.lastRefresh, + isExpanded: $isLiveActivityExpanded + ) + } + + private var favoriteProcessKeys: Set { + decodeKeys(favoriteProcessKeysRaw) + } + + private var watchedProcessKeys: Set { + decodeKeys(watchedProcessKeysRaw) + } + + private func rememberSelectedProcess() { + let resolution = ProcessSelectionPolicy.reconcile( + selectedProcessID: selectedProcessID, + retainedProcess: retainedSelectedProcess, + currentProcesses: allProcesses + ) + if selectedProcessID != resolution.selectedProcessID { + selectedProcessID = resolution.selectedProcessID + } + if retainedSelectedProcess != resolution.retainedProcess { + retainedSelectedProcess = resolution.retainedProcess + } + } + + private func scheduleRememberSelectedProcess() { + DispatchQueue.main.async { + rememberSelectedProcess() + } + } + + private func toggleSelectedFavorite() { + guard let selectedProcessForDetail else { + return + } + + let willRemove = ProcessPresentation.isSaved(selectedProcessForDetail, in: favoriteProcessKeys) + favoriteProcessKeysRaw = toggledKeys(for: selectedProcessForDetail, in: favoriteProcessKeys) + store.presentFeedback( + title: willRemove ? "Favorite removed" : "Favorite added", + detail: store.displayName(for: selectedProcessForDetail), + symbolName: willRemove ? "star.slash" : "star.fill", + kind: .success + ) + } + + private func toggleSelectedWatch() { + guard let selectedProcessForDetail else { + return + } + + let willRemove = ProcessPresentation.isSaved(selectedProcessForDetail, in: watchedProcessKeys) + watchedProcessKeysRaw = toggledKeys(for: selectedProcessForDetail, in: watchedProcessKeys) + store.presentFeedback( + title: willRemove ? "Watch removed" : "Watch added", + detail: store.displayName(for: selectedProcessForDetail), + symbolName: willRemove ? "eye.slash" : "eye.fill", + kind: .success + ) + } + + private func exportSelectedProcess() { + guard let selectedProcessForDetail else { + return + } + + copyToPasteboard( + ProcessPresentation.exportRows([selectedProcessForDetail]), label: "Process export") + store.statusMessage = "Copied process export" + store.presentFeedback( + title: "Process export copied", + detail: store.displayName(for: selectedProcessForDetail), + symbolName: "square.and.arrow.up", + kind: .success + ) + } + + private func exportVisibleProcesses() { + copyToPasteboard( + ProcessPresentation.exportRows(activeVisibleProcesses), label: "Visible rows export") + store.statusMessage = "Copied \(activeVisibleProcesses.count) OS processes" + store.presentFeedback( + title: "Visible rows copied", + detail: + "\(activeVisibleProcesses.count) redacted OS process row\(activeVisibleProcesses.count == 1 ? "" : "s")", + symbolName: "square.and.arrow.up", + kind: .success + ) + } + + private func matchesActivityType(_ item: ClassifiedDevProcess) -> Bool { + activityTypeFilter.matches( + isAutomated: store.liveSnapshot.automationLinksByProcessID[item.process.pid] != nil, + isLongRunning: store.liveSnapshot.longRunningProcessIDs.contains(item.process.pid) + ) + } + + private func processes(withIDs processIDs: [Int32]) -> [ClassifiedDevProcess] { + let visibleIDs = Set(processIDs) + return activityFilteredProcesses.filter { visibleIDs.contains($0.process.pid) } + } + + private func alignSelectionWithActivityScope() { + switch activityScope { + case .workflows: + if selectedWorkflow == nil { + selectedCategoryID = intelligenceWorkflows.first?.id ?? ProcessPresentation.allCategoryID + } + case .processes: + if selectedWorkflow != nil { + selectedCategoryID = ProcessPresentation.allCategoryID + } + case .applications, .hierarchy: + break + } + } + + private func copySelectedCommand() { + guard let selectedProcessForDetail else { + return + } + + copyCommand(selectedProcessForDetail) + } + + private func copyCommand(_ item: ClassifiedDevProcess) { + copyToPasteboard( + item.process.command, + label: "Process command", + recoveryText: ProcessPresentation.redactedCommand(item.process.command) + ) + store.presentFeedback( + title: "Command copied", + detail: store.displayName(for: item), + symbolName: "doc.on.doc.fill", + kind: .success + ) + } + + private func openSelectedFolder() { + guard let selectedProcessForDetail, + let currentDirectory = selectedProcessForDetail.process.currentDirectory + else { + return + } + + let didOpen = NSWorkspace.shared.open( + URL(fileURLWithPath: currentDirectory, isDirectory: true) + ) + store.presentFeedback( + title: didOpen ? "Folder opened" : "Folder unavailable", + detail: didOpen ? currentDirectory : "macOS could not open this process folder.", + symbolName: didOpen ? "folder.fill" : "folder.badge.questionmark", + kind: didOpen ? .info : .warning + ) + } + + private func toggledKeys(for item: ClassifiedDevProcess, in keys: Set) -> String { + var updated = keys + let aliases = ProcessPresentation.identityKeys(for: item) + if !updated.isDisjoint(with: aliases) { + updated.subtract(aliases) + } else { + updated.insert(ProcessPresentation.identityKey(for: item)) + } + return encodeKeys(updated) + } + + private func decodeKeys(_ rawValue: String) -> Set { + Set(rawValue.split(separator: "\n").map(String.init)) + } + + private func encodeKeys(_ keys: Set) -> String { + keys.sorted().joined(separator: "\n") + } + + private func migrateLegacySavedIdentityKeys() { + favoriteProcessKeysRaw = encodeKeys( + ProcessPresentation.sanitizedSavedIdentityKeys(decodeKeys(favoriteProcessKeysRaw)) + ) + watchedProcessKeysRaw = encodeKeys( + ProcessPresentation.sanitizedSavedIdentityKeys(decodeKeys(watchedProcessKeysRaw)) + ) + } + + private func announceFeedback(_ feedback: ProcessActionFeedback) { + let announcement = "\(feedback.title). \(feedback.detail)" + let userInfo: [NSAccessibility.NotificationUserInfoKey: Any] = [ + .announcement: announcement, + .priority: NSAccessibilityPriorityLevel.high.rawValue, + ] + NSAccessibility.post( + element: NSApp as Any, + notification: .announcementRequested, + userInfo: userInfo + ) + } + + private func copyToPasteboard(_ value: String, label: String, recoveryText: String? = nil) { + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(value, forType: .string) + do { + lastCopiedPayload = try persistentCopyStore.save(text: recoveryText ?? value, label: label) + } catch { + store.presentFeedback( + title: "Copy recovery failed", + detail: error.localizedDescription, + symbolName: "externaldrive.badge.exclamationmark", + kind: .warning + ) + } + } + + private func loadLastCopiedPayload() { + do { + lastCopiedPayload = try persistentCopyStore.load() + } catch { + lastCopiedPayload = nil + store.presentFeedback( + title: "Copy recovery unavailable", + detail: error.localizedDescription, + symbolName: "externaldrive.badge.exclamationmark", + kind: .warning + ) + } + } + + private func restoreLastCopiedPayload() { + do { + guard let payload = try persistentCopyStore.load() else { + lastCopiedPayload = nil + store.presentFeedback( + title: "No saved copy", + detail: "Copy or export a process first.", + symbolName: "clipboard", + kind: .info + ) + return + } + + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(payload.text, forType: .string) + lastCopiedPayload = payload + store.presentFeedback( + title: "Redacted recovery copy restored", + detail: payload.isTruncated + ? "\(payload.label) · recovery cache was truncated" : payload.label, + symbolName: "clipboard.fill", + kind: payload.isTruncated ? .warning : .success + ) + } catch { + lastCopiedPayload = nil + store.presentFeedback( + title: "Restore failed", + detail: error.localizedDescription, + symbolName: "clipboard", + kind: .error + ) + } + } + + private var feedbackTransition: AnyTransition { + reduceMotion ? .identity : .move(edge: .top).combined(with: .opacity) + } + + private var workspaceMode: DevScopeWorkspaceMode { + DevScopeWorkspaceMode(rawValue: workspaceModeRaw) ?? .processes + } + + private var workspaceModeBinding: Binding { + Binding( + get: { workspaceMode }, + set: { workspaceModeRaw = $0.rawValue } + ) + } + + private var pendingKillBinding: Binding { + Binding( + get: { pendingKillAction != nil }, + set: { presented in + if !presented { pendingKillAction = nil } + } + ) + } +} + +private extension View { + func workspaceLayer(isActive: Bool) -> some View { + opacity(isActive ? 1 : 0) + .disabled(!isActive) + .allowsHitTesting(isActive) + .accessibilityHidden(!isActive) + } +} + +private struct AutomationIntegrationModifier: ViewModifier { + @ObservedObject var processStore: ProcessStore + @ObservedObject var automationStore: AutomationStore + let automationNotifier: AutomationNotifier + @AppStorage(DevScopeSettingsKey.longRunningThresholdSeconds) private var longRunningThresholdSeconds = 14_400.0 + @AppStorage(DevScopeSettingsKey.notifyLongRunningAutomation) private var notifyLongRunningAutomation = false + @AppStorage(DevScopeSettingsKey.notifyUnexpectedAutomationExit) private var notifyUnexpectedAutomationExit = false + @AppStorage(DevScopeSettingsKey.notifyRepeatedAutomationFailure) private var notifyRepeatedAutomationFailure = false + + func body(content: Content) -> some View { + content + .onChange(of: automationStore.snapshot) { _, snapshot in + processStore.updateAutomationContext( + inventory: snapshot, + longRunningThreshold: AutomationPresentationSettings.normalizedThreshold(longRunningThresholdSeconds) + ) + } + .onChange(of: longRunningThresholdSeconds) { _, threshold in + processStore.updateAutomationContext( + inventory: automationStore.snapshot, + longRunningThreshold: AutomationPresentationSettings.normalizedThreshold(threshold) + ) + } + .onChange(of: processStore.automationEvents) { _, events in + guard !events.isEmpty else { return } + Task { + await automationNotifier.consume(events) + } + } + .task { + processStore.updateAutomationContext( + inventory: automationStore.snapshot, + longRunningThreshold: AutomationPresentationSettings.normalizedThreshold(longRunningThresholdSeconds) + ) + await automationNotifier.synchronize(AutomationNotificationPreferences( + crossedLongRunningThreshold: notifyLongRunningAutomation, + unexpectedExit: notifyUnexpectedAutomationExit, + repeatedFailure: notifyRepeatedAutomationFailure + )) + automationStore.start() + } + .onDisappear { + automationStore.stop() + } + } +} + +private struct ActionFeedbackBanner: View { + let feedback: ProcessActionFeedback + + var body: some View { + HStack(alignment: .top, spacing: 10) { + Image(systemName: feedback.symbolName) + .font(.system(size: 15, weight: .semibold)) + .foregroundStyle(tint) + .frame(width: 18, height: 18) + + VStack(alignment: .leading, spacing: 2) { + Text(feedback.title) + .font(.callout.weight(.semibold)) + .lineLimit(1) + Text(feedback.detail) + .font(.caption) + .foregroundStyle(.secondary) + .lineLimit(2) + .truncationMode(.middle) + } + .frame(maxWidth: 300, alignment: .leading) + } + .padding(.horizontal, 12) + .padding(.vertical, 10) + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous)) + .overlay { + RoundedRectangle(cornerRadius: 12, style: .continuous) + .stroke(tint.opacity(0.28), lineWidth: 1) + } + .shadow(color: .black.opacity(0.22), radius: 16, y: 8) + } + + private var tint: Color { + switch feedback.kind { + case .success: + .green + case .warning: + .orange + case .error: + .red + case .info: + DevScopePalette.accent + } + } +} + +private struct CategoryRail: View { + @Environment(\.openSettings) private var openSettings + @Binding var selectedCategoryID: String + @Binding var activityScope: ProcessActivityScope + let workflows: [DevWorkflow] + let categories: [DevProcessCategory] + @Binding var isCollapsed: Bool + let isEnhancingWorkflows: Bool + let sponsorAction: () -> Void + @State private var showsAllWorkflows = false + @State private var showsRuntimeFilters = true + + var body: some View { + VStack(spacing: 10) { + RailHeader(isCollapsed: $isCollapsed) + + ScrollView { + VStack(alignment: .leading, spacing: isCollapsed ? 6 : 8) { + if !savedCategories.isEmpty { + if !isCollapsed { + RailSectionTitle(title: "Saved", detail: nil) + } + + VStack(spacing: 3) { + ForEach(savedCategories) { category in + Button { + selectedCategoryID = category.id + activityScope = .processes + } label: { + CategoryRailItem( + category: category, + isSelected: selectedCategoryID == category.id, + isCollapsed: isCollapsed + ) + } + .buttonStyle(.plain) + .accessibilityLabel(category.title) + .accessibilityValue("\(category.count) item\(category.count == 1 ? "" : "s")") + .accessibilityHint("Filters the running activity list to \(category.title).") + .accessibilityAddTraits(selectedCategoryID == category.id ? .isSelected : []) + .help("Filter running activity by \(category.title)") + } + } + .padding(.bottom, isCollapsed ? 0 : 2) + } + + if !activityCategories.isEmpty { + if !isCollapsed { + RuntimeFiltersDisclosure( + categories: activityCategories, + selectedCategoryID: selectedCategoryID, + isExpanded: $showsRuntimeFilters + ) + } + + if isCollapsed || showsRuntimeFilters + || activityCategories.contains(where: { $0.id == selectedCategoryID }) + { + VStack(spacing: 2) { + ForEach(activityCategories) { category in + Button { + selectedCategoryID = category.id + activityScope = .processes + } label: { + RuntimeRailItem( + category: category, + isSelected: selectedCategoryID == category.id, + isCollapsed: isCollapsed + ) + } + .buttonStyle(.plain) + .accessibilityLabel(category.title) + .accessibilityValue("\(category.count) item\(category.count == 1 ? "" : "s")") + .accessibilityHint("Filters the running activity list to \(category.title).") + .accessibilityAddTraits(selectedCategoryID == category.id ? .isSelected : []) + .help("Filter running activity by \(category.title)") + } + } + .padding(.leading, isCollapsed ? 0 : 12) + .padding(.bottom, isCollapsed ? 0 : 4) + } + } + + if !workflows.isEmpty { + if !isCollapsed { + RailSectionTitle( + title: "Focus", + detail: isEnhancingWorkflows ? "Apple AI" : "\(workflows.count)" + ) + } + + VStack(spacing: 4) { + ForEach(visibleWorkflows) { workflow in + Button { + selectedCategoryID = workflow.id + activityScope = .workflows + } label: { + WorkflowRailItem( + workflow: workflow, + isSelected: selectedCategoryID == workflow.id, + isCollapsed: isCollapsed + ) + } + .buttonStyle(.plain) + .accessibilityLabel(workflow.title) + .accessibilityValue(workflow.subtitle) + .accessibilityHint(workflow.summary) + .accessibilityAddTraits(selectedCategoryID == workflow.id ? .isSelected : []) + .help(workflow.summary) + } + + if !isCollapsed && workflows.count > visibleWorkflows.count { + RailDisclosureButton( + title: showsAllWorkflows + ? "Show fewer" : "Show \(workflows.count - visibleWorkflows.count) more", + systemImage: showsAllWorkflows ? "chevron.up" : "chevron.down", + tint: DevScopePalette.accent + ) { + showsAllWorkflows.toggle() + } + } + } + } + } + } + + Divider() + + VStack(spacing: 3) { + RailActionButton( + title: "Support", symbolName: "cup.and.saucer.fill", tint: .pink, + isCollapsed: isCollapsed, action: sponsorAction) + RailActionButton( + title: "Settings", symbolName: "gearshape", tint: .secondary, isCollapsed: isCollapsed + ) { + openSettings() + } + } + } + .padding(6) + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous)) + .overlay { + RoundedRectangle(cornerRadius: 12, style: .continuous) + .stroke(.white.opacity(0.08), lineWidth: 1) + } + } + + private var savedCategories: [DevProcessCategory] { + categories.filter { category in + category.id == ProcessPresentation.favoritesCategoryID + || category.id == ProcessPresentation.watchedCategoryID + } + } + + private var activityCategories: [DevProcessCategory] { + categories.filter { category in + category.id != ProcessPresentation.favoritesCategoryID + && category.id != ProcessPresentation.watchedCategoryID + } + } + + private var visibleWorkflows: [DevWorkflow] { + guard !isCollapsed else { + return workflows + } + + return InterfacePresentation.visibleFocusWorkflows( + workflows, + selectedID: selectedCategoryID, + showsAll: showsAllWorkflows + ) + } +} + +private func disclosureAnimation(reduceMotion: Bool) -> Animation? { + reduceMotion ? nil : .snappy(duration: 0.18) +} + +private struct RailHeader: View { + @Environment(\.accessibilityReduceMotion) private var reduceMotion + @Binding var isCollapsed: Bool + + var body: some View { + HStack { + if !isCollapsed { + Text("Control") + .font(.caption.weight(.semibold)) + .foregroundStyle(.secondary) + .textCase(.uppercase) + Spacer() + } + + Button { + withAnimation(disclosureAnimation(reduceMotion: reduceMotion)) { + isCollapsed.toggle() + } + } label: { + Image(systemName: isCollapsed ? "sidebar.left" : "sidebar.leading") + .accessibilityHidden(true) + } + .buttonStyle(.borderless) + .accessibilityLabel(isCollapsed ? "Expand control panel" : "Collapse control panel") + .accessibilityHint(isCollapsed ? "Shows the control panel." : "Hides the control panel.") + .help(isCollapsed ? "Expand panel" : "Collapse panel") + } + .padding(.horizontal, isCollapsed ? 2 : 8) + } +} + +private struct RuntimeFiltersDisclosure: View { + @Environment(\.accessibilityReduceMotion) private var reduceMotion + let categories: [DevProcessCategory] + let selectedCategoryID: String + @Binding var isExpanded: Bool + + var body: some View { + Button { + withAnimation(disclosureAnimation(reduceMotion: reduceMotion)) { + isExpanded.toggle() + } + } label: { + VStack(alignment: .leading, spacing: 8) { + HStack { + Label("Activity types", systemImage: "square.grid.2x2") + .font(.caption.weight(.semibold)) + .foregroundStyle(.secondary) + .textCase(.uppercase) + + Spacer() + + Text("\(runtimeOnlyCategories.count)") + .font(.caption2.monospacedDigit().weight(.semibold)) + .foregroundStyle(.tertiary) + + Image(systemName: isExpanded ? "chevron.up" : "chevron.down") + .font(.caption.weight(.semibold)) + .foregroundStyle(.secondary) + } + + RuntimeMixBar(runtimeCounts: runtimeCounts, height: 7) + + HStack(spacing: 7) { + ForEach(topRuntimeCategories.prefix(3)) { category in + HStack(spacing: 4) { + Circle() + .fill(color(for: category)) + .frame(width: 6, height: 6) + Text(category.title) + .lineLimit(1) + Text("\(category.count)") + .monospacedDigit() + .foregroundStyle(.tertiary) + } + .font(.caption2) + .foregroundStyle(selectedCategoryID == category.id ? .primary : .secondary) + } + } + } + .padding(.horizontal, 8) + .padding(.vertical, 8) + .background( + .quaternary.opacity(0.20), in: RoundedRectangle(cornerRadius: 8, style: .continuous) + ) + .overlay { + RoundedRectangle(cornerRadius: 8, style: .continuous) + .stroke(.white.opacity(0.08), lineWidth: 1) + } + .accessibilityHidden(true) + } + .buttonStyle(.plain) + .accessibilityLabel(isExpanded ? "Collapse activity types" : "Expand activity types") + .accessibilityValue(isExpanded ? "Expanded" : "Collapsed") + .accessibilityHint("Shows or hides dynamic activity filters.") + .help(isExpanded ? "Hide activity filters" : "Show activity filters") + } + + private var runtimeOnlyCategories: [DevProcessCategory] { + categories.filter { category in + DevRuntimeKind(rawValue: category.title) != nil + } + } + + private var runtimeCounts: [DevRuntimeKind: Int] { + Dictionary( + uniqueKeysWithValues: runtimeOnlyCategories.compactMap { category in + guard let kind = DevRuntimeKind(rawValue: category.title) else { + return nil + } + return (kind, category.count) + } + ) + } + + private var topRuntimeCategories: [DevProcessCategory] { + runtimeOnlyCategories.sorted { lhs, rhs in + if lhs.count != rhs.count { + return lhs.count > rhs.count + } + return lhs.title.localizedStandardCompare(rhs.title) == .orderedAscending + } + } + + private func color(for category: DevProcessCategory) -> Color { + if category.id == ProcessPresentation.allCategoryID { + return DevScopePalette.accent + } + guard let kind = DevRuntimeKind(rawValue: category.title) else { + return .secondary + } + return DevScopePalette.color(for: kind) + } +} + +private struct RuntimeRailItem: View { + let category: DevProcessCategory + let isSelected: Bool + let isCollapsed: Bool + + var body: some View { + HStack(spacing: isCollapsed ? 0 : 7) { + if !isCollapsed { + Capsule() + .fill(color.opacity(0.45)) + .frame(width: 2, height: 20) + } + + Image(systemName: category.symbolName) + .font(.system(size: 12, weight: .semibold)) + .foregroundStyle(color) + .frame(width: isCollapsed ? 30 : 16, height: 22) + + if !isCollapsed { + Text(category.title) + .font(.caption.weight(isSelected ? .semibold : .regular)) + .foregroundStyle(isSelected ? .primary : .secondary) + .lineLimit(1) + .minimumScaleFactor(0.72) + + Spacer(minLength: 4) + + Text("\(category.count)") + .font(.caption2.monospacedDigit().weight(.semibold)) + .foregroundStyle(isSelected ? .primary : .tertiary) + .contentTransition(.numericText()) + .frame(width: 22, alignment: .trailing) + } + } + .padding(.horizontal, isCollapsed ? 5 : 7) + .padding(.vertical, 4) + .background { + if isSelected { + RoundedRectangle(cornerRadius: 7, style: .continuous) + .fill(color.opacity(0.14)) + } + } + .overlay { + if isSelected { + RoundedRectangle(cornerRadius: 7, style: .continuous) + .stroke(color.opacity(0.28), lineWidth: 1) + } + } + .contentShape(RoundedRectangle(cornerRadius: 7, style: .continuous)) + .accessibilityHidden(true) + } + + private var color: Color { + if category.id == ProcessPresentation.allCategoryID { + return DevScopePalette.accent + } + guard let kind = DevRuntimeKind(rawValue: category.title) else { + return .secondary + } + return DevScopePalette.color(for: kind) + } +} + +private struct RailDisclosureButton: View { + @Environment(\.accessibilityReduceMotion) private var reduceMotion + let title: String + let systemImage: String + let tint: Color + let action: () -> Void + + var body: some View { + Button { + withAnimation(disclosureAnimation(reduceMotion: reduceMotion)) { + action() + } + } label: { + Label(title, systemImage: systemImage) + .font(.caption.weight(.semibold)) + .foregroundStyle(tint) + .frame(maxWidth: .infinity) + .padding(.vertical, 5) + .background(tint.opacity(0.08), in: RoundedRectangle(cornerRadius: 7, style: .continuous)) + .accessibilityHidden(true) + } + .buttonStyle(.plain) + .accessibilityLabel(title) + .accessibilityHint(title) + .help(title) + } +} + +private struct RailSectionTitle: View { + let title: String + let detail: String? + + var body: some View { + HStack { + Text(title) + .font(.caption.weight(.semibold)) + .foregroundStyle(.secondary) + .textCase(.uppercase) + Spacer() + if let detail { + Text(detail) + .font(.caption2.monospacedDigit()) + .foregroundStyle(.tertiary) + } + } + .padding(.horizontal, 7) + .padding(.top, 3) + } +} + +private struct WorkflowRailItem: View { + let workflow: DevWorkflow + let isSelected: Bool + let isCollapsed: Bool + + var body: some View { + HStack(spacing: isCollapsed ? 0 : 8) { + Image(systemName: workflow.kind.symbolName) + .font(.system(size: 14, weight: .semibold)) + .foregroundStyle(workflowColor) + .frame(width: isCollapsed ? 30 : 34, height: 24) + + if !isCollapsed { + VStack(alignment: .leading, spacing: 2) { + Text(workflow.title) + .font(.callout.weight(isSelected ? .semibold : .regular)) + .foregroundStyle(isSelected ? .primary : .secondary) + .lineLimit(1) + + Text(workflow.subtitle) + .font(.caption2) + .foregroundStyle(.tertiary) + .lineLimit(1) + } + .layoutPriority(1) + + Spacer(minLength: 4) + + Text(riskLabel) + .font(.caption2.weight(.semibold)) + .foregroundStyle(riskColor) + .lineLimit(1) + .frame(width: 38, alignment: .trailing) + } + } + .padding(.horizontal, isCollapsed ? 5 : 8) + .padding(.vertical, 5) + .frame(minHeight: isCollapsed ? 34 : 38, alignment: .center) + .frame(maxWidth: .infinity, alignment: .leading) + .background { + if isSelected { + RoundedRectangle(cornerRadius: 7, style: .continuous) + .fill(workflowColor.opacity(0.16)) + } + } + .overlay { + if isSelected { + RoundedRectangle(cornerRadius: 7, style: .continuous) + .stroke(workflowColor.opacity(0.34), lineWidth: 1) + } + } + .contentShape(RoundedRectangle(cornerRadius: 7, style: .continuous)) + .accessibilityHidden(true) + } + + private var workflowColor: Color { + switch workflow.kind { + case .aiMLLab, .localLLMStack: + .pink + case .notebookSession, .dataApp: + .cyan + case .trainingRun: + .orange + case .apiService, .webWorkspace: + .indigo + case .vectorDatabase: + .purple + case .buildWorkspace: + .blue + case .projectWorkspace, .runtimeGroup: + DevScopePalette.accent + } + } + + private var riskLabel: String { + switch workflow.risk { + case .normal: + "" + case .busy: + "Busy" + case .heavy: + "Heavy" + } + } + + private var riskColor: Color { + switch workflow.risk { + case .normal: + .secondary + case .busy: + .orange + case .heavy: + .red + } + } +} + +private struct CategoryRailItem: View { + let category: DevProcessCategory + let isSelected: Bool + let isCollapsed: Bool + + var body: some View { + HStack(spacing: isCollapsed ? 0 : 8) { + Image(systemName: category.symbolName) + .font(.system(size: 14, weight: .semibold)) + .foregroundStyle(iconColor) + .frame(width: isCollapsed ? 30 : 18, height: 24) + + if !isCollapsed { + Text(category.title) + .font(.subheadline.weight(isSelected ? .semibold : .regular)) + .foregroundStyle(isSelected ? .primary : .secondary) + .lineLimit(1) + .minimumScaleFactor(0.72) + .layoutPriority(1) + + Spacer() + + Text("\(category.count)") + .font(.caption.monospacedDigit().weight(.semibold)) + .foregroundStyle(isSelected ? .primary : .tertiary) + .contentTransition(.numericText()) + .lineLimit(1) + .minimumScaleFactor(0.75) + .frame(width: 26, alignment: .trailing) + } + } + .padding(.horizontal, isCollapsed ? 5 : 8) + .padding(.vertical, 6) + .background { + if isSelected { + RoundedRectangle(cornerRadius: 7, style: .continuous) + .fill(DevScopePalette.accent.opacity(0.16)) + } + } + .overlay { + if isSelected { + RoundedRectangle(cornerRadius: 7, style: .continuous) + .stroke(DevScopePalette.accent.opacity(0.36), lineWidth: 1) + } + } + .contentShape(RoundedRectangle(cornerRadius: 7, style: .continuous)) + .accessibilityHidden(true) + } + + private var iconColor: Color { + if category.id == ProcessPresentation.allCategoryID { + DevScopePalette.accent + } else if category.id == ProcessPresentation.favoritesCategoryID { + .yellow + } else if category.id == ProcessPresentation.watchedCategoryID { + .cyan + } else if let kind = DevRuntimeKind(rawValue: category.title) { + DevScopePalette.color(for: kind) + } else { + .secondary + } + } +} + +private struct RailActionButton: View { + let title: String + let symbolName: String + let tint: Color + let isCollapsed: Bool + let action: () -> Void + + var body: some View { + Button(action: action) { + HStack(spacing: isCollapsed ? 0 : 8) { + Image(systemName: symbolName) + .font(.system(size: 14, weight: .semibold)) + .foregroundStyle(tint) + .frame(width: isCollapsed ? 30 : 18, height: 24) + if !isCollapsed { + Text(title) + .font(.subheadline) + .foregroundStyle(.secondary) + Spacer() + } + } + .padding(.horizontal, isCollapsed ? 5 : 8) + .padding(.vertical, 6) + .contentShape(RoundedRectangle(cornerRadius: 7, style: .continuous)) + .accessibilityHidden(true) + } + .buttonStyle(.plain) + .accessibilityLabel(title) + .accessibilityHint("Opens \(title).") + .help(title) + } +} + +private struct ControlStrip: View { + @Binding var searchText: String + @Binding var activityScope: ProcessActivityScope + let selectedCategoryTitle: String + let visibleProcessCount: Int + let totalProcessCount: Int + let applicationCount: Int + @Binding var activityTypeFilter: AutomationActivityTypeFilter + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + HStack(spacing: 12) { + ProcessScopePicker(selection: $activityScope) + + Spacer(minLength: 12) + + Text(resultText) + .font(.callout.monospacedDigit()) + .foregroundStyle(.secondary) + .lineLimit(1) + } + + HStack(spacing: 12) { + HStack(spacing: 8) { + Image(systemName: "magnifyingglass") + .foregroundStyle(.tertiary) + + TextField("Search application, project, command, PID, folder", text: $searchText) + .textFieldStyle(.plain) + + if !searchText.isEmpty { + Button { + searchText = "" + } label: { + Image(systemName: "xmark.circle.fill") + } + .buttonStyle(.plain) + .foregroundStyle(.tertiary) + .accessibilityLabel("Clear search") + .help("Clear search") + .transition(.scale.combined(with: .opacity)) + } + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + .softPanel(cornerRadius: 10) + .frame(maxWidth: 520) + + Spacer() + + Picker("Activity Type", selection: $activityTypeFilter) { + ForEach(AutomationActivityTypeFilter.allCases, id: \.self) { filter in + Text(filter.displayTitle).tag(filter) + } + } + .frame(width: 170) + .accessibilityHint("Filters Automated and Long Running signals independently or together.") + } + } + .padding(.horizontal, 10) + .padding(.vertical, 8) + .background(.bar, in: RoundedRectangle(cornerRadius: 12, style: .continuous)) + .overlay { + RoundedRectangle(cornerRadius: 12, style: .continuous) + .stroke(.white.opacity(0.08), lineWidth: 1) + } + } + + private var resultText: String { + switch activityScope { + case .applications: + return "\(applicationCount) applications · \(visibleProcessCount) OS processes" + case .processes, .hierarchy, .workflows: + if selectedCategoryTitle == "All" + && searchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + && activityTypeFilter == .all + { + return "\(totalProcessCount) OS processes" + } + return + "\(visibleProcessCount) of \(totalProcessCount) OS processes · \(selectedCategoryTitle)" + } + } +} + +private extension AutomationActivityTypeFilter { + var displayTitle: String { + switch self { + case .all: "All Activity" + case .automated: "Automated" + case .longRunning: "Long Running" + case .both: "Both" + } + } +} + +struct DevScopeBackground: View { + var body: some View { + ZStack { + LinearGradient( + colors: [ + DevScopePalette.backgroundTop, + DevScopePalette.backgroundBottom, + Color(nsColor: .controlBackgroundColor), + ], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + + RadialGradient( + colors: [ + DevScopePalette.accent.opacity(0.18), + .clear, + ], + center: .topLeading, + startRadius: 40, + endRadius: 520 + ) + .opacity(0.65) + } + .ignoresSafeArea() + } +} + +private struct LiveActivityHeightPreferenceKey: PreferenceKey { + static let defaultValue: CGFloat = 0 + + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value = nextValue() + } +} + +private enum KillAction: Identifiable { + case single(ClassifiedDevProcess) + case tree(ClassifiedDevProcess) + case forceSingle(ClassifiedDevProcess) + case forceTree(ClassifiedDevProcess) + + var process: ClassifiedDevProcess { + switch self { + case .single(let process), .tree(let process), + .forceSingle(let process), .forceTree(let process): + return process + } + } + + var id: String { + switch self { + case .single(let process): + "single-\(process.process.pid)" + case .tree(let process): + "tree-\(process.process.pid)" + case .forceSingle(let process): + "force-single-\(process.process.pid)" + case .forceTree(let process): + "force-tree-\(process.process.pid)" + } + } + + var title: String { + switch self { + case .single(let process): + "Terminate \(process.classification.displayName)?" + case .tree(let process): + "Terminate \(process.classification.displayName) and children?" + case .forceSingle(let process): + "Force kill \(process.classification.displayName)?" + case .forceTree(let process): + "Force kill \(process.classification.displayName) and children?" + } + } + + var buttonTitle: String { + switch self { + case .single: + "Send TERM" + case .tree: + "Send TERM to Tree" + case .forceSingle: + "Send KILL" + case .forceTree: + "Send KILL to Tree" + } + } + + private var terminationAction: ProcessTerminationAction { + switch self { + case .single: + .single + case .tree: + .tree + case .forceSingle: + .forceSingle + case .forceTree: + .forceTree + } + } + + func consequence(descendantCount: Int) -> String { + terminationAction.consequence( + pid: process.process.pid, + descendantCount: descendantCount + ) + } +} diff --git a/Sources/DevScope/Views/DevScopeStyle.swift b/Sources/DevScope/Views/DevScopeStyle.swift new file mode 100644 index 0000000..6722d41 --- /dev/null +++ b/Sources/DevScope/Views/DevScopeStyle.swift @@ -0,0 +1,131 @@ +import DevScopeCore +import SwiftUI + +enum DevScopePalette { + static let accent = Color(nsColor: .controlAccentColor) + static let backgroundTop = Color(nsColor: .windowBackgroundColor) + static let backgroundBottom = Color(nsColor: .underPageBackgroundColor) + + static func color(for kind: DevRuntimeKind) -> Color { + switch kind { + case .javascript: + .yellow + case .python: + .green + case .swift: + .blue + case .rust: + .orange + case .go: + .cyan + case .flutter: + .blue + case .java: + .red + case .database: + .purple + case .container: + .mint + case .webServer: + .indigo + case .ai: + .pink + case .mcp: + .indigo + case .browser: + .teal + case .macApp: + .blue + case .backgroundAgent: + .gray + case .systemService: + .brown + case .shell: + .yellow + case .other: + .secondary + } + } + + static func color(for automationKind: AutomationKind) -> Color { + AutomationVisualIdentity.fallback(for: automationKind).color + } +} + +extension AutomationColorToken { + var color: Color { + switch self { + case .accent: + DevScopePalette.accent + case .blue: + .blue + case .indigo: + .indigo + case .cyan: + .cyan + case .purple: + .purple + case .green: + .green + case .orange: + .orange + case .red: + .red + case .secondary: + .secondary + } + } +} + +extension AutomationVisualIdentity { + var color: Color { colorToken.color } +} + +struct DevScopeGlassContainer: View { + private let spacing: CGFloat + private let content: Content + + init(spacing: CGFloat = 18, @ViewBuilder content: () -> Content) { + self.spacing = spacing + self.content = content() + } + + var body: some View { + if #available(macOS 26.0, *) { + GlassEffectContainer(spacing: spacing) { + content + } + } else { + content + } + } +} + +extension View { + @ViewBuilder + func devScopeGlass(cornerRadius: CGFloat = 14, interactive: Bool = false) -> some View { + let shape = RoundedRectangle(cornerRadius: cornerRadius, style: .continuous) + + if #available(macOS 26.0, *) { + if interactive { + self.glassEffect(.regular.interactive(), in: .rect(cornerRadius: cornerRadius)) + } else { + self.glassEffect(.regular, in: .rect(cornerRadius: cornerRadius)) + } + } else { + self + .background(.regularMaterial, in: shape) + .overlay { + shape.stroke(.white.opacity(0.14), lineWidth: 1) + } + } + } + + func softPanel(cornerRadius: CGFloat = 14) -> some View { + background(.regularMaterial, in: RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)) + .overlay { + RoundedRectangle(cornerRadius: cornerRadius, style: .continuous) + .stroke(.white.opacity(0.12), lineWidth: 1) + } + } +} diff --git a/Sources/DevScope/Views/LiveNotchPanelPresenter.swift b/Sources/DevScope/Views/LiveNotchPanelPresenter.swift new file mode 100644 index 0000000..d337014 --- /dev/null +++ b/Sources/DevScope/Views/LiveNotchPanelPresenter.swift @@ -0,0 +1,269 @@ +import AppKit +import DevScopeCore +import SwiftUI + +@MainActor +final class LiveNotchPresentationModel: ObservableObject { + @Published private(set) var state = LiveNotchPresentationState.collapsed + + func publish(_ state: LiveNotchPresentationState) { + self.state = state + } +} + +@MainActor +struct LiveNotchPanelPresenter: NSViewRepresentable { + let stats: ProcessDashboardStats + let metricHistory: [DevProcessMetricSample] + let selectedScope: String + let isRefreshing: Bool + let lastRefresh: Date? + let refreshAction: @MainActor () -> Void + let displayName: @MainActor (ClassifiedDevProcess) -> String + + func makeCoordinator() -> Coordinator { + Coordinator() + } + + func makeNSView(context: Context) -> NSView { + let view = NSView(frame: .zero) + context.coordinator.update(from: self) + return view + } + + func updateNSView(_ nsView: NSView, context: Context) { + context.coordinator.update(from: self) + } + + static func dismantleNSView(_ nsView: NSView, coordinator: Coordinator) { + coordinator.close() + } + + @MainActor + final class Coordinator { + private static let screenTopInset: CGFloat = 3 + private static let visibleFrameLift: CGFloat = 6 + private static let activationBandHeight: CGFloat = 12 + private var panel: NSPanel? + private var isClosed = false + private var isApplyingPresentationState = false + private let presentationModel = LiveNotchPresentationModel() + + func update(from presenter: LiveNotchPanelPresenter) { + guard !isClosed else { + return + } + + let createdPanel = panel == nil + let panel = panel ?? makePanel() + self.panel = panel + + let rootView = DynamicIslandStatusView( + stats: presenter.stats, + metricHistory: presenter.metricHistory, + selectedScope: presenter.selectedScope, + isRefreshing: presenter.isRefreshing, + lastRefresh: presenter.lastRefresh, + refreshAction: presenter.refreshAction, + displayName: presenter.displayName, + presentationModel: presentationModel + ) + + if let hostingView = panel.contentView as? LiveNotchHostingView { + hostingView.rootView = rootView + } else { + let hostingView = LiveNotchHostingView(rootView: rootView) + hostingView.onPointerEvent = { [weak self] pointerEvent in + self?.handle(pointerEvent) + } + panel.contentView = hostingView + } + + if createdPanel || !panel.isVisible { + panel.orderFrontRegardless() + } + } + + func close() { + guard !isClosed else { + return + } + + if let hostingView = panel?.contentView as? LiveNotchHostingView { + hostingView.tearDown() + } + applyPresentationState(.collapsed) + isClosed = true + panel?.orderOut(nil) + panel?.close() + panel = nil + } + + private func makePanel() -> NSPanel { + let panel = NSPanel( + contentRect: frame(for: .collapsed), + styleMask: [.borderless, .nonactivatingPanel], + backing: .buffered, + defer: false + ) + panel.backgroundColor = .clear + panel.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary, .ignoresCycle] + panel.acceptsMouseMovedEvents = true + panel.hasShadow = false + panel.hidesOnDeactivate = false + panel.ignoresMouseEvents = false + panel.isMovable = false + panel.isOpaque = false + panel.isReleasedWhenClosed = false + panel.level = .statusBar + panel.title = "DevScope Live Notch" + panel.titleVisibility = .hidden + panel.titlebarAppearsTransparent = true + return panel + } + + private func handle(_ pointerEvent: LiveNotchPointerEvent) { + guard !isClosed, !isApplyingPresentationState, let panel else { + return + } + + let pointerLocation = pointerEvent.screenLocation + guard panel.frame.contains(pointerLocation) else { + applyPresentationState(.collapsed) + return + } + + switch (presentationModel.state, pointerEvent.kind) { + case (.collapsed, .entered), (.collapsed, .moved): + applyPresentationState(.compact) + case (.compact, .moved) where !isInActivationBand(pointerLocation, panelFrame: panel.frame): + applyPresentationState(.expanded) + case (.expanded, .moved) where isInActivationBand(pointerLocation, panelFrame: panel.frame): + applyPresentationState(.compact) + default: + break + } + } + + private func applyPresentationState(_ state: LiveNotchPresentationState) { + guard !isClosed, let panel else { + return + } + + let frame = frame(for: state) + guard presentationModel.state != state || !panel.frame.nearlyEquals(frame) else { + return + } + + isApplyingPresentationState = true + NSAnimationContext.runAnimationGroup { context in + context.duration = 0 + context.allowsImplicitAnimation = false + panel.setFrame(frame, display: true) + } + presentationModel.publish(state) + isApplyingPresentationState = false + + let pointerLocation = NSEvent.mouseLocation + if state != .collapsed, !frame.contains(pointerLocation) { + applyPresentationState(.collapsed) + } + } + + private func isInActivationBand(_ pointerLocation: NSPoint, panelFrame: NSRect) -> Bool { + pointerLocation.y >= panelFrame.maxY - Self.activationBandHeight + } + + private func frame(for state: LiveNotchPresentationState) -> NSRect { + let screen = NSScreen.main ?? NSScreen.screens.first + let screenFrame = screen?.frame ?? NSRect(x: 0, y: 0, width: 1440, height: 900) + let visibleFrame = screen?.visibleFrame ?? screenFrame + let panelSize = state.panelSize + let x = screenFrame.midX - panelSize.width / 2 + let topEdge = min(screenFrame.maxY - Self.screenTopInset, visibleFrame.maxY + Self.visibleFrameLift) + let y = topEdge - panelSize.height + return NSRect(x: x, y: y, width: panelSize.width, height: panelSize.height) + } + } +} + +@MainActor +private final class LiveNotchHostingView: NSHostingView { + var onPointerEvent: ((LiveNotchPointerEvent) -> Void)? + private var pointerTrackingArea: NSTrackingArea? + private var isTornDown = false + + override func updateTrackingAreas() { + super.updateTrackingAreas() + guard !isTornDown, pointerTrackingArea == nil else { + return + } + + let trackingArea = NSTrackingArea( + rect: .zero, + options: [.mouseEnteredAndExited, .mouseMoved, .activeAlways, .inVisibleRect], + owner: self, + userInfo: nil + ) + addTrackingArea(trackingArea) + pointerTrackingArea = trackingArea + } + + func tearDown() { + isTornDown = true + onPointerEvent = nil + if let pointerTrackingArea { + removeTrackingArea(pointerTrackingArea) + self.pointerTrackingArea = nil + } + } + + override func mouseEntered(with event: NSEvent) { + super.mouseEntered(with: event) + onPointerEvent?(LiveNotchPointerEvent(kind: .entered, screenLocation: NSEvent.mouseLocation)) + } + + override func mouseMoved(with event: NSEvent) { + super.mouseMoved(with: event) + onPointerEvent?(LiveNotchPointerEvent(kind: .moved, screenLocation: NSEvent.mouseLocation)) + } + + override func mouseExited(with event: NSEvent) { + super.mouseExited(with: event) + onPointerEvent?(LiveNotchPointerEvent(kind: .exited, screenLocation: NSEvent.mouseLocation)) + } +} + +private struct LiveNotchPointerEvent { + enum Kind { + case entered + case exited + case moved + } + + let kind: Kind + let screenLocation: NSPoint +} + +private extension NSRect { + func nearlyEquals(_ other: NSRect) -> Bool { + abs(origin.x - other.origin.x) < 0.5 && + abs(origin.y - other.origin.y) < 0.5 && + abs(size.width - other.size.width) < 0.5 && + abs(size.height - other.size.height) < 0.5 + } +} + +private extension LiveNotchPresentationState { + var panelSize: NSSize { + switch self { + case .collapsed: + NSSize(width: 128, height: 18) + case .compact: + NSSize(width: 640, height: 52) + case .expanded: + NSSize(width: 780, height: 178) + } + } + +} diff --git a/Sources/DevScope/Views/ProcessCommandBar.swift b/Sources/DevScope/Views/ProcessCommandBar.swift new file mode 100644 index 0000000..daa4573 --- /dev/null +++ b/Sources/DevScope/Views/ProcessCommandBar.swift @@ -0,0 +1,166 @@ +import AppKit +import DevScopeCore +import SwiftUI + +struct ProcessCommandBar: View { + let process: ClassifiedDevProcess + let displayName: String + let isEnded: Bool + let actionDecision: ProcessActionDecision + let isFavorite: Bool + let isWatched: Bool + let favoriteAction: () -> Void + let watchAction: () -> Void + let exportAction: () -> Void + let copyCommandAction: () -> Void + let openFolderAction: () -> Void + let killAction: () -> Void + let killTreeAction: () -> Void + let forceKillAction: () -> Void + let forceKillTreeAction: () -> Void + + private var terminatePresentation: ProcessActionControlPresentation { + ProcessActionPresentation.control(.terminate, isEnded: isEnded, actionDecision: actionDecision) + } + + private var terminateTreePresentation: ProcessActionControlPresentation { + ProcessActionPresentation.control(.terminateTree, isEnded: isEnded, actionDecision: actionDecision) + } + + private var forceOptionsPresentation: ProcessActionControlPresentation { + ProcessActionPresentation.control(.forceOptions, isEnded: isEnded, actionDecision: actionDecision) + } + + var body: some View { + HStack(spacing: 8) { + HStack(spacing: 8) { + Image(systemName: "terminal") + .font(.system(size: 15, weight: .semibold)) + .foregroundStyle(.secondary) + + VStack(alignment: .leading, spacing: 2) { + Text(displayName) + .font(.caption.weight(.semibold)) + .lineLimit(1) + Text("Command") + .font(.caption2) + .foregroundStyle(.tertiary) + .lineLimit(1) + } + } + .frame(width: 132, alignment: .leading) + + Text(process.process.command) + .font(.system(.caption, design: .monospaced).weight(.semibold)) + .textSelection(.enabled) + .lineLimit(1) + .truncationMode(.middle) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 12) + .padding(.vertical, 7) + .background(.quaternary.opacity(0.35), in: RoundedRectangle(cornerRadius: 7, style: .continuous)) + + HStack(spacing: 6) { + CommandBarButton( + title: isFavorite ? "Unfavorite" : "Favorite", + symbolName: isFavorite ? "star.fill" : "star", + tint: .yellow, + action: favoriteAction + ) + CommandBarButton( + title: isWatched ? "Unwatch" : "Watch", + symbolName: isWatched ? "eye.fill" : "eye", + tint: .cyan, + action: watchAction + ) + CommandBarButton(title: "Export", symbolName: "square.and.arrow.up", tint: .blue, action: exportAction) + CommandBarButton(title: "Copy", symbolName: "doc.on.doc", tint: .blue, action: copyCommandAction) + CommandBarButton( + title: "Folder", + symbolName: "folder", + tint: .blue, + isDisabled: process.process.currentDirectory == nil, + action: openFolderAction + ) + } + + Divider() + .frame(height: 28) + + Menu { + Button(role: .destructive, action: killAction) { + Label("Terminate Process…", systemImage: "xmark.circle") + } + .disabled(terminatePresentation.isDisabled) + .accessibilityHint(terminatePresentation.accessibilityHint) + .help(terminatePresentation.help) + + Button(role: .destructive, action: killTreeAction) { + Label("Terminate Process Tree…", systemImage: "point.3.connected.trianglepath.dotted") + } + .disabled(terminateTreePresentation.isDisabled) + .accessibilityHint(terminateTreePresentation.accessibilityHint) + .help(terminateTreePresentation.help) + + Divider() + + Button(role: .destructive, action: forceKillAction) { + Label("Force Kill Process…", systemImage: "bolt.fill") + } + .disabled(forceOptionsPresentation.isDisabled) + .accessibilityHint(forceOptionsPresentation.accessibilityHint) + .help(forceOptionsPresentation.help) + + Button(role: .destructive, action: forceKillTreeAction) { + Label("Force Kill Process Tree…", systemImage: "bolt.horizontal.fill") + } + .disabled(forceOptionsPresentation.isDisabled) + .accessibilityHint(forceOptionsPresentation.accessibilityHint) + .help(forceOptionsPresentation.help) + } label: { + Label("Terminate…", systemImage: "xmark.circle") + } + .menuStyle(.button) + .tint(.red) + .controlSize(.small) + .disabled( + terminatePresentation.isDisabled && + terminateTreePresentation.isDisabled && + forceOptionsPresentation.isDisabled + ) + .accessibilityLabel("Process termination options") + .accessibilityHint("Choose whether to terminate this process, its process tree, or use a force-kill option.") + .help("Process termination options") + } + .padding(.horizontal, 10) + .padding(.vertical, 7) + .background(.bar, in: RoundedRectangle(cornerRadius: 12, style: .continuous)) + .overlay { + RoundedRectangle(cornerRadius: 12, style: .continuous) + .stroke(.white.opacity(0.10), lineWidth: 1) + } + .transition(.move(edge: .top).combined(with: .opacity)) + } +} + +private struct CommandBarButton: View { + let title: String + let symbolName: String + let tint: Color + var isDisabled = false + let action: () -> Void + + var body: some View { + Button(action: action) { + Label(title, systemImage: symbolName) + .labelStyle(.iconOnly) + .frame(width: 24) + } + .buttonStyle(.bordered) + .tint(tint) + .controlSize(.small) + .disabled(isDisabled) + .accessibilityLabel(title) + .help(title) + } +} diff --git a/Sources/DevScope/Views/ProcessDetailView.swift b/Sources/DevScope/Views/ProcessDetailView.swift new file mode 100644 index 0000000..5191005 --- /dev/null +++ b/Sources/DevScope/Views/ProcessDetailView.swift @@ -0,0 +1,635 @@ +import AppKit +import DevScopeCore +import SwiftUI + +struct ProcessDetailView: View { + @Environment(\.accessibilityReduceMotion) private var reduceMotion + let process: ClassifiedDevProcess? + let displayName: String? + let metricHistory: [DevProcessMetricSample] + let familySummary: ProcessFamilySummary? + let showsMetricHistory: Bool + let isEnded: Bool + let isFavorite: Bool + let isWatched: Bool + let workflow: DevWorkflow? + let processInsight: DevProcessInsight? + let workflowNote: String? + let automationLink: AutomationProcessLink? + let automationRecord: AutomationRecord? + let showInAutomations: (AutomationRecord.ID) -> Void + + var body: some View { + Group { + if let process { + VStack(spacing: 0) { + VStack(alignment: .leading, spacing: 12) { + DetailHeader(process: process, title: displayName ?? process.classification.displayName) + + if let processInsight { + ProcessSafetySummary(insight: processInsight) + } + + if isEnded { + Label( + "This process ended. Last known details are retained for review.", + systemImage: "checkmark.circle" + ) + .font(.callout.weight(.medium)) + .foregroundStyle(.secondary) + .padding(10) + .frame(maxWidth: .infinity, alignment: .leading) + .softPanel(cornerRadius: 10) + } + } + .padding(16) + + Divider() + + ScrollView { + VStack(alignment: .leading, spacing: 16) { + DetailSection(title: "Status", systemImage: "checklist") { + DetailGrid { + DetailChip(label: "State", value: isEnded ? "Ended" : "Live") + DetailChip(label: "Favorite", value: isFavorite ? "Yes" : "No") + DetailChip(label: "Watched", value: isWatched ? "Yes" : "No") + DetailChip(label: "PID", value: "\(process.process.pid)") + DetailChip(label: "PPID", value: "\(process.process.parentPID)") + if let familySummary { + DetailChip(label: "Children", value: "\(familySummary.childCount)") + DetailChip(label: "Tree", value: "\(familySummary.descendantCount)") + } + } + } + + DetailSection(title: "Resources", systemImage: "gauge.with.dots.needle.67percent") { + DetailGrid { + DetailChip( + label: "CPU", + value: ProcessMetricFormat.cpu(process.process.resourceUsage?.cpuPercent)) + DetailChip(label: "CPU Avg", value: ProcessMetricFormat.cpu(averageCPU)) + DetailChip(label: "CPU Peak", value: ProcessMetricFormat.cpu(peakCPU)) + DetailChip( + label: "Memory", + value: ProcessMetricFormat.memory( + process.process.resourceUsage?.residentMemoryBytes)) + DetailChip(label: "Mem Avg", value: ProcessMetricFormat.memory(averageMemory)) + DetailChip(label: "Mem Peak", value: ProcessMetricFormat.memory(peakMemory)) + DetailChip(label: "GPU", value: ProcessMetricFormat.gpu(latestGPU)) + DetailChip( + label: "Running", value: process.process.resourceUsage?.elapsedTime ?? "-") + DetailChip(label: "Samples", value: "\(metricHistory.count)") + } + } + + if !process.classification.tags.isEmpty { + DetailSection(title: "Tags", systemImage: "tag") { + FlowTagList(tags: process.classification.tags) + } + } + + if let workflow { + WorkflowContextCard( + workflow: workflow, + workflowNote: workflowNote + ) + } + + if let automationLink, let automationRecord { + DetailSection(title: "Automation", systemImage: "gearshape.2") { + DetailGrid { + DetailChip(label: "Definition", value: automationRecord.label) + DetailChip(label: "Source", value: automationRecord.sourceKind.displayTitle) + DetailChip(label: "Trigger", value: automationRecord.schedule.summary) + DetailChip(label: "Evidence", value: automationLink.strength == .strong ? "Strong" : "Weak") + } + ForEach(automationLink.evidence, id: \.self) { evidence in + Text("\(evidence.source): \(evidence.detail)") + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } + Button("Show in Automations") { + showInAutomations(automationRecord.id) + } + .buttonStyle(.borderedProminent) + .accessibilityHint("Switches workspace mode and selects this exact automation definition.") + .help("Show this definition in Automations") + } + } + + if showsMetricHistory { + ProcessMetricHistoryView(samples: metricHistory) + } + + Divider() + + DetailSection(title: "Identity", systemImage: "person.text.rectangle") { + DetailGrid { + DetailChip(label: "Kind", value: process.classification.kind.rawValue) + DetailChip( + label: "Name", value: displayName ?? process.classification.displayName) + DetailChip( + label: "Executable", + value: URL(fileURLWithPath: ProcessPresentation.executablePath(for: process)) + .lastPathComponent) + DetailChip( + label: "Project", + value: ProcessPresentation.projectName(for: process) ?? "Unknown") + } + + DetailField( + label: "Executable Path", value: ProcessPresentation.executablePath(for: process)) + DetailField( + label: "Current Directory", value: process.process.currentDirectory ?? "Unknown") + } + + DetailSection(title: "Command", systemImage: "terminal") { + DetailGrid { + DetailChip(label: "Arguments", value: "\(commandArguments.count)") + DetailChip(label: "Length", value: "\(process.process.command.count) chars") + DetailChip( + label: "Redacted", + value: redactedCommand == process.process.command ? "No" : "Yes") + } + + DetailField(label: "Command", value: process.process.command, lineLimit: 10) + DetailField(label: "Redacted Command", value: redactedCommand, lineLimit: 8) + + if !commandArguments.isEmpty { + ArgumentTokenList(arguments: commandArguments) + } + } + } + .padding(16) + } + } + .transition(reduceMotion ? .identity : .opacity.combined(with: .scale(scale: 0.985))) + } else { + EmptyProcessInspector() + } + } + } + + private var redactedCommand: String { + guard let process else { + return "" + } + + return ProcessPresentation.redactedCommand(process.process.command) + } + + private var commandArguments: [String] { + guard let process else { + return [] + } + + return process.process.command + .split(whereSeparator: { $0.isWhitespace }) + .dropFirst() + .map(String.init) + } + + private var latestGPU: Double? { + metricHistory.last { $0.gpuPercent != nil }?.gpuPercent + } + + private var averageCPU: Double? { + average(metricHistory.map(\.cpuPercent)) + } + + private var peakCPU: Double? { + metricHistory.map(\.cpuPercent).max() + } + + private var averageMemory: Int64? { + let values = metricHistory.map(\.residentMemoryBytes) + guard !values.isEmpty else { + return nil + } + + return values.reduce(Int64(0), +) / Int64(values.count) + } + + private var peakMemory: Int64? { + metricHistory.map(\.residentMemoryBytes).max() + } + + private func average(_ values: [Double]) -> Double? { + guard !values.isEmpty else { + return nil + } + + return values.reduce(0, +) / Double(values.count) + } +} + +private struct EmptyProcessInspector: View { + var body: some View { + VStack(spacing: 16) { + Spacer(minLength: 24) + + Image(systemName: "cursorarrow.rays") + .font(.system(size: 38, weight: .medium)) + .foregroundStyle(.tertiary) + .frame(width: 68, height: 68) + .softPanel(cornerRadius: 16) + + VStack(spacing: 6) { + Text("Select a process") + .font(.title3.weight(.semibold)) + .lineLimit(1) + Text( + "Choose any row to inspect identity, command, resources, process tree, workflow context, and safe actions." + ) + .font(.callout) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + .fixedSize(horizontal: false, vertical: true) + .frame(maxWidth: 300) + } + + VStack(alignment: .leading, spacing: 8) { + EmptyInspectorLine( + symbolName: "gauge.with.dots.needle.67percent", title: "Resources", + detail: "CPU, memory, GPU sample, peaks, averages, and sample count") + EmptyInspectorLine( + symbolName: "point.3.connected.trianglepath.dotted", title: "Tree", + detail: "Parent, children, and retained ended-process state") + EmptyInspectorLine( + symbolName: "terminal", title: "Command", + detail: "Full command, redacted command, argument count, and copyable fields") + } + .padding(14) + .frame(maxWidth: 340, alignment: .leading) + .softPanel(cornerRadius: 14) + + Spacer(minLength: 24) + } + .padding(18) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } +} + +private struct EmptyInspectorLine: View { + let symbolName: String + let title: String + let detail: String + + var body: some View { + HStack(alignment: .top, spacing: 10) { + Image(systemName: symbolName) + .font(.system(size: 13, weight: .semibold)) + .foregroundStyle(DevScopePalette.accent) + .frame(width: 18) + + VStack(alignment: .leading, spacing: 2) { + Text(title) + .font(.caption.weight(.semibold)) + Text(detail) + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } + } + } +} + +private struct DetailSection: View { + let title: String + let systemImage: String + @ViewBuilder let content: Content + + var body: some View { + VStack(alignment: .leading, spacing: 10) { + Label(title, systemImage: systemImage) + .font(.subheadline.weight(.semibold)) + .foregroundStyle(.primary) + + VStack(alignment: .leading, spacing: 10) { + content + } + .frame(maxWidth: .infinity, alignment: .leading) + } + } +} + +private struct DetailGrid: View { + @ViewBuilder let content: Content + private let columns = [ + GridItem(.adaptive(minimum: 82, maximum: 160), spacing: 8, alignment: .topLeading) + ] + + var body: some View { + LazyVGrid(columns: columns, alignment: .leading, spacing: 8) { + content + } + } +} + +private struct FlowTagList: View { + let tags: [DevProcessTag] + + var body: some View { + HStack(spacing: 6) { + ForEach(tags) { tag in + ProcessTagBadge(tag: tag) + } + } + } +} + +private struct WorkflowContextCard: View { + let workflow: DevWorkflow + let workflowNote: String? + + var body: some View { + VStack(alignment: .leading, spacing: 10) { + HStack(alignment: .top, spacing: 10) { + Image(systemName: workflow.kind.symbolName) + .font(.system(size: 17, weight: .semibold)) + .foregroundStyle(workflowColor) + .frame(width: 24) + + VStack(alignment: .leading, spacing: 4) { + Text(workflow.title) + .font(.headline) + .lineLimit(1) + Text(workflow.summary) + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } + + Spacer() + + Text(workflow.risk.rawValue) + .font(.caption.weight(.semibold)) + .foregroundStyle(riskColor) + } + + if let workflowNote { + Label(workflowNote, systemImage: "sparkles") + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } + + } + .padding(12) + .softPanel(cornerRadius: 12) + } + + private var workflowColor: Color { + switch workflow.kind { + case .aiMLLab, .localLLMStack: + .pink + case .notebookSession, .dataApp: + .cyan + case .trainingRun: + .orange + case .apiService, .webWorkspace: + .indigo + case .vectorDatabase: + .purple + case .buildWorkspace: + .blue + case .projectWorkspace, .runtimeGroup: + DevScopePalette.accent + } + } + + private var riskColor: Color { + switch workflow.risk { + case .normal: + .secondary + case .busy: + .orange + case .heavy: + .red + } + } +} + +private struct ProcessSafetySummary: View { + let insight: DevProcessInsight + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + Label("At a glance", systemImage: "lightbulb") + .font(.subheadline.weight(.semibold)) + + VStack(alignment: .leading, spacing: 6) { + InsightLine(title: "Role", value: insight.role) + InsightLine(title: "Impact", value: insight.resourceBehavior) + InsightLine(title: "Context", value: insight.workflowContext) + InsightLine(title: "Safe action", value: insight.safeAction) + } + } + .padding(12) + .frame(maxWidth: .infinity, alignment: .leading) + .softPanel(cornerRadius: 12) + } +} + +private struct InsightLine: View { + let title: String + let value: String + + var body: some View { + VStack(alignment: .leading, spacing: 2) { + Text(title) + .font(.caption2.weight(.semibold)) + .foregroundStyle(.tertiary) + Text(value) + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } + } +} + +private struct DetailHeader: View { + let process: ClassifiedDevProcess + let title: String + + var body: some View { + HStack(alignment: .top, spacing: 12) { + ZStack { + RoundedRectangle(cornerRadius: 13, style: .continuous) + .fill(DevScopePalette.color(for: process.classification.kind).opacity(0.18)) + Image(systemName: process.classification.kind.symbolName) + .font(.system(size: 22, weight: .semibold)) + .foregroundStyle(DevScopePalette.color(for: process.classification.kind)) + } + .frame(width: 48, height: 48) + .devScopeGlass(cornerRadius: 13) + + VStack(alignment: .leading, spacing: 4) { + Text(title) + .font(.title3.weight(.semibold)) + .lineLimit(2) + + Text( + process.classification.displayName == title + ? ProcessPresentation.contextLabel(for: process) : process.classification.displayName + ) + .font(.callout) + .foregroundStyle(.secondary) + .lineLimit(1) + } + + Spacer() + } + } +} + +private struct DetailChip: View { + let label: String + let value: String + + var body: some View { + VStack(alignment: .leading, spacing: 2) { + Text(label) + .font(.caption2.weight(.medium)) + .foregroundStyle(.tertiary) + Text(value) + .font(.caption.monospacedDigit().weight(.semibold)) + .lineLimit(1) + .truncationMode(.middle) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 10) + .padding(.vertical, 7) + .softPanel(cornerRadius: 10) + } +} + +private struct DetailField: View { + let label: String + let value: String + var lineLimit: Int = 4 + + var body: some View { + VStack(alignment: .leading, spacing: 7) { + HStack { + Text(label) + .font(.caption.weight(.semibold)) + .foregroundStyle(.secondary) + + Spacer() + + Button { + copy(value) + } label: { + Image(systemName: "doc.on.doc") + } + .buttonStyle(.borderless) + .accessibilityLabel("Copy \(label.lowercased())") + .accessibilityHint("Copies the \(label.lowercased()) value to the clipboard.") + .help("Copy \(label.lowercased())") + } + + Text(value) + .font(.system(.callout, design: .monospaced)) + .textSelection(.enabled) + .lineLimit(lineLimit) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(11) + .background( + .quaternary.opacity(0.34), in: RoundedRectangle(cornerRadius: 10, style: .continuous)) + } + } + + private func copy(_ value: String) { + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(value, forType: .string) + } +} + +private struct ArgumentTokenList: View { + let arguments: [String] + private let maxVisibleArguments = 18 + + var body: some View { + VStack(alignment: .leading, spacing: 7) { + Text("Arguments") + .font(.caption.weight(.semibold)) + .foregroundStyle(.secondary) + + FlowLayout(spacing: 6) { + ForEach(Array(arguments.prefix(maxVisibleArguments).enumerated()), id: \.offset) { + _, argument in + Text(argument) + .font(.caption2.monospaced()) + .lineLimit(1) + .truncationMode(.middle) + .padding(.horizontal, 7) + .padding(.vertical, 4) + .background(.quaternary.opacity(0.34), in: Capsule()) + .help(argument) + } + + if arguments.count > maxVisibleArguments { + Text("+\(arguments.count - maxVisibleArguments)") + .font(.caption2.monospaced().weight(.semibold)) + .foregroundStyle(.secondary) + .padding(.horizontal, 7) + .padding(.vertical, 4) + .background(.quaternary.opacity(0.34), in: Capsule()) + } + } + } + } +} + +private struct FlowLayout: Layout { + var spacing: CGFloat = 6 + + func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { + layout(in: proposal.width ?? 320, subviews: subviews).size + } + + func placeSubviews( + in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout () + ) { + let result = layout(in: bounds.width, subviews: subviews) + for item in result.items { + subviews[item.index].place( + at: CGPoint(x: bounds.minX + item.origin.x, y: bounds.minY + item.origin.y), + proposal: ProposedViewSize(item.size) + ) + } + } + + private func layout(in width: CGFloat, subviews: Subviews) -> ( + items: [(index: Int, origin: CGPoint, size: CGSize)], size: CGSize + ) { + var items: [(index: Int, origin: CGPoint, size: CGSize)] = [] + var x: CGFloat = 0 + var y: CGFloat = 0 + var rowHeight: CGFloat = 0 + let availableWidth = max(width, 1) + + for index in subviews.indices { + let measuredSize = subviews[index].sizeThatFits( + ProposedViewSize(width: availableWidth, height: nil) + ) + let size = CGSize( + width: min(measuredSize.width, availableWidth), + height: measuredSize.height + ) + if x > 0, x + size.width > availableWidth { + x = 0 + y += rowHeight + spacing + rowHeight = 0 + } + + items.append((index: index, origin: CGPoint(x: x, y: y), size: size)) + x += size.width + spacing + rowHeight = max(rowHeight, size.height) + } + + return (items, CGSize(width: availableWidth, height: y + rowHeight)) + } +} diff --git a/Sources/DevScope/Views/ProcessHierarchyView.swift b/Sources/DevScope/Views/ProcessHierarchyView.swift new file mode 100644 index 0000000..b59167a --- /dev/null +++ b/Sources/DevScope/Views/ProcessHierarchyView.swift @@ -0,0 +1,69 @@ +import DevScopeCore +import SwiftUI + +struct ProcessHierarchyView: View { + let nodes: [ProcessHierarchyNode] + @Binding var selection: Int32? + let displayName: (ClassifiedDevProcess) -> String + + var body: some View { + ZStack { + List(selection: $selection) { + OutlineGroup(nodes, children: \.outlineChildren) { node in + hierarchyRow(node) + .tag(node.id) + } + } + .listStyle(.inset) + + if nodes.isEmpty { + ContentUnavailableView( + "No matching hierarchy", + systemImage: "point.3.connected.trianglepath.dotted", + description: Text("Try a different search or refresh the process snapshot.") + ) + } + } + } + + private func hierarchyRow(_ node: ProcessHierarchyNode) -> some View { + let item = node.item + + return HStack(spacing: 10) { + Image(systemName: item.classification.kind.symbolName) + .font(.caption.weight(.semibold)) + .foregroundStyle(DevScopePalette.color(for: item.classification.kind)) + .frame(width: 18) + + VStack(alignment: .leading, spacing: 2) { + Text(displayName(item)) + .font(.callout) + .lineLimit(1) + HStack(spacing: 8) { + Text("PID \(item.process.pid)") + Text("PPID \(item.process.parentPID)") + if !node.children.isEmpty { + Text("\(node.children.count) child\(node.children.count == 1 ? "" : "ren")") + } + } + .font(.caption.monospacedDigit()) + .foregroundStyle(.secondary) + } + + Spacer(minLength: 12) + + Text(ProcessMetricFormat.cpu(item.process.resourceUsage?.cpuPercent)) + .font(.callout.monospacedDigit()) + .frame(width: 76, alignment: .trailing) + Text(ProcessMetricFormat.memory(item.process.resourceUsage?.residentMemoryBytes)) + .font(.callout.monospacedDigit()) + .frame(width: 96, alignment: .trailing) + } + .padding(.vertical, 3) + .accessibilityElement(children: .combine) + .accessibilityLabel("\(displayName(item)), PID \(item.process.pid)") + .accessibilityValue( + "\(node.children.count) direct children, \(ProcessMetricFormat.cpu(item.process.resourceUsage?.cpuPercent)), \(ProcessMetricFormat.memory(item.process.resourceUsage?.residentMemoryBytes))" + ) + } +} diff --git a/Sources/DevScope/Views/ProcessMetricHistoryView.swift b/Sources/DevScope/Views/ProcessMetricHistoryView.swift new file mode 100644 index 0000000..7935d10 --- /dev/null +++ b/Sources/DevScope/Views/ProcessMetricHistoryView.swift @@ -0,0 +1,171 @@ +import DevScopeCore +import SwiftUI + +struct ProcessMetricHistoryView: View { + let samples: [DevProcessMetricSample] + + var body: some View { + VStack(alignment: .leading, spacing: 10) { + HStack { + Text("Behavior") + .font(.headline) + Spacer() + Text(windowText) + .font(.caption.monospacedDigit()) + .foregroundStyle(.tertiary) + } + + MetricChart(samples: samples) + .frame(height: 120) + + HStack(spacing: 12) { + LegendSwatch(color: .cyan, label: "CPU") + LegendSwatch(color: .purple, label: "Memory") + if hasGPUSamples { + LegendSwatch(color: .orange, label: "GPU") + } + Spacer() + Text(summaryText) + .font(.caption.monospacedDigit()) + .foregroundStyle(.secondary) + .lineLimit(1) + } + } + .padding(12) + .softPanel(cornerRadius: 12) + } + + private var windowText: String { + guard let first = samples.first?.timestamp, + let last = samples.last?.timestamp, + last > first + else { + return "collecting" + } + + return "\(Int(last.timeIntervalSince(first)))s" + } + + private var summaryText: String { + guard let latest = samples.last else { + return "No samples yet" + } + + return + "\(ProcessMetricFormat.cpu(latest.cpuPercent)) · \(ProcessMetricFormat.memory(latest.residentMemoryBytes)) · GPU \(ProcessMetricFormat.gpu(latest.gpuPercent))" + } + + private var hasGPUSamples: Bool { + samples.contains { $0.gpuPercent != nil } + } +} + +private struct MetricChart: View { + let samples: [DevProcessMetricSample] + + var body: some View { + Canvas { context, size in + let rect = CGRect(origin: .zero, size: size).insetBy(dx: 8, dy: 8) + drawGrid(in: rect, context: &context) + + guard samples.count > 1 else { + return + } + + let cpuMax = max(100, samples.map(\.cpuPercent).max() ?? 100) + let memoryMax = max(1, samples.map(\.residentMemoryBytes).max() ?? 1) + + context.stroke( + path(for: samples.map(\.cpuPercent), maxValue: cpuMax, in: rect), + with: .color(.cyan), + style: StrokeStyle(lineWidth: 2.2, lineCap: .round, lineJoin: .round) + ) + context.stroke( + path( + for: samples.map { Double($0.residentMemoryBytes) }, maxValue: Double(memoryMax), in: rect + ), + with: .color(.purple), + style: StrokeStyle(lineWidth: 2.2, lineCap: .round, lineJoin: .round) + ) + + let gpuValues = samples.compactMap(\.gpuPercent) + if gpuValues.count == samples.count { + context.stroke( + path(for: gpuValues, maxValue: 100, in: rect), + with: .color(.orange), + style: StrokeStyle(lineWidth: 2.2, lineCap: .round, lineJoin: .round) + ) + } + } + .background( + .quaternary.opacity(0.24), in: RoundedRectangle(cornerRadius: 10, style: .continuous) + ) + .overlay { + if samples.count < 2 { + Text("Collecting live samples") + .font(.caption) + .foregroundStyle(.secondary) + } + } + .accessibilityElement(children: .ignore) + .accessibilityLabel("Process resource history") + .accessibilityValue(accessibilitySummary) + } + + private var accessibilitySummary: String { + guard let first = samples.first, let last = samples.last else { + return "No samples yet" + } + let cpuValues = samples.map(\.cpuPercent) + let memoryValues = samples.map(\.residentMemoryBytes) + let duration = max(0, Int(last.timestamp.timeIntervalSince(first.timestamp))) + return + "\(samples.count) samples over \(duration) seconds. CPU from \(ProcessMetricFormat.cpu(cpuValues.min())) to \(ProcessMetricFormat.cpu(cpuValues.max())). Memory from \(ProcessMetricFormat.memory(memoryValues.min())) to \(ProcessMetricFormat.memory(memoryValues.max()))." + } + + private func drawGrid(in rect: CGRect, context: inout GraphicsContext) { + var grid = Path() + for fraction in stride(from: 0.25, through: 0.75, by: 0.25) { + let y = rect.minY + rect.height * fraction + grid.move(to: CGPoint(x: rect.minX, y: y)) + grid.addLine(to: CGPoint(x: rect.maxX, y: y)) + } + context.stroke(grid, with: .color(.secondary.opacity(0.18)), lineWidth: 1) + } + + private func path(for values: [Double], maxValue: Double, in rect: CGRect) -> Path { + var path = Path() + let denominator = max(1, values.count - 1) + + for (index, value) in values.enumerated() { + let x = rect.minX + rect.width * CGFloat(index) / CGFloat(denominator) + let normalized = max(0, min(1, value / maxValue)) + let y = rect.maxY - rect.height * CGFloat(normalized) + let point = CGPoint(x: x, y: y) + + if index == 0 { + path.move(to: point) + } else { + path.addLine(to: point) + } + } + + return path + } +} + +private struct LegendSwatch: View { + let color: Color + let label: String + + var body: some View { + HStack(spacing: 5) { + Circle() + .fill(color) + .frame(width: 7, height: 7) + Text(label) + .font(.caption) + .foregroundStyle(.secondary) + } + } +} diff --git a/Sources/DevScope/Views/ProcessScopePicker.swift b/Sources/DevScope/Views/ProcessScopePicker.swift new file mode 100644 index 0000000..0ca75c7 --- /dev/null +++ b/Sources/DevScope/Views/ProcessScopePicker.swift @@ -0,0 +1,22 @@ +import DevScopeCore +import SwiftUI + +struct ProcessScopePicker: View { + @Binding var selection: ProcessActivityScope + + var body: some View { + Picker("Activity scope", selection: $selection) { + ForEach(ProcessActivityScope.allCases) { scope in + Label(scope.title, systemImage: scope.symbolName) + .tag(scope) + } + } + .pickerStyle(.segmented) + .controlSize(.small) + .fixedSize() + .accessibilityHint( + "Switches between application families, flat OS processes, parent-child hierarchy, and project workflows." + ) + .help("Choose how running OS processes are grouped") + } +} diff --git a/Sources/DevScope/Views/ProcessSelectionPolicy.swift b/Sources/DevScope/Views/ProcessSelectionPolicy.swift new file mode 100644 index 0000000..8c96223 --- /dev/null +++ b/Sources/DevScope/Views/ProcessSelectionPolicy.swift @@ -0,0 +1,36 @@ +import DevScopeCore + +struct ProcessSelectionResolution: Equatable { + let selectedProcessID: Int32? + let retainedProcess: ClassifiedDevProcess? +} + +enum ProcessSelectionPolicy { + static func reconcile( + selectedProcessID: Int32?, + retainedProcess: ClassifiedDevProcess?, + currentProcesses: [ClassifiedDevProcess] + ) -> ProcessSelectionResolution { + guard let selectedProcessID else { + return ProcessSelectionResolution(selectedProcessID: nil, retainedProcess: nil) + } + guard let current = currentProcesses.first(where: { $0.process.pid == selectedProcessID }) else { + return ProcessSelectionResolution( + selectedProcessID: selectedProcessID, + retainedProcess: retainedProcess + ) + } + if let retainedProcess, + retainedProcess.process.pid == selectedProcessID { + guard let retainedBirth = retainedProcess.process.birthToken, + let currentBirth = current.process.birthToken, + retainedBirth == currentBirth else { + return ProcessSelectionResolution(selectedProcessID: nil, retainedProcess: nil) + } + } + return ProcessSelectionResolution( + selectedProcessID: selectedProcessID, + retainedProcess: current + ) + } +} diff --git a/Sources/DevScope/Views/ProcessStatsPanelView.swift b/Sources/DevScope/Views/ProcessStatsPanelView.swift new file mode 100644 index 0000000..c90b932 --- /dev/null +++ b/Sources/DevScope/Views/ProcessStatsPanelView.swift @@ -0,0 +1,1199 @@ +import DevScopeCore +import Foundation +import SwiftUI + +private enum SystemLoadFormat { + static var cpuCapacity: Double { + max(1, Double(ProcessInfo.processInfo.activeProcessorCount) * 100) + } + + static func cpuCapacityPercent(_ aggregateCPU: Double?) -> String { + guard let aggregateCPU else { + return "-" + } + + return "\(Int((cpuLoadFraction(aggregateCPU) * 100).rounded()))% cap" + } + + static func cpuLoadPercent(_ aggregateCPU: Double) -> Double { + cpuLoadFraction(aggregateCPU) * 100 + } + + static func cpuLoadFraction(_ aggregateCPU: Double) -> Double { + min(max(aggregateCPU / cpuCapacity, 0), 1) + } + + static func pressureScore(for sample: DevProcessMetricSample?) -> Double { + guard let sample else { + return 0 + } + + let cpuLoad = cpuLoadFraction(sample.cpuPercent) + let gpuLoad = min(max((sample.gpuPercent ?? 0) / 100, 0), 1) + let memoryLoad = min( + max(Double(sample.residentMemoryBytes) / Double(ProcessInfo.processInfo.physicalMemory), 0), 1 + ) + let weighted = cpuLoad * 0.40 + gpuLoad * 0.35 + memoryLoad * 0.25 + + return min(max(max(weighted, max(cpuLoad, gpuLoad, memoryLoad) * 0.72), 0), 1) + } + + static func pressureLabel(for score: Double) -> String { + switch score { + case 0..<0.25: + "Low" + case 0.25..<0.50: + "Normal" + case 0.50..<0.75: + "Busy" + case 0.75..<0.90: + "Heavy" + default: + "Critical" + } + } + + static func pressureTint(for score: Double) -> Color { + switch score { + case 0..<0.25: + .green + case 0.25..<0.50: + DevScopePalette.accent + case 0.50..<0.75: + .orange + default: + .red + } + } +} + +enum LiveNotchPresentationState: Equatable { + case collapsed + case compact + case expanded + + var isVisible: Bool { + self != .collapsed + } + + var isExpanded: Bool { + self == .expanded + } +} + +struct DynamicIslandStatusView: View { + @Environment(\.accessibilityReduceMotion) private var reduceMotion + let stats: ProcessDashboardStats + let metricHistory: [DevProcessMetricSample] + let selectedScope: String + let isRefreshing: Bool + let lastRefresh: Date? + let refreshAction: @MainActor () -> Void + let displayName: @MainActor (ClassifiedDevProcess) -> String + @ObservedObject var presentationModel: LiveNotchPresentationModel + @State private var isSystemStatsPresented = false + + private var presentationAnimation: Animation? { + reduceMotion ? nil : .spring(response: 0.28, dampingFraction: 0.86) + } + + private var islandTransition: AnyTransition { + if reduceMotion { + return .opacity + } + + return .asymmetric( + insertion: .opacity.combined(with: .move(edge: .top)).combined( + with: .scale(scale: 0.96, anchor: .top)), + removal: .opacity.combined(with: .move(edge: .top)) + ) + } + + private var expandedContentTransition: AnyTransition { + reduceMotion ? .opacity : .opacity.combined(with: .move(edge: .top)) + } + + private var isVisible: Bool { + presentationModel.state.isVisible + } + + private var isExpanded: Bool { + presentationModel.state.isExpanded + } + + var body: some View { + ZStack(alignment: .top) { + notchHoverTarget + + ZStack(alignment: .top) { + if isVisible { + islandSurface + .padding(.top, 5) + .transition(islandTransition) + } + } + .animation(presentationAnimation, value: presentationModel.state) + } + .frame( + width: isVisible ? (isExpanded ? 780 : 640) : 128, + height: isVisible ? (isExpanded ? 178 : 52) : 18, alignment: .top + ) + .sheet(isPresented: $isSystemStatsPresented) { + SystemStatsSheet(samples: metricHistory, runtimeCounts: stats.runtimeCounts) + } + } + + private var notchHoverTarget: some View { + Capsule() + .fill(isVisible ? Color.primary.opacity(0.10) : Color.black.opacity(0.001)) + .overlay { + Capsule() + .stroke(isVisible ? Color.white.opacity(0.10) : Color.clear, lineWidth: 1) + } + .frame(width: 112, height: 12) + .contentShape(Capsule()) + .accessibilityLabel("DevScope live notch") + .accessibilityHint("Hover to reveal live process status.") + .help("Hover to reveal DevScope Live") + } + + private var islandSurface: some View { + VStack(alignment: .leading, spacing: isExpanded ? 10 : 0) { + compactRow + .contentShape(Capsule()) + + if isExpanded { + expandedContent + .transition(expandedContentTransition) + } + } + .padding(.horizontal, isExpanded ? 14 : 12) + .padding(.vertical, isExpanded ? 12 : 9) + .frame(width: isExpanded ? 760 : 620, alignment: .leading) + .background { + RoundedRectangle(cornerRadius: isExpanded ? 30 : 24, style: .continuous) + .fill(.black.opacity(0.70)) + .background( + .regularMaterial, + in: RoundedRectangle(cornerRadius: isExpanded ? 30 : 24, style: .continuous) + ) + .overlay { + RoundedRectangle(cornerRadius: isExpanded ? 30 : 24, style: .continuous) + .stroke(.white.opacity(isExpanded ? 0.16 : 0.12), lineWidth: 1) + } + .overlay(alignment: .top) { + Capsule() + .fill(.white.opacity(isExpanded ? 0.16 : 0.10)) + .frame(width: isExpanded ? 58 : 44, height: 2) + .padding(.top, 4) + } + } + .overlay { + RoundedRectangle(cornerRadius: isExpanded ? 30 : 24, style: .continuous) + .stroke(pressureTint.opacity(isExpanded ? 0.16 : 0.10), lineWidth: 1) + .blendMode(.plusLighter) + } + .shadow(color: .black.opacity(0.28), radius: isExpanded ? 20 : 14, y: isExpanded ? 10 : 7) + .animation(presentationAnimation, value: isExpanded) + .accessibilityElement(children: .combine) + .accessibilityLabel("Live DevScope status") + .accessibilityValue(accessibilityStatus) + .help("Hover the top-center notch to reveal live status.") + } + + private var compactRow: some View { + HStack(spacing: 8) { + LivePulseDot(tint: pressureTint, isActive: isRefreshing || stats.totalCount > 0) + + Text("DevScope") + .font(.callout.weight(.semibold)) + .foregroundStyle(.white) + .lineLimit(1) + .layoutPriority(3) + + Text("\(stats.totalCount)") + .font(.callout.monospacedDigit().weight(.bold)) + .foregroundStyle(DevScopePalette.accent) + .padding(.horizontal, 8) + .padding(.vertical, 3) + .background(DevScopePalette.accent.opacity(0.18), in: Capsule()) + + Text(selectedScope) + .font(.caption.weight(.medium)) + .foregroundStyle(.white.opacity(0.70)) + .lineLimit(1) + .truncationMode(.tail) + .frame(maxWidth: 110, alignment: .leading) + .layoutPriority(1) + + Spacer(minLength: 8) + + LiveIslandMetricLabel(title: "CPU", value: cpuText, tint: .orange) + LiveIslandMetricLabel(title: "GPU", value: gpuText, tint: .green) + LiveIslandMetricLabel(title: "MEM", value: memoryText, tint: .cyan) + + Label(pressureLabel, systemImage: "gauge.with.dots.needle.67percent") + .font(.caption.weight(.semibold)) + .foregroundStyle(pressureTint) + .labelStyle(.titleAndIcon) + .lineLimit(1) + .layoutPriority(1) + + Image(systemName: isExpanded ? "chevron.up" : "chevron.down") + .font(.caption.weight(.bold)) + .foregroundStyle(.white.opacity(0.55)) + } + } + + private var expandedContent: some View { + VStack(alignment: .leading, spacing: 10) { + Divider() + .overlay(.white.opacity(0.14)) + + HStack(alignment: .center, spacing: 12) { + VStack(alignment: .leading, spacing: 8) { + HStack(spacing: 8) { + GraphLegendItem(title: "CPU", color: .orange) + GraphLegendItem(title: "GPU", color: .green) + GraphLegendItem(title: "Memory", color: .cyan) + Spacer() + Text(lastRefreshText) + .font(.caption2.monospacedDigit()) + .foregroundStyle(.white.opacity(0.46)) + } + + LiveIslandSparkline(samples: metricHistory) + .frame(height: 42) + } + .frame(minWidth: 300, maxWidth: .infinity, alignment: .leading) + + VStack(alignment: .leading, spacing: 8) { + LiveIslandTopProcessRow( + title: "Top Memory", process: stats.topMemory, displayName: displayName) + } + .frame(width: 170, alignment: .leading) + + VStack(spacing: 8) { + LiveIslandActionButton( + title: "Refresh", symbolName: "arrow.clockwise", tint: DevScopePalette.accent + ) { + refreshAction() + } + + LiveIslandActionButton(title: "Inspect", symbolName: "chart.xyaxis.line", tint: .orange) { + isSystemStatsPresented = true + } + } + .frame(width: 112) + } + } + } + + private var latest: DevProcessMetricSample? { + metricHistory.last + } + + private var cpuText: String { + SystemLoadFormat.cpuCapacityPercent(latest?.cpuPercent) + } + + private var gpuText: String { + ProcessMetricFormat.gpu(latest?.gpuPercent) + } + + private var memoryText: String { + ProcessMetricFormat.memory(latest?.residentMemoryBytes) + } + + private var pressureScore: Double { + SystemLoadFormat.pressureScore(for: latest) + } + + private var pressureLabel: String { + switch pressureScore { + case 0..<0.25: + "Low" + case 0.25..<0.50: + "Normal" + case 0.50..<0.75: + "Busy" + case 0.75..<0.90: + "Heavy" + default: + "Critical" + } + } + + private var pressureTint: Color { + switch pressureScore { + case 0..<0.25: + .green + case 0.25..<0.50: + DevScopePalette.accent + case 0.50..<0.75: + .orange + default: + .red + } + } + + private var lastRefreshText: String { + guard let lastRefresh else { + return isRefreshing ? "collecting" : "waiting" + } + + return "updated \(lastRefresh.formatted(date: .omitted, time: .standard))" + } + + private var accessibilityStatus: String { + "\(stats.totalCount) running items, CPU \(cpuText), GPU \(gpuText), memory \(memoryText), machine load \(pressureLabel)" + } +} + +private struct LivePulseDot: View { + @Environment(\.accessibilityReduceMotion) private var reduceMotion + let tint: Color + let isActive: Bool + + var body: some View { + Group { + if reduceMotion { + pulse(phase: 0) + } else { + TimelineView(.animation(minimumInterval: 0.8)) { timeline in + let phase = + isActive + ? timeline.date.timeIntervalSinceReferenceDate.truncatingRemainder(dividingBy: 1.6) + / 1.6 : 0 + pulse(phase: phase) + } + } + } + .frame(width: 22, height: 22) + } + + private func pulse(phase: Double) -> some View { + ZStack { + Circle() + .fill(tint.opacity(0.24)) + .frame(width: 20, height: 20) + .scaleEffect(1 + phase * 0.55) + .opacity(isActive ? 1 - phase : 0.35) + + Circle() + .fill(tint) + .frame(width: 8, height: 8) + .shadow(color: tint.opacity(0.55), radius: 8) + } + } +} + +private struct LiveIslandMetricLabel: View { + let title: String + let value: String + let tint: Color + + var body: some View { + HStack(spacing: 4) { + Circle() + .fill(tint) + .frame(width: 6, height: 6) + Text(title) + .foregroundStyle(.white.opacity(0.48)) + Text(value) + .foregroundStyle(.white.opacity(0.82)) + .monospacedDigit() + } + .font(.caption2.weight(.semibold)) + .lineLimit(1) + .fixedSize(horizontal: true, vertical: false) + } +} + +private struct LiveIslandSparkline: View { + let samples: [DevProcessMetricSample] + + var body: some View { + Canvas { context, size in + drawGrid(context: &context, size: size) + drawLine( + values: samples.map { SystemLoadFormat.cpuLoadPercent($0.cpuPercent) }, + maxValue: 100, + color: .orange, + context: &context, + size: size + ) + drawLine( + values: samples.map { $0.gpuPercent ?? 0 }, + maxValue: 100, + color: .green, + context: &context, + size: size + ) + drawLine( + values: samples.map { Double($0.residentMemoryBytes) }, + maxValue: max(1, Double(samples.map(\.residentMemoryBytes).max() ?? 1)), + color: .cyan, + context: &context, + size: size + ) + } + .background(.white.opacity(0.055), in: RoundedRectangle(cornerRadius: 12, style: .continuous)) + .overlay { + RoundedRectangle(cornerRadius: 12, style: .continuous) + .stroke(.white.opacity(0.10), lineWidth: 1) + } + } + + private func drawGrid(context: inout GraphicsContext, size: CGSize) { + var path = Path() + for fraction in [0.33, 0.66] { + let y = size.height * fraction + path.move(to: CGPoint(x: 0, y: y)) + path.addLine(to: CGPoint(x: size.width, y: y)) + } + context.stroke(path, with: .color(.white.opacity(0.08)), lineWidth: 1) + } + + private func drawLine( + values: [Double], + maxValue: Double, + color: Color, + context: inout GraphicsContext, + size: CGSize + ) { + guard values.count > 1, maxValue > 0 else { + return + } + + var path = Path() + for index in values.indices { + let progress = Double(index) / Double(values.count - 1) + let normalizedValue = min(max(values[index] / maxValue, 0), 1) + let point = CGPoint( + x: progress * size.width, + y: size.height - normalizedValue * size.height + ) + if index == values.startIndex { + path.move(to: point) + } else { + path.addLine(to: point) + } + } + + context.stroke(path, with: .color(color.opacity(0.88)), lineWidth: 1.6) + } +} + +private struct LiveIslandTopProcessRow: View { + let title: String + let process: ClassifiedDevProcess? + let displayName: (ClassifiedDevProcess) -> String + + var body: some View { + VStack(alignment: .leading, spacing: 2) { + Text(title) + .font(.caption2.weight(.medium)) + .foregroundStyle(.white.opacity(0.46)) + Text(process.map(displayName) ?? "No process") + .font(.caption.weight(.semibold)) + .foregroundStyle(.white.opacity(0.84)) + .lineLimit(1) + .truncationMode(.tail) + } + } +} + +private struct LiveIslandActionButton: View { + let title: String + let symbolName: String + let tint: Color + let action: () -> Void + + var body: some View { + Button(action: action) { + Label(title, systemImage: symbolName) + .font(.caption.weight(.semibold)) + .lineLimit(1) + .frame(maxWidth: .infinity, minHeight: 28) + } + .buttonStyle(.plain) + .foregroundStyle(tint) + .background(tint.opacity(0.14), in: Capsule()) + .overlay { + Capsule() + .stroke(tint.opacity(0.26), lineWidth: 1) + } + } +} + +struct LiveActivitiesDockView: View { + @Environment(\.accessibilityReduceMotion) private var reduceMotion + let stats: ProcessDashboardStats + let metricHistory: [DevProcessMetricSample] + let isRefreshing: Bool + let lastRefresh: Date? + @Binding var isExpanded: Bool + @State private var isSystemStatsPresented = false + + var body: some View { + GeometryReader { proxy in + let mode = LiveActivityLayoutPolicy.mode(availableWidth: proxy.size.width) + let verticalMode = LiveActivityLayoutPolicy.verticalMode( + availableHeight: proxy.size.height, + layoutMode: mode + ) + + VStack(alignment: .leading, spacing: isExpanded ? 10 : 0) { + responsiveHeader + + if isExpanded { + expandedMetrics(verticalMode: verticalMode) + .transition(reduceMotion ? .opacity : .opacity.combined(with: .move(edge: .bottom))) + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .padding(12) + .devScopeGlass(cornerRadius: 16) + } + .animation(reduceMotion ? nil : .snappy(duration: 0.18), value: isExpanded) + .sheet(isPresented: $isSystemStatsPresented) { + SystemStatsSheet(samples: metricHistory, runtimeCounts: stats.runtimeCounts) + } + } + + private var responsiveHeader: some View { + ViewThatFits(in: .horizontal) { + wideHeader + compactHeader + } + } + + private var wideHeader: some View { + HStack(spacing: 12) { + headerIdentity + + Spacer(minLength: 8) + + compactMetricSummary + loadIndicator + expansionButton + } + } + + private var compactHeader: some View { + HStack(spacing: 8) { + headerIdentity + + Spacer(minLength: 4) + + loadIndicator + expansionButton + } + } + + private var headerIdentity: some View { + HStack(spacing: 12) { + Label("Live Activity", systemImage: "dock.rectangle") + .font(.callout.weight(.semibold)) + .foregroundStyle(.primary) + .lineLimit(1) + + Text(statusText) + .font(.caption.monospacedDigit()) + .foregroundStyle(.secondary) + .lineLimit(1) + } + } + + private var compactMetricSummary: some View { + HStack(spacing: 12) { + CompactLiveMetric( + title: "CPU", + value: SystemLoadFormat.cpuCapacityPercent(latestSample?.cpuPercent), + color: .orange + ) + CompactLiveMetric( + title: "GPU", + value: latestSample?.gpuPercent.map(ProcessMetricFormat.gpu) ?? "-", + color: .green + ) + CompactLiveMetric( + title: "Memory", + value: ProcessMetricFormat.memory(latestSample?.residentMemoryBytes), + color: .cyan + ) + } + } + + private var loadIndicator: some View { + Label(loadLabel, systemImage: "gauge.with.dots.needle.67percent") + .font(.caption.weight(.semibold)) + .foregroundStyle(loadTint) + .lineLimit(1) + } + + private var expansionButton: some View { + Button { + isExpanded.toggle() + } label: { + Image(systemName: isExpanded ? "chevron.down" : "chevron.up") + .frame(width: 20, height: 20) + } + .buttonStyle(.borderless) + .accessibilityLabel(isExpanded ? "Hide live activity details" : "Show live activity details") + .accessibilityHint("Toggles the detailed CPU, GPU, and memory history graph.") + .help(isExpanded ? "Hide live activity details" : "Show live activity details") + } + + @ViewBuilder + private func expandedMetrics( + verticalMode: LiveActivityVerticalMode + ) -> some View { + historyGraph(density: verticalMode == .condensed ? .condensed : .standard) + } + + private func historyGraph( + density: DashboardMetricGraphDensity = .standard + ) -> some View { + DashboardMetricGraph( + samples: metricHistory, + density: density, + showsLegend: false, + showsInspectButton: false, + showsSummary: false, + fillsAvailableHeight: true + ) { + isSystemStatsPresented = true + } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) + } + + private var statusText: String { + guard let lastRefresh else { + return isRefreshing ? "collecting" : "waiting" + } + + return "updated \(lastRefresh.formatted(date: .omitted, time: .standard))" + } + + private var latestSample: DevProcessMetricSample? { + metricHistory.last + } + + private var loadScore: Double { + SystemLoadFormat.pressureScore(for: latestSample) + } + + private var loadLabel: String { + SystemLoadFormat.pressureLabel(for: loadScore) + } + + private var loadTint: Color { + SystemLoadFormat.pressureTint(for: loadScore) + } +} + +private struct CompactLiveMetric: View { + let title: String + let value: String + let color: Color + + var body: some View { + HStack(spacing: 4) { + Circle() + .fill(color) + .frame(width: 5, height: 5) + Text("\(title) \(value)") + .font(.caption.monospacedDigit()) + .foregroundStyle(.secondary) + .lineLimit(1) + } + } +} + +private enum DashboardMetricGraphDensity { + case standard + case condensed +} + +private struct DashboardMetricGraph: View { + @Environment(\.accessibilityReduceMotion) private var reduceMotion + let samples: [DevProcessMetricSample] + var density: DashboardMetricGraphDensity = .standard + var showsLegend = true + var showsInspectButton = true + var showsSummary = true + var fillsAvailableHeight = false + let action: () -> Void + @State private var hoverLocation: CGPoint? + + private func selectedSample(width: CGFloat? = nil) -> DevProcessMetricSample? { + guard let hoverLocation, samples.count > 1 else { + return samples.last + } + + let graphWidth = max(1, width ?? 1) + let progress = min(max(hoverLocation.x / graphWidth, 0), 1) + let index = min( + samples.count - 1, max(0, Int((progress * CGFloat(samples.count - 1)).rounded()))) + return samples[index] + } + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + if density == .standard, showsLegend { + HStack(spacing: 12) { + GraphLegendItem(title: "CPU cap", color: .orange) + GraphLegendItem(title: "GPU", color: .green) + GraphLegendItem(title: "Memory", color: .cyan) + Text("Running activity") + .font(.caption2.weight(.medium)) + .foregroundStyle(.tertiary) + if showsInspectButton { + Button(action: action) { + Label("Inspect", systemImage: "arrow.up.left.and.arrow.down.right") + .font(.caption2.weight(.semibold)) + .labelStyle(.titleAndIcon) + } + .buttonStyle(.bordered) + .controlSize(.small) + .accessibilityLabel("Open system statistics") + .accessibilityHint("Opens the detailed system statistics sheet.") + .help("Open system statistics") + } + Spacer() + Text(windowText) + .font(.caption2.monospacedDigit()) + .foregroundStyle(.tertiary) + } + } + + Button(action: action) { + Canvas { context, size in + drawGrid(context: &context, size: size) + drawLine( + values: samples.map { SystemLoadFormat.cpuLoadPercent($0.cpuPercent) }, + maxValue: 100, + color: .orange, + context: &context, + size: size + ) + drawLine( + values: samples.map { $0.gpuPercent ?? 0 }, + maxValue: 100, + color: .green, + context: &context, + size: size + ) + drawLine( + values: samples.map { Double($0.residentMemoryBytes) }, + maxValue: max(1, Double(samples.map(\.residentMemoryBytes).max() ?? 1)), + color: .cyan, + context: &context, + size: size + ) + drawHover(context: &context, size: size) + } + .frame( + minHeight: graphMinimumHeight, + maxHeight: fillsAvailableHeight ? .infinity : graphMinimumHeight + ) + .background( + .black.opacity(0.13), in: RoundedRectangle(cornerRadius: 10, style: .continuous) + ) + .overlay(alignment: .topTrailing) { + if showsSummary { + Text(summaryText) + .font(.caption2.monospacedDigit()) + .foregroundStyle(.secondary) + .padding(.horizontal, 8) + .padding(.vertical, 5) + } + } + .overlay(alignment: .topLeading) { + GeometryReader { proxy in + if hoverLocation != nil, let sample = selectedSample(width: proxy.size.width) { + GraphHoverTooltip(sample: sample) + .padding(8) + .transition(reduceMotion ? .identity : .opacity.combined(with: .scale(scale: 0.98))) + } + } + } + .onContinuousHover { phase in + switch phase { + case .active(let location): + hoverLocation = location + case .ended: + hoverLocation = nil + } + } + } + .buttonStyle(.plain) + .accessibilityLabel("Open system statistics") + .accessibilityHint(summaryText) + .help("Open system statistics") + } + .frame( + maxHeight: fillsAvailableHeight ? .infinity : nil, + alignment: .topLeading + ) + } + + private var graphMinimumHeight: CGFloat { + density == .condensed ? 40 : 52 + } + + private var summaryText: String { + guard let sample = selectedSample() else { + return "waiting" + } + + let gpu = ProcessMetricFormat.gpu(sample.gpuPercent) + return + "CPU \(SystemLoadFormat.cpuCapacityPercent(sample.cpuPercent)) GPU \(gpu) MEM \(ProcessMetricFormat.memory(sample.residentMemoryBytes))" + } + + private var windowText: String { + guard let first = samples.first?.timestamp, + let last = samples.last?.timestamp, + last > first + else { + return "collecting" + } + return "\(Int(last.timeIntervalSince(first)))s" + } + + private func drawGrid(context: inout GraphicsContext, size: CGSize) { + var path = Path() + for fraction in [0.25, 0.5, 0.75] { + let y = size.height * fraction + path.move(to: CGPoint(x: 0, y: y)) + path.addLine(to: CGPoint(x: size.width, y: y)) + } + context.stroke(path, with: .color(.white.opacity(0.08)), lineWidth: 1) + } + + private func drawHover(context: inout GraphicsContext, size: CGSize) { + guard let hoverLocation, samples.count > 1 else { + return + } + + let x = min(max(hoverLocation.x, 0), size.width) + var path = Path() + path.move(to: CGPoint(x: x, y: 0)) + path.addLine(to: CGPoint(x: x, y: size.height)) + context.stroke( + path, with: .color(.white.opacity(0.32)), style: StrokeStyle(lineWidth: 1, dash: [3, 4])) + } + + private func drawLine( + values: [Double], + maxValue: Double, + color: Color, + context: inout GraphicsContext, + size: CGSize + ) { + guard values.count > 1, maxValue > 0 else { + return + } + + var path = Path() + for index in values.indices { + let progress = Double(index) / Double(values.count - 1) + let normalizedValue = min(max(values[index] / maxValue, 0), 1) + let point = CGPoint( + x: progress * size.width, + y: size.height - normalizedValue * size.height + ) + if index == values.startIndex { + path.move(to: point) + } else { + path.addLine(to: point) + } + } + + context.stroke(path, with: .color(color.opacity(0.88)), lineWidth: 1.7) + } +} + +private struct RuntimeMixTile: View { + let runtimeCounts: [DevRuntimeKind: Int] + + var body: some View { + VStack(alignment: .leading, spacing: 7) { + Label("Activity Mix", systemImage: "square.grid.2x2") + .font(.caption) + .foregroundStyle(.secondary) + .lineLimit(1) + + RuntimeMixBar(runtimeCounts: runtimeCounts, height: 8) + + HStack(spacing: 8) { + ForEach(topRuntimes.prefix(3), id: \.key) { kind, count in + HStack(spacing: 4) { + Circle() + .fill(DevScopePalette.color(for: kind)) + .frame(width: 6, height: 6) + Text(kind.rawValue) + .lineLimit(1) + Text("\(count)") + .monospacedDigit() + .foregroundStyle(.tertiary) + } + .font(.caption2) + } + } + } + .frame(width: 168, alignment: .leading) + .padding(10) + .background(.white.opacity(0.045), in: RoundedRectangle(cornerRadius: 12, style: .continuous)) + .overlay { + RoundedRectangle(cornerRadius: 12, style: .continuous) + .stroke(.white.opacity(0.10), lineWidth: 1) + } + } + + private var topRuntimes: [(key: DevRuntimeKind, value: Int)] { + runtimeCounts.sorted { lhs, rhs in + if lhs.value != rhs.value { + return lhs.value > rhs.value + } + return lhs.key.rawValue < rhs.key.rawValue + } + } +} + +struct RuntimeMixBar: View { + let runtimeCounts: [DevRuntimeKind: Int] + let height: CGFloat + + var body: some View { + GeometryReader { proxy in + HStack(spacing: 2) { + ForEach(sortedRuntimes, id: \.key) { kind, count in + RoundedRectangle(cornerRadius: height / 2, style: .continuous) + .fill(DevScopePalette.color(for: kind)) + .frame(width: max(4, proxy.size.width * CGFloat(count) / CGFloat(max(1, totalCount)))) + } + } + } + .frame(height: height) + .accessibilityLabel("Activity mix") + } + + private var sortedRuntimes: [(key: DevRuntimeKind, value: Int)] { + runtimeCounts.sorted { lhs, rhs in + if lhs.value != rhs.value { + return lhs.value > rhs.value + } + return lhs.key.rawValue < rhs.key.rawValue + } + } + + private var totalCount: Int { + runtimeCounts.values.reduce(0, +) + } +} + +private struct GraphHoverTooltip: View { + let sample: DevProcessMetricSample + + var body: some View { + VStack(alignment: .leading, spacing: 3) { + Text(sample.timestamp, style: .time) + .font(.caption2.weight(.semibold)) + Text("CPU \(SystemLoadFormat.cpuCapacityPercent(sample.cpuPercent))") + Text("GPU \(ProcessMetricFormat.gpu(sample.gpuPercent))") + Text("Memory \(ProcessMetricFormat.memory(sample.residentMemoryBytes))") + } + .font(.caption2.monospacedDigit()) + .padding(.horizontal, 8) + .padding(.vertical, 6) + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 8, style: .continuous)) + .overlay { + RoundedRectangle(cornerRadius: 8, style: .continuous) + .stroke(.white.opacity(0.14), lineWidth: 1) + } + } +} + +private struct SystemStatsSheet: View { + let samples: [DevProcessMetricSample] + let runtimeCounts: [DevRuntimeKind: Int] + @Environment(\.dismiss) private var dismiss + + var body: some View { + VStack(alignment: .leading, spacing: 16) { + HStack { + VStack(alignment: .leading, spacing: 3) { + Text("System Statistics") + .font(.title2.weight(.semibold)) + Text("Aggregate live samples across running items plus system GPU utilization.") + .font(.callout) + .foregroundStyle(.secondary) + } + + Spacer() + + Button("Done") { + dismiss() + } + .keyboardShortcut(.defaultAction) + } + + HStack(spacing: 12) { + SummaryPill( + title: "CPU Avg", value: SystemLoadFormat.cpuCapacityPercent(averageCPU), tint: .orange) + SummaryPill(title: "GPU Avg", value: ProcessMetricFormat.gpu(averageGPU), tint: .green) + SummaryPill( + title: "Memory Peak", value: ProcessMetricFormat.memory(peakMemory), tint: .cyan) + SummaryPill(title: "Samples", value: "\(samples.count)", tint: DevScopePalette.accent) + } + + DashboardMetricGraph(samples: samples, showsInspectButton: false) {} + .frame(minHeight: 92) + + VStack(alignment: .leading, spacing: 10) { + Text("Activity Mix") + .font(.headline) + RuntimeMixBar(runtimeCounts: runtimeCounts, height: 10) + RuntimeBreakdown(runtimeCounts: runtimeCounts) + } + + Spacer(minLength: 0) + } + .padding(20) + .frame(width: 720, height: 430) + .background(DevScopeBackground()) + } + + private var averageCPU: Double? { + guard !samples.isEmpty else { + return nil + } + return samples.reduce(0) { $0 + $1.cpuPercent } / Double(samples.count) + } + + private var averageGPU: Double? { + let values = samples.compactMap(\.gpuPercent) + guard !values.isEmpty else { + return nil + } + return values.reduce(0, +) / Double(values.count) + } + + private var peakMemory: Int64? { + samples.map(\.residentMemoryBytes).max() + } +} + +private struct SummaryPill: View { + let title: String + let value: String + let tint: Color + + var body: some View { + VStack(alignment: .leading, spacing: 5) { + Text(title) + .font(.caption) + .foregroundStyle(.secondary) + Text(value) + .font(.callout.monospacedDigit().weight(.semibold)) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(10) + .background(tint.opacity(0.08), in: RoundedRectangle(cornerRadius: 10, style: .continuous)) + .overlay { + RoundedRectangle(cornerRadius: 10, style: .continuous) + .stroke(tint.opacity(0.18), lineWidth: 1) + } + } +} + +private struct RuntimeBreakdown: View { + let runtimeCounts: [DevRuntimeKind: Int] + + var body: some View { + LazyVGrid(columns: [GridItem(.adaptive(minimum: 130), spacing: 8)], spacing: 8) { + ForEach(sortedRuntimes, id: \.key) { kind, count in + HStack(spacing: 7) { + Circle() + .fill(DevScopePalette.color(for: kind)) + .frame(width: 8, height: 8) + Text(kind.rawValue) + .lineLimit(1) + Spacer() + Text("\(count)") + .monospacedDigit() + .foregroundStyle(.secondary) + } + .font(.caption) + .padding(.horizontal, 9) + .padding(.vertical, 7) + .background(.white.opacity(0.04), in: RoundedRectangle(cornerRadius: 9, style: .continuous)) + } + } + } + + private var sortedRuntimes: [(key: DevRuntimeKind, value: Int)] { + runtimeCounts.sorted { lhs, rhs in + if lhs.value != rhs.value { + return lhs.value > rhs.value + } + return lhs.key.rawValue < rhs.key.rawValue + } + } +} + +private struct GraphLegendItem: View { + let title: String + let color: Color + + var body: some View { + HStack(spacing: 5) { + Circle() + .fill(color) + .frame(width: 7, height: 7) + Text(title) + .font(.caption2.weight(.medium)) + .foregroundStyle(.secondary) + } + } +} + +private struct RuntimeStrip: View { + let runtimeCounts: [DevRuntimeKind: Int] + + var body: some View { + ScrollView(.horizontal) { + HStack(spacing: 8) { + ForEach(runtimeCounts.sorted(by: sortRuntime), id: \.key) { kind, count in + HStack(spacing: 5) { + Circle() + .fill(DevScopePalette.color(for: kind)) + .frame(width: 7, height: 7) + Text(kind.rawValue) + .fixedSize(horizontal: true, vertical: false) + Text("\(count)") + .monospacedDigit() + .foregroundStyle(.tertiary) + } + .font(.caption) + } + } + } + .scrollIndicators(.hidden) + .frame(maxWidth: 360, alignment: .trailing) + } + + private func sortRuntime( + _ lhs: (key: DevRuntimeKind, value: Int), + _ rhs: (key: DevRuntimeKind, value: Int) + ) -> Bool { + if lhs.value != rhs.value { + return lhs.value > rhs.value + } + return lhs.key.rawValue < rhs.key.rawValue + } +} diff --git a/Sources/DevScope/Views/ProcessTableView.swift b/Sources/DevScope/Views/ProcessTableView.swift new file mode 100644 index 0000000..529a5e0 --- /dev/null +++ b/Sources/DevScope/Views/ProcessTableView.swift @@ -0,0 +1,414 @@ +import AppKit +import DevScopeCore +import SwiftUI + +struct ProcessTableRow: Identifiable, Equatable, Sendable { + let item: ClassifiedDevProcess + let title: String + let isFavorite: Bool + let isWatched: Bool + let automationBadges: [AutomationProcessBadge] + let snapshotOrder: Int + + var id: Int32 { item.process.pid } + var processName: String { title } + var pid: Int32 { item.process.pid } + var cpuPercent: Double { item.process.resourceUsage?.cpuPercent ?? 0 } + var memoryBytes: Int64 { item.process.resourceUsage?.residentMemoryBytes ?? 0 } + var elapsedTime: String { item.process.resourceUsage?.elapsedTime ?? "-" } + var elapsedSeconds: Int64 { ProcessPresentation.elapsedSeconds(elapsedTime) } + var command: String { item.process.command } + var runtime: String { item.classification.kind.rawValue } + var stableOrderKey: String { + [ + String(format: "%08d", snapshotOrder), + ProcessPresentation.projectName(for: item) ?? "", + title, + item.process.executableName, + "\(item.process.pid)" + ].joined(separator: "\u{1F}") + } +} + +struct ProcessTableView: View { + let rows: [ProcessTableRow] + let emptyTitle: String + let emptyDescription: String + @Binding var selection: Int32? + @Binding var sort: ProcessTableSortState + let copyCommand: (ClassifiedDevProcess) -> Void + @Environment(\.controlActiveState) private var controlActiveState + @State private var hoveredRowID: Int32? + @State private var verticalScrollTargetID: Int32? + @FocusState private var isTableFocused: Bool + + init( + rows: [ProcessTableRow], + emptyTitle: String = "No matching running items", + emptyDescription: String = "Refresh after opening an app, starting a service, running a script, or launching a build watcher.", + selection: Binding, + sort: Binding, + copyCommand: @escaping (ClassifiedDevProcess) -> Void + ) { + self.rows = rows + self.emptyTitle = emptyTitle + self.emptyDescription = emptyDescription + _selection = selection + _sort = sort + self.copyCommand = copyCommand + } + + var body: some View { + ZStack { + ScrollView(.horizontal) { + VStack(spacing: 0) { + header + + ScrollView(.vertical) { + LazyVStack(spacing: 0) { + ForEach(rows) { row in + dataRow(for: row) + .id(row.id) + } + } + .scrollTargetLayout() + } + .scrollPosition(id: $verticalScrollTargetID, anchor: .center) + } + .frame(minWidth: 920, maxWidth: .infinity, alignment: .topLeading) + } + + if rows.isEmpty { + ContentUnavailableView( + emptyTitle, + systemImage: emptySystemImage, + description: Text(emptyDescription) + ) + } + } + .focusable() + .focused($isTableFocused) + .focusEffectDisabled() + .onKeyPress(.upArrow) { + moveSelection(.previous) + return .handled + } + .onKeyPress(.downArrow) { + moveSelection(.next) + return .handled + } + } + + private var header: some View { + HStack(spacing: 0) { + sortableHeader("Process", column: .process) + .frame(width: 230, alignment: .leading) + sortableHeader("PID", column: .pid) + .frame(width: 76, alignment: .leading) + sortableHeader("CPU", column: .cpu) + .frame(width: 76, alignment: .leading) + sortableHeader("Memory", column: .memory) + .frame(width: 96, alignment: .leading) + sortableHeader("Time", column: .time) + .frame(width: 116, alignment: .leading) + sortableHeader("Command", column: .command) + .frame(width: 326, alignment: .leading) + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + .background(.ultraThinMaterial) + .overlay(alignment: .bottom) { + Divider() + } + } + + private func dataRow(for row: ProcessTableRow) -> some View { + Button { + selection = row.id + isTableFocused = true + } label: { + HStack(spacing: 0) { + ProcessCell(row: row) + .frame(width: 230, alignment: .leading) + MonospaceValue("\(row.pid)") + .frame(width: 76, alignment: .leading) + MetricValue(ProcessMetricFormat.cpu(row.cpuPercent)) + .frame(width: 76, alignment: .leading) + MetricValue(ProcessMetricFormat.memory(row.memoryBytes)) + .frame(width: 96, alignment: .leading) + MonospaceValue(row.elapsedTime) + .frame(width: 116, alignment: .leading) + Text(row.command) + .font(.callout) + .lineLimit(1) + .truncationMode(.middle) + .foregroundStyle(.secondary) + .textSelection(.enabled) + .frame(width: 326, alignment: .leading) + } + .padding(.horizontal, 12) + .padding(.vertical, 4) + .contentShape(Rectangle()) + .background(rowBackground(for: row)) + .overlay(alignment: .leading) { + if row.id == selection { + Rectangle() + .fill(DevScopePalette.accent) + .frame(width: 3) + } + } + .foregroundStyle(.primary) + } + .buttonStyle(.plain) + .onHover { hovering in + hoveredRowID = hovering ? row.id : (hoveredRowID == row.id ? nil : hoveredRowID) + } + .contextMenu { + processContextMenu(for: row.item) + } + .accessibilityElement(children: .combine) + .accessibilityLabel("\(row.processName), \(row.runtime), PID \(row.pid)") + .accessibilityValue("\(ProcessMetricFormat.cpu(row.cpuPercent)), \(ProcessMetricFormat.memory(row.memoryBytes))") + .accessibilityAddTraits(row.id == selection ? .isSelected : []) + } + + private func rowBackground(for row: ProcessTableRow) -> Color { + if row.id == selection { + return DevScopePalette.accent.opacity(controlActiveState == .inactive ? 0.10 : 0.20) + } + if row.id == hoveredRowID { + return Color.primary.opacity(0.055) + } + return row.snapshotOrder.isMultiple(of: 2) ? Color.white.opacity(0.025) : Color.clear + } + + private func moveSelection(_ direction: InterfaceSelectionDirection) { + let nextSelection = InterfacePresentation.movedSelection( + in: rows.map(\.id), + current: selection, + direction: direction + ) + guard nextSelection != selection else { + return + } + selection = nextSelection + if let nextSelection { + verticalScrollTargetID = nextSelection + } + } + + private func sortableHeader(_ title: String, column: ProcessTableSortColumn) -> some View { + SortableTableHeader( + title: title, + column: column, + activeColumn: sort.column, + direction: sort.direction, + activate: { sort.activate($0) } + ) + } + + private var emptySystemImage: String { + emptyTitle == "Process scanning blocked" ? "lock.shield" : "checkmark.seal" + } + + @ViewBuilder + private func processContextMenu(for item: ClassifiedDevProcess) -> some View { + Button("Copy Command") { + copyCommand(item) + } + + Button("Copy PID") { + copy("\(item.process.pid)") + } + + if let currentDirectory = item.process.currentDirectory { + Button("Open Folder") { + NSWorkspace.shared.open(URL(fileURLWithPath: currentDirectory, isDirectory: true)) + } + } + } + + private func copy(_ value: String) { + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(value, forType: .string) + } +} + +private struct ProcessCell: View { + let row: ProcessTableRow + + private var item: ClassifiedDevProcess { row.item } + + var body: some View { + HStack(spacing: 10) { + ZStack { + RoundedRectangle(cornerRadius: 8, style: .continuous) + .fill(DevScopePalette.color(for: item.classification.kind).opacity(0.16)) + Image(systemName: item.classification.kind.symbolName) + .font(.system(size: 13, weight: .semibold)) + .foregroundStyle(DevScopePalette.color(for: item.classification.kind)) + } + .frame(width: 28, height: 28) + + VStack(alignment: .leading, spacing: 2) { + Text(row.title) + .font(.callout.weight(.semibold)) + .lineLimit(1) + .truncationMode(.tail) + + HStack(spacing: 5) { + if row.isFavorite { + Image(systemName: "star.fill") + .foregroundStyle(.yellow) + .help("Favorite") + } + + if row.isWatched { + Image(systemName: "eye.fill") + .foregroundStyle(.cyan) + .help("Watched") + } + + Text(ProcessPresentation.contextLabel(for: item)) + .font(.caption) + .foregroundStyle(.secondary) + .lineLimit(1) + + ForEach(item.classification.tags.prefix(2)) { tag in + ProcessTagBadge(tag: tag) + } + } + + if !row.automationBadges.isEmpty { + HStack(spacing: 5) { + ForEach(Array(row.automationBadges.enumerated()), id: \.offset) { _, badge in + AutomationProcessBadgeView(badge: badge) + } + } + } + } + } + .padding(.vertical, 5) + } +} + +private struct AutomationProcessBadgeView: View { + let badge: AutomationProcessBadge + + var body: some View { + Label(title, systemImage: symbolName) + .font(.caption2.weight(.semibold)) + .lineLimit(1) + .padding(.horizontal, 6) + .padding(.vertical, 2) + .background(tint.opacity(0.14), in: Capsule()) + .foregroundStyle(tint) + .accessibilityLabel(accessibilityLabel) + .help(accessibilityLabel) + } + + private var title: String { + switch badge { + case .automated: "Automated" + case .longRunning(let duration): "Long Running · \(duration)" + } + } + + private var symbolName: String { + switch badge { + case .automated: "gearshape.2" + case .longRunning: "clock.badge.exclamationmark" + } + } + + private var tint: Color { + switch badge { + case .automated: DevScopePalette.accent + case .longRunning: .orange + } + } + + private var accessibilityLabel: String { + switch badge { + case .automated: "Automated: linked to a verified automation definition" + case .longRunning(let duration): "Long Running: crossed the configured threshold, running for \(duration)" + } + } +} + +struct ProcessTagBadge: View { + let tag: DevProcessTag + + var body: some View { + Label(tag.title, systemImage: tag.symbolName) + .font(.caption2.weight(.semibold)) + .labelStyle(.titleAndIcon) + .lineLimit(1) + .padding(.horizontal, 6) + .padding(.vertical, 2) + .background(DevScopePalette.accent.opacity(0.12), in: Capsule()) + .foregroundStyle(.secondary) + } +} + +private struct MetricValue: View { + let value: String + + init(_ value: String) { + self.value = value + } + + var body: some View { + Text(value) + .font(.system(.callout, design: .monospaced)) + .foregroundStyle(.secondary) + .contentTransition(.numericText()) + .lineLimit(1) + } +} + +private struct MonospaceValue: View { + let value: String + + init(_ value: String) { + self.value = value + } + + var body: some View { + Text(value) + .font(.system(.callout, design: .monospaced)) + .foregroundStyle(.secondary) + .textSelection(.enabled) + } +} + +enum ProcessMetricFormat { + static func cpu(_ value: Double?) -> String { + guard let value else { + return "-" + } + + return String(format: "%.1f%%", value) + } + + static func gpu(_ value: Double?) -> String { + guard let value else { + return "Unavailable" + } + + return String(format: "%.1f%%", value) + } + + static func memory(_ bytes: Int64?) -> String { + guard let bytes else { + return "-" + } + + let megabytes = Double(bytes) / 1_048_576 + if megabytes < 1024 { + return String(format: "%.0f MB", megabytes) + } + + return String(format: "%.1f GB", megabytes / 1024) + } +} diff --git a/Sources/DevScope/Views/SettingsView.swift b/Sources/DevScope/Views/SettingsView.swift new file mode 100644 index 0000000..409294e --- /dev/null +++ b/Sources/DevScope/Views/SettingsView.swift @@ -0,0 +1,788 @@ +import AppKit +import DevScopeCore +import SwiftUI + +enum DevScopeSettingsKey { + static let useAppleNaming = "useAppleNaming" + static let showProcessGraphs = "showProcessGraphs" + static let showLiveNotch = "showLiveNotch" + static let selectedCategoryID = "selectedCategoryID" + static let activityScope = "activityScope" + static let isRailCollapsed = "isRailCollapsed" + static let isLiveActivityExpanded = "isLiveActivityExpanded" + static let liveActivityPreferredHeight = "liveActivityPreferredHeight" + static let longRunningThresholdSeconds = AutomationPresentationSettings.longRunningThresholdSecondsKey + static let includeAppleSystemServices = AutomationPresentationSettings.includeAppleSystemServicesKey + static let selectedWorkspaceMode = AutomationPresentationSettings.selectedWorkspaceModeKey + static let notifyLongRunningAutomation = AutomationPresentationSettings.notifyLongRunningAutomationKey + static let notifyUnexpectedAutomationExit = AutomationPresentationSettings.notifyUnexpectedAutomationExitKey + static let notifyRepeatedAutomationFailure = AutomationPresentationSettings.notifyRepeatedAutomationFailureKey +} + +struct SettingsView: View { + @ObservedObject var automationNotifier: AutomationNotifier + @AppStorage(DevScopeSettingsKey.useAppleNaming) private var useAppleNaming = true + @AppStorage(DevScopeSettingsKey.showProcessGraphs) private var showProcessGraphs = true + @AppStorage(DevScopeSettingsKey.showLiveNotch) private var showLiveNotch = true + @AppStorage(DevScopeSettingsKey.longRunningThresholdSeconds) private var longRunningThresholdSeconds = 14_400.0 + @AppStorage(DevScopeSettingsKey.includeAppleSystemServices) private var includeAppleSystemServices = false + @AppStorage(DevScopeSettingsKey.notifyLongRunningAutomation) private var notifyLongRunningAutomation = false + @AppStorage(DevScopeSettingsKey.notifyUnexpectedAutomationExit) private var notifyUnexpectedAutomationExit = false + @AppStorage(DevScopeSettingsKey.notifyRepeatedAutomationFailure) private var notifyRepeatedAutomationFailure = false + @State private var didCopyAccessDiagnostics = false + @State private var didCopySupportURL = false + @State private var finderRevealFeedback: FinderRevealFeedback? + @State private var accessAssessment = ProcessAccessAssessment.assess( + isSandboxed: ProcessAccessStatus.isSandboxed, + processes: nil, + errorDescription: nil + ) + @State private var isCheckingAccess = false + + var body: some View { + TabView { + ScrollView { + VStack(alignment: .leading, spacing: 18) { + SettingsPanel(title: "Live Monitoring") { + Toggle("Show process graphs", isOn: $showProcessGraphs) + Toggle("Show live notch", isOn: $showLiveNotch) + Text("Reveal DevScope Live by hovering the top-center screen notch area.") + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + SettingsValueRow(label: "Metric cadence", value: "2 seconds") + SettingsValueRow(label: "History window", value: "2 minutes") + } + + SettingsPanel(title: "Apple Intelligence") { + Toggle("Use Apple naming when available", isOn: $useAppleNaming) + Text( + "Deterministic labels and workflow grouping stay in control. Apple Intelligence can add concise process names and workflow notes when available." + ) + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } + + Spacer() + } + .padding(20) + } + .tabItem { + Label("General", systemImage: "gearshape") + } + + ScrollView { + VStack(alignment: .leading, spacing: 18) { + SettingsPanel(title: "Automation Detection") { + Stepper(value: thresholdHoursBinding, in: 1...168, step: 1) { + LabeledContent("Long-running threshold", value: thresholdDescription) + } + Text("A process becomes Long Running at or after this duration. This remains independent from the Automated badge.") + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + + Toggle("Include Apple System Services", isOn: $includeAppleSystemServices) + Text("Off by default. User and third-party automations, including organization-managed third-party definitions, remain visible.") + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } + + SettingsPanel(title: "Automation Notifications") { + Toggle("Long-running automation", isOn: $notifyLongRunningAutomation) + Toggle("Unexpected automation exit", isOn: $notifyUnexpectedAutomationExit) + Toggle("Repeated automation failure", isOn: $notifyRepeatedAutomationFailure) + + notificationDeliveryStatus + + if automationNotifier.deliveryState == .denied { + Button("Open Notification Settings") { openNotificationSettings() } + .accessibilityHint("Opens macOS notification settings so DevScope delivery can be allowed.") + } + } + Spacer() + } + .padding(20) + } + .tabItem { + Label("Automations", systemImage: "gearshape.2") + } + + ScrollView { + VStack(alignment: .leading, spacing: 18) { + SettingsPanel(title: "Process Access") { + HStack(alignment: .top, spacing: 12) { + Image(systemName: accessSummary.symbolName) + .font(.system(size: 24, weight: .semibold)) + .foregroundStyle(accessSummary.tint) + .frame(width: 34, height: 34) + + VStack(alignment: .leading, spacing: 4) { + HStack(spacing: 8) { + Text(accessSummary.title) + .font(.headline) + Text(accessSummary.badge) + .font(.caption.weight(.semibold)) + .foregroundStyle(accessSummary.tint) + .padding(.horizontal, 8) + .padding(.vertical, 3) + .background(accessSummary.tint.opacity(0.12), in: Capsule()) + } + + Text(accessSummary.message) + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } + } + + Divider() + + ForEach(accessAssessment.requirements, id: \.kind) { requirement in + AccessRequirementRow(requirement: requirement) { action in + performAccessAction(action) + } + } + + Divider() + + if shouldShowFullDiskAccessGuidance { + VStack(alignment: .leading, spacing: 8) { + Text(fullDiskAccessGuidance.title) + .font(.callout.weight(.semibold)) + + Text(fullDiskAccessGuidance.detail) + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + + VStack(alignment: .leading, spacing: 3) { + Text("Current app") + .font(.caption2.weight(.semibold)) + .foregroundStyle(.secondary) + Text(fullDiskAccessGuidance.appPath) + .font(.caption.monospaced()) + .textSelection(.enabled) + .fixedSize(horizontal: false, vertical: true) + } + + if !fullDiskAccessGuidance.isSandboxed + && neededAccessActions.contains(.fullDiskAccess) + { + VStack(alignment: .leading, spacing: 6) { + ForEach( + Array(fullDiskAccessGuidance.steps.enumerated()), id: \.offset + ) { index, step in + HStack(alignment: .firstTextBaseline, spacing: 7) { + Text("\(index + 1).") + .font(.caption.weight(.semibold)) + .frame(width: 18, alignment: .trailing) + Text(step) + .font(.caption) + .fixedSize(horizontal: false, vertical: true) + } + } + } + .padding(.vertical, 4) + + HStack { + Button { + openFullDiskAccess() + } label: { + Label( + "Open Full Disk Access", systemImage: "externaldrive.badge.timemachine") + } + .buttonStyle(.borderedProminent) + .controlSize(.small) + .accessibilityLabel("Open Full Disk Access") + .accessibilityHint( + "Opens Full Disk Access in macOS System Settings.") + + Button { + revealDevScopeInFinder() + } label: { + Label("Reveal DevScope in Finder", systemImage: "finder") + } + .controlSize(.small) + .accessibilityHint("Selects the exact running DevScope app in Finder.") + } + + if let finderRevealFeedback { + Label( + finderRevealFeedback.message, systemImage: finderRevealFeedback.symbolName + ) + .font(.caption) + .foregroundStyle(finderRevealFeedback.tint) + .fixedSize(horizontal: false, vertical: true) + } + } + } + + Divider() + } + + VStack(alignment: .leading, spacing: 8) { + Text("General privacy settings") + .font(.caption.weight(.semibold)) + Text( + fullDiskAccessGuidance.isSandboxed + ? "You may inspect general privacy settings, but no permission toggle can remove this build's App Sandbox restriction." + : "Open the main Privacy & Security pane for other macOS privacy controls." + ) + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + Button { + openPrivacyAndSecurity() + } label: { + Label("Open Privacy & Security", systemImage: "gearshape.2") + } + .controlSize(.small) + .accessibilityHint("Opens the general Privacy & Security pane in System Settings.") + } + + HStack { + Button { + refreshAccessAssessment() + } label: { + Label( + isCheckingAccess ? "Checking" : "Check Access", systemImage: "arrow.clockwise") + } + .disabled(isCheckingAccess) + .accessibilityLabel("Check process access") + .accessibilityHint("Checks process metadata and folder-context access again.") + + Spacer() + + Button { + copyAccessDiagnostics() + } label: { + Label( + didCopyAccessDiagnostics ? "Copied" : "Copy Diagnostics", + systemImage: didCopyAccessDiagnostics ? "checkmark" : "doc.on.doc") + } + .accessibilityLabel("Copy access diagnostics") + .accessibilityHint("Copies DevScope access and sandbox diagnostics to the clipboard.") + } + } + + if accessAssessment.isSandboxed { + SettingsPanel(title: "Distribution Guidance") { + Text( + "This build is sandboxed. If process metadata is blocked, there is no extra macOS privacy permission to request; ship a reduced App Store mode or a notarized Developer ID build for full DevScope process control." + ) + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } + } + + Spacer() + } + .padding(20) + } + .task { + refreshAccessAssessment() + } + .tabItem { + Label("Access", systemImage: "lock.shield") + } + + ScrollView { + VStack(alignment: .leading, spacing: 18) { + SettingsPanel(title: "Support DevScope") { + HStack(spacing: 10) { + Image(systemName: "cup.and.saucer.fill") + .font(.system(size: 22, weight: .semibold)) + .foregroundStyle(.pink) + .frame(width: 32, height: 32) + + VStack(alignment: .leading, spacing: 2) { + Text("Buy Me a Coffee") + .font(.headline) + Text("Support open-source maintenance and faster DevScope releases.") + .font(.caption) + .foregroundStyle(.secondary) + } + } + + SettingsValueRow( + label: "Destination", + value: DevScopeSupport.buyMeACoffeeURL.host() ?? "buymeacoffee.com") + Text(DevScopeSupport.buyMeACoffeeURLString) + .font(.caption.monospaced()) + .foregroundStyle(.secondary) + .textSelection(.enabled) + + HStack { + Button { + copySupportURL() + } label: { + Label( + didCopySupportURL ? "Copied" : "Copy Link", + systemImage: didCopySupportURL ? "checkmark" : "doc.on.doc") + } + .accessibilityLabel("Copy Buy Me a Coffee link") + .accessibilityHint("Copies the DevScope support link.") + + Spacer() + + Button { + openSponsorURL() + } label: { + Label("Open Buy Me a Coffee", systemImage: "safari") + } + .buttonStyle(.borderedProminent) + .accessibilityLabel("Open Buy Me a Coffee") + .accessibilityHint("Opens the DevScope Buy Me a Coffee support page.") + } + + Text( + "This support destination is fixed for the public DevScope build so users see the same trusted link everywhere." + ) + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } + + Spacer() + } + .padding(20) + } + .tabItem { + Label("Support", systemImage: "heart") + } + } + .frame(width: 640, height: 520) + .task { + normalizeThreshold() + await automationNotifier.synchronize(persistedNotificationPreferences) + } + .onChange(of: longRunningThresholdSeconds) { _, _ in normalizeThreshold() } + .onChange(of: notifyLongRunningAutomation) { _, enabled in + Task { await automationNotifier.setPreference(.crossedLongRunningThreshold, isEnabled: enabled) } + } + .onChange(of: notifyUnexpectedAutomationExit) { _, enabled in + Task { await automationNotifier.setPreference(.unexpectedExit, isEnabled: enabled) } + } + .onChange(of: notifyRepeatedAutomationFailure) { _, enabled in + Task { await automationNotifier.setPreference(.repeatedFailure, isEnabled: enabled) } + } + } + + private var thresholdHoursBinding: Binding { + Binding( + get: { AutomationPresentationSettings.normalizedThreshold(longRunningThresholdSeconds) / 3_600 }, + set: { longRunningThresholdSeconds = $0 * 3_600 } + ) + } + + private var thresholdDescription: String { + let hours = Int(AutomationPresentationSettings.normalizedThreshold(longRunningThresholdSeconds) / 3_600) + if hours.isMultiple(of: 24) { return "\(hours / 24) day\(hours == 24 ? "" : "s")" } + return "\(hours) hour\(hours == 1 ? "" : "s")" + } + + private var persistedNotificationPreferences: AutomationNotificationPreferences { + AutomationNotificationPreferences( + crossedLongRunningThreshold: notifyLongRunningAutomation, + unexpectedExit: notifyUnexpectedAutomationExit, + repeatedFailure: notifyRepeatedAutomationFailure + ) + } + + @ViewBuilder private var notificationDeliveryStatus: some View { + switch automationNotifier.deliveryState { + case .notRequested: + Text("Notifications are off until you opt in. macOS may ask for permission.") + case .requesting: + Label("Waiting for macOS notification permission", systemImage: "clock") + case .enabled: + Label("Notification delivery is allowed", systemImage: "checkmark.circle") + .foregroundStyle(.green) + case .denied: + Label("Preferences are saved, but macOS denied notification delivery.", systemImage: "exclamationmark.triangle") + .foregroundStyle(.orange) + case .authorizationFailed: + Label("Preferences are saved, but DevScope could not verify notification permission. Turn a notification off and on to retry.", systemImage: "exclamationmark.triangle") + .foregroundStyle(.orange) + case .deliveryFailed: + Label("macOS did not accept the last notification after one retry. Review notification permission before relying on alerts.", systemImage: "exclamationmark.triangle") + .foregroundStyle(.orange) + } + } + + private func normalizeThreshold() { + let normalized = AutomationPresentationSettings.normalizedThreshold(longRunningThresholdSeconds) + if normalized != longRunningThresholdSeconds { longRunningThresholdSeconds = normalized } + } + + private func openNotificationSettings() { + guard let url = URL(string: "x-apple.systempreferences:com.apple.Notifications-Settings.extension") else { return } + NSWorkspace.shared.open(url) + } + + private func openSponsorURL() { + NSWorkspace.shared.open(DevScopeSupport.buyMeACoffeeURL) + } + + private func copySupportURL() { + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(DevScopeSupport.buyMeACoffeeURLString, forType: .string) + didCopySupportURL = true + } + + private var accessSummary: ProcessAccessSummary { + ProcessAccessSummary(assessment: accessAssessment) + } + + private var neededAccessActions: [ProcessAccessAction] { + accessAssessment.requirements.reduce(into: []) { actions, requirement in + guard requirement.state == .needed, + let action = requirement.action, + !actions.contains(action) + else { + return + } + actions.append(action) + } + } + + private var fullDiskAccessGuidance: FullDiskAccessGuidance { + FullDiskAccessGuidance( + appPath: Bundle.main.bundleURL.path, + isSandboxed: accessAssessment.isSandboxed + ) + } + + private var shouldShowFullDiskAccessGuidance: Bool { + ProcessAccessStatus.isSandboxed || neededAccessActions.contains(.fullDiskAccess) + } + + private func refreshAccessAssessment() { + guard !isCheckingAccess else { + return + } + + let isSandboxed = ProcessAccessStatus.isSandboxed + isCheckingAccess = true + Task { @MainActor in + accessAssessment = await Task.detached(priority: .userInitiated) { + do { + let processes = try SystemProcessScanner().snapshot(includeCurrentDirectories: true) + return ProcessAccessAssessment.assess( + isSandboxed: isSandboxed, + processes: processes, + errorDescription: nil + ) + } catch { + return ProcessAccessAssessment.assess( + isSandboxed: isSandboxed, + processes: nil, + errorDescription: error.localizedDescription + ) + } + }.value + isCheckingAccess = false + } + } + + private func performAccessAction(_ action: ProcessAccessAction) { + switch action { + case .privacySecurity: + openPrivacyAndSecurity() + case .fullDiskAccess: + openFullDiskAccess() + } + } + + private func accessActionTitle(for action: ProcessAccessAction) -> String { + switch action { + case .privacySecurity: + "Open Privacy & Security" + case .fullDiskAccess: + "Open Full Disk Access" + } + } + + private func accessActionDestination(for action: ProcessAccessAction) -> String { + switch action { + case .privacySecurity: + "Privacy & Security" + case .fullDiskAccess: + "Full Disk Access" + } + } + + private func accessActionSymbol(for action: ProcessAccessAction) -> String { + switch action { + case .privacySecurity: + "gearshape.2" + case .fullDiskAccess: + "externaldrive.badge.timemachine" + } + } + + private func openPrivacyAndSecurity() { + if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security") { + NSWorkspace.shared.open(url) + } else { + openSystemSettingsApp() + } + } + + private func openFullDiskAccess() { + FullDiskAccessSettingsRoute.open( + using: NSWorkspace.shared.open, + fallback: openPrivacyAndSecurity + ) + } + + private func revealDevScopeInFinder() { + let appURL = Bundle.main.bundleURL + guard FileManager.default.fileExists(atPath: appURL.path) else { + finderRevealFeedback = .failure( + "The current DevScope app could not be found at the displayed path.") + return + } + + NSWorkspace.shared.activateFileViewerSelecting([Bundle.main.bundleURL]) + finderRevealFeedback = .success("DevScope is selected in Finder.") + } + + private func openSystemSettingsApp() { + for path in [ + "/System/Applications/System Settings.app", + "/System/Applications/System Preferences.app", + ] { + let url = URL(fileURLWithPath: path, isDirectory: true) + if FileManager.default.fileExists(atPath: url.path) { + NSWorkspace.shared.open(url) + return + } + } + } + + private func copyAccessDiagnostics() { + let bundle = Bundle.main + let diagnostics = """ + DevScope Access Diagnostics + Bundle ID: \(bundle.bundleIdentifier ?? "unknown") + Version: \(bundle.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "unknown") (\(bundle.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "unknown")) + App path: \(bundle.bundlePath) + Sandbox detected: \(ProcessAccessStatus.isSandboxed ? "yes" : "no") + Process count: \(accessAssessment.processCount) + Working directory count: \(accessAssessment.currentDirectoryCount) + Access rows: \(accessAssessment.requirements.map { "\($0.title)=\($0.state.rawValue)" }.joined(separator: ", ")) + Scan error: \(accessAssessment.scanErrorDescription ?? "none") + Scanner: user-level ps/lsof with native libproc fallback + Guidance: DevScope only requests actionable permissions. App Sandbox process blocking cannot be fixed from Privacy & Security. + """ + + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(diagnostics, forType: .string) + didCopyAccessDiagnostics = true + } +} + +private struct FinderRevealFeedback { + let message: String + let symbolName: String + let tint: Color + + static func success(_ message: String) -> FinderRevealFeedback { + FinderRevealFeedback(message: message, symbolName: "checkmark.circle.fill", tint: .green) + } + + static func failure(_ message: String) -> FinderRevealFeedback { + FinderRevealFeedback(message: message, symbolName: "exclamationmark.triangle.fill", tint: .red) + } +} + +private struct ProcessAccessSummary { + let title: String + let badge: String + let message: String + let symbolName: String + let tint: Color + + init(assessment: ProcessAccessAssessment) { + if assessment.hasBlockedRequirement { + title = "Process access blocked" + badge = "Blocked" + message = + assessment.isSandboxed + ? "The current build cannot inspect local process metadata. No Privacy & Security permission can override App Sandbox for unrestricted process inspection." + : "DevScope could not inspect local process metadata. This failure is not mapped to a known macOS permission toggle." + symbolName = "lock.shield" + tint = .red + } else if assessment.hasNeededAction { + title = "Permission needed" + badge = "Needed" + message = + "DevScope can see processes, but one optional capability needs a macOS privacy setting." + symbolName = "exclamationmark.shield" + tint = .orange + } else { + title = "Process access ready" + badge = "Ready" + message = "DevScope can inspect local running activity with current-user permissions." + symbolName = "checkmark.shield" + tint = .green + } + } +} + +private enum ProcessAccessStatus { + static var isSandboxed: Bool { + ProcessInfo.processInfo.environment["APP_SANDBOX_CONTAINER_ID"] != nil + } +} + +private struct AccessRequirementRow: View { + let requirement: ProcessAccessRequirement + let performAction: (ProcessAccessAction) -> Void + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + HStack(alignment: .top, spacing: 10) { + Image(systemName: symbolName) + .font(.system(size: 14, weight: .semibold)) + .foregroundStyle(tint) + .frame(width: 22, height: 22) + + VStack(alignment: .leading, spacing: 2) { + HStack(spacing: 8) { + Text(requirement.title) + .font(.callout.weight(.medium)) + Text(badge) + .font(.caption2.weight(.bold)) + .foregroundStyle(tint) + .padding(.horizontal, 7) + .padding(.vertical, 2) + .background(tint.opacity(0.12), in: Capsule()) + } + + Text(requirement.detail) + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } + + Spacer(minLength: 12) + } + + if let action = requirement.action, requirement.state == .needed { + Button { + performAction(action) + } label: { + Label(actionTitle(for: action), systemImage: actionSymbol(for: action)) + } + .controlSize(.small) + .accessibilityLabel(actionTitle(for: action)) + .accessibilityHint("Opens \(actionDestination(for: action)) in macOS System Settings.") + } else if requirement.state == .blocked { + Text("No System Settings permission can grant this access for the current build.") + .font(.caption2) + .foregroundStyle(.secondary) + } + } + .padding(.vertical, 2) + } + + private var symbolName: String { + switch requirement.state { + case .ready: + "checkmark.circle.fill" + case .needed: + "exclamationmark.triangle.fill" + case .blocked: + "lock.fill" + } + } + + private var tint: Color { + switch requirement.state { + case .ready: + .green + case .needed: + .orange + case .blocked: + .red + } + } + + private var badge: String { + switch requirement.state { + case .ready: + "Ready" + case .needed: + "Needed" + case .blocked: + "Blocked" + } + } + + private func actionTitle(for action: ProcessAccessAction) -> String { + switch action { + case .privacySecurity: + "Open Privacy & Security" + case .fullDiskAccess: + "Open Full Disk Access" + } + } + + private func actionSymbol(for action: ProcessAccessAction) -> String { + switch action { + case .privacySecurity: + "gearshape.2" + case .fullDiskAccess: + "externaldrive.badge.timemachine" + } + } + + private func actionDestination(for action: ProcessAccessAction) -> String { + switch action { + case .privacySecurity: + "Privacy & Security" + case .fullDiskAccess: + "Full Disk Access" + } + } +} + +private struct SettingsPanel: View { + let title: String + @ViewBuilder let content: Content + + var body: some View { + VStack(alignment: .leading, spacing: 10) { + Text(title) + .font(.headline) + VStack(alignment: .leading, spacing: 10) { + content + } + .padding(12) + .frame(maxWidth: .infinity, alignment: .leading) + .softPanel(cornerRadius: 12) + } + } +} + +private struct SettingsValueRow: View { + let label: String + let value: String + + var body: some View { + HStack { + Text(label) + .foregroundStyle(.secondary) + Spacer() + Text(value) + .monospacedDigit() + } + .font(.callout) + } +} diff --git a/Sources/DevScope/Views/SortableTableHeader.swift b/Sources/DevScope/Views/SortableTableHeader.swift new file mode 100644 index 0000000..531040d --- /dev/null +++ b/Sources/DevScope/Views/SortableTableHeader.swift @@ -0,0 +1,44 @@ +import SwiftUI + +struct SortableTableHeader: View { + let title: String + let column: Column + let activeColumn: Column? + let direction: TableSortDirection + let contentAlignment: Alignment + let activate: (Column) -> Void + + init( + title: String, + column: Column, + activeColumn: Column?, + direction: TableSortDirection, + contentAlignment: Alignment = .leading, + activate: @escaping (Column) -> Void + ) { + self.title = title + self.column = column + self.activeColumn = activeColumn + self.direction = direction + self.contentAlignment = contentAlignment + self.activate = activate + } + + var body: some View { + Button { activate(column) } label: { + HStack(spacing: 4) { + Text(title).font(.caption.weight(.semibold)) + if activeColumn == column { + Image(systemName: direction.symbolName).font(.caption2.weight(.bold)) + } + } + .frame(maxWidth: .infinity, alignment: contentAlignment) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .accessibilityValue(activeColumn == column ? "Sorted \(direction.accessibilityTitle)" : "Not sorted") + .help(activeColumn == column + ? "Sort by \(title) \(direction == .ascending ? "descending" : "ascending")" + : "Sort by \(title)") + } +} diff --git a/Sources/DevScope/Views/SponsorSheetView.swift b/Sources/DevScope/Views/SponsorSheetView.swift new file mode 100644 index 0000000..2404966 --- /dev/null +++ b/Sources/DevScope/Views/SponsorSheetView.swift @@ -0,0 +1,101 @@ +import AppKit +import SwiftUI + +struct SponsorSheetView: View { + @Environment(\.dismiss) private var dismiss + @State private var didCopySupportURL = false + + var body: some View { + VStack(alignment: .leading, spacing: 18) { + HStack(alignment: .top, spacing: 14) { + ZStack { + RoundedRectangle(cornerRadius: 16, style: .continuous) + .fill(.pink.opacity(0.16)) + Image(systemName: "cup.and.saucer.fill") + .font(.system(size: 28, weight: .semibold)) + .foregroundStyle(.pink) + } + .frame(width: 62, height: 62) + .devScopeGlass(cornerRadius: 16) + + VStack(alignment: .leading, spacing: 5) { + Text("Buy Me a Coffee") + .font(.title2.weight(.semibold)) + Text("Support DevScope development, open-source maintenance, and faster macOS polish.") + .font(.callout) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } + } + + VStack(alignment: .leading, spacing: 10) { + SponsorPoint(symbolName: "bolt.fill", title: "Realtime polish", detail: "Support work on lower-latency process metrics and smoother charts.") + SponsorPoint(symbolName: "brain.head.profile", title: "Apple Intelligence", detail: "Fund safer local naming and process insight features.") + SponsorPoint(symbolName: "checkmark.seal", title: "Open source quality", detail: "Keep tests, docs, and release hardening part of every feature.") + } + + HStack { + Button("Close") { + dismiss() + } + .keyboardShortcut(.cancelAction) + + Spacer() + + Button { + copySupportURL() + } label: { + Label(didCopySupportURL ? "Copied" : "Copy Link", systemImage: didCopySupportURL ? "checkmark" : "doc.on.doc") + } + .accessibilityLabel("Copy Buy Me a Coffee link") + .accessibilityHint("Copies the DevScope support link.") + + Button { + openSponsorURL() + } label: { + Label("Open Buy Me a Coffee", systemImage: "safari") + } + .buttonStyle(.borderedProminent) + .accessibilityLabel("Open Buy Me a Coffee") + .accessibilityHint("Opens the DevScope Buy Me a Coffee support page.") + .keyboardShortcut(.defaultAction) + } + } + .padding(22) + .frame(width: 480) + } + + private func openSponsorURL() { + NSWorkspace.shared.open(DevScopeSupport.buyMeACoffeeURL) + } + + private func copySupportURL() { + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(DevScopeSupport.buyMeACoffeeURLString, forType: .string) + didCopySupportURL = true + } +} + +private struct SponsorPoint: View { + let symbolName: String + let title: String + let detail: String + + var body: some View { + HStack(alignment: .top, spacing: 10) { + Image(systemName: symbolName) + .font(.system(size: 14, weight: .semibold)) + .foregroundStyle(DevScopePalette.accent) + .frame(width: 18) + + VStack(alignment: .leading, spacing: 2) { + Text(title) + .font(.callout.weight(.semibold)) + Text(detail) + .font(.caption) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) + } + } + } +} diff --git a/Sources/DevScope/Views/TableSortState.swift b/Sources/DevScope/Views/TableSortState.swift new file mode 100644 index 0000000..9d44c54 --- /dev/null +++ b/Sources/DevScope/Views/TableSortState.swift @@ -0,0 +1,156 @@ +import DevScopeCore +import Foundation + +enum TableSortDirection: Equatable, Sendable { + case ascending + case descending + + var sortOrder: SortOrder { self == .ascending ? .forward : .reverse } + var symbolName: String { self == .ascending ? "chevron.up" : "chevron.down" } + var accessibilityTitle: String { self == .ascending ? "ascending" : "descending" } +} + +enum ProcessTableSortColumn: CaseIterable, Equatable, Hashable, Sendable { + case process, pid, cpu, memory, time, command + + var defaultDirection: TableSortDirection { + switch self { + case .cpu, .memory, .time: .descending + case .process, .pid, .command: .ascending + } + } +} + +struct ProcessTableSortState: Equatable, Sendable { + var column: ProcessTableSortColumn? + var direction: TableSortDirection + + init(column: ProcessTableSortColumn? = nil, direction: TableSortDirection = .ascending) { + self.column = column + self.direction = direction + } + + mutating func activate(_ selected: ProcessTableSortColumn) { + if column == selected { + direction = direction == .ascending ? .descending : .ascending + } else { + column = selected + direction = selected.defaultDirection + } + } + + var comparators: [KeyPathComparator] { + guard let column else { + return [KeyPathComparator(\ProcessTableRow.snapshotOrder, order: .forward)] + } + let order = direction.sortOrder + switch column { + case .process: return [KeyPathComparator(\ProcessTableRow.processName, order: order)] + case .pid: return [KeyPathComparator(\ProcessTableRow.pid, order: order)] + case .cpu: return [KeyPathComparator(\ProcessTableRow.cpuPercent, order: order)] + case .memory: return [KeyPathComparator(\ProcessTableRow.memoryBytes, order: order)] + case .time: return [KeyPathComparator(\ProcessTableRow.elapsedSeconds, order: order)] + case .command: return [KeyPathComparator(\ProcessTableRow.command, order: order)] + } + } +} + +enum AutomationTableSortColumn: CaseIterable, Equatable, Hashable, Sendable { + case automation, kind, trigger, state, owner, runs + + var defaultDirection: TableSortDirection { + self == .runs ? .descending : .ascending + } +} + +struct AutomationTableSortState: Equatable, Sendable { + var column: AutomationTableSortColumn? = nil + var direction: TableSortDirection = .ascending + + mutating func activate(_ selected: AutomationTableSortColumn) { + if column == selected { + direction = direction == .ascending ? .descending : .ascending + } else { + column = selected + direction = selected.defaultDirection + } + } + + func sorted(_ rows: [AutomationTableRow]) -> [AutomationTableRow] { + guard let column else { return rows } + return rows.sorted { lhs, rhs in + let comparison = compare(lhs, rhs, column: column) + if comparison == .orderedSame { + return lhs.id.rawValue < rhs.id.rawValue + } + return direction == .ascending + ? comparison == .orderedAscending + : comparison == .orderedDescending + } + } + + private func compare( + _ lhs: AutomationTableRow, + _ rhs: AutomationTableRow, + column: AutomationTableSortColumn + ) -> ComparisonResult { + switch column { + case .automation: + return compareText( + "\(lhs.record.displayName)\u{1F}\(lhs.record.label)", + "\(rhs.record.displayName)\u{1F}\(rhs.record.label)" + ) + case .trigger: + return compareText(lhs.record.schedule.summary, rhs.record.schedule.summary) + case .kind: + return compareInteger(kindRank(lhs.record.kind), kindRank(rhs.record.kind)) + case .state: + return compareInteger(stateRank(lhs.record.state), stateRank(rhs.record.state)) + case .owner: + return compareInteger( + ownershipRank(lhs.record.ownership), + ownershipRank(rhs.record.ownership) + ) + case .runs: + return compareInteger(lhs.linkedProcessCount, rhs.linkedProcessCount) + } + } + + private func compareText(_ lhs: String, _ rhs: String) -> ComparisonResult { + lhs.localizedStandardCompare(rhs) + } + + private func compareInteger(_ lhs: Int, _ rhs: Int) -> ComparisonResult { + lhs == rhs ? .orderedSame : (lhs < rhs ? .orderedAscending : .orderedDescending) + } + + private func kindRank(_ kind: AutomationKind) -> Int { + switch kind { + case .launchAgent: 0 + case .launchDaemon: 1 + case .loginItem: 2 + case .backgroundItem: 3 + case .cron: 4 + } + } + + private func stateRank(_ state: AutomationState) -> Int { + switch state { + case .running: 0 + case .idle: 1 + case .needsApproval: 2 + case .disabled: 3 + case .invalid: 4 + case .unresolved: 5 + } + } + + private func ownershipRank(_ ownership: AutomationOwnership) -> Int { + switch ownership { + case .user: 0 + case .thirdPartySystem: 1 + case .managed: 2 + case .appleSystem: 3 + } + } +} diff --git a/Sources/DevScopeCore/AutomationCapabilityPolicy.swift b/Sources/DevScopeCore/AutomationCapabilityPolicy.swift new file mode 100644 index 0000000..ea0854b --- /dev/null +++ b/Sources/DevScopeCore/AutomationCapabilityPolicy.swift @@ -0,0 +1,166 @@ +import Darwin +import Foundation + +public enum AutomationPathAuthorization { + public static func isApprovedDestination( + _ destination: URL, + approvedRoot: URL, + destinationExists: Bool, + verifiedMetadataURL: URL, + metadataIsSymbolicLink: Bool + ) -> Bool { + let destination = destination.standardizedFileURL + let root = approvedRoot.standardizedFileURL + let verifiedMetadataURL = verifiedMetadataURL.standardizedFileURL + let expectedMetadataURL = destinationExists + ? destination + : destination.deletingLastPathComponent().standardizedFileURL + return !metadataIsSymbolicLink + && destination.path.hasPrefix(root.path + "/") + && verifiedMetadataURL.path == expectedMetadataURL.path + } +} + +public struct AutomationCapabilityDecision: Equatable, Sendable { + public let capabilities: Set + public let reason: String? + + public init(capabilities: Set, reason: String?) { + self.capabilities = capabilities + self.reason = reason + } +} + +public struct AutomationCapabilityContext: Equatable, Sendable { + public let currentUID: uid_t + public let canonicalPathIsApproved: Bool + public let sourceOwnerUID: uid_t? + public let isSymlink: Bool + public let isManaged: Bool + public let implementedCapabilities: Set + + public init( + currentUID: uid_t, + canonicalPathIsApproved: Bool, + sourceOwnerUID: uid_t?, + isSymlink: Bool, + isManaged: Bool, + implementedCapabilities: Set = [] + ) { + self.currentUID = currentUID + self.canonicalPathIsApproved = canonicalPathIsApproved + self.sourceOwnerUID = sourceOwnerUID + self.isSymlink = isSymlink + self.isManaged = isManaged + self.implementedCapabilities = implementedCapabilities + } +} + +public enum AutomationCapabilityPolicy { + private static let inspectionCapabilities: Set = [.exportRecord] + private static let mutableSourceKinds: Set = [ + .launchAgent, + .legacyLoginItem, + .crontab, + ] + + public static func decision( + for record: AutomationRecord, + context: AutomationCapabilityContext + ) -> AutomationCapabilityDecision { + if context.isManaged || record.ownership == .managed { + return denied("This automation is managed by your organization.", context: context) + } + if record.ownership == .appleSystem { + return denied("Apple system services are inspection only.", context: context) + } + if record.ownership == .thirdPartySystem || record.kind == .launchDaemon { + return denied( + "System-wide automations require administrator management outside DevScope.", + context: context + ) + } + if record.sourceKind == .serviceManagement || record.kind == .backgroundItem { + return denied( + "Modern background items must be managed by their owning app or System Settings.", + context: context + ) + } + guard record.ownership == .user else { + return denied("Only automations owned by the current user can be managed.", context: context) + } + guard mutableSourceKinds.contains(record.sourceKind) else { + return denied("This automation source is inspection only.", context: context) + } + guard context.canonicalPathIsApproved else { + return denied(canonicalPathReason(for: record.sourceKind), context: context) + } + guard !context.isSymlink else { + return denied("Symbolic-link automation sources are inspection only.", context: context) + } + guard let ownerUID = context.sourceOwnerUID else { + return denied("DevScope could not verify who owns this automation source.", context: context) + } + guard ownerUID == context.currentUID, record.ownerUID == context.currentUID else { + return denied("The automation source is not owned by the current user.", context: context) + } + if record.sourceKind == .launchAgent, + record.enabledState == .unknown || record.loadState == .unknown + { + return denied( + "DevScope could not verify this LaunchAgent's enabled and loaded state, so management is unavailable.", + context: context + ) + } + + let capabilities = Set(AutomationCapability.allCases) + .intersection(context.implementedCapabilities) + let reason: String? + if capabilities == Set(AutomationCapability.allCases) { + reason = nil + } else { + reason = unavailableReason(for: record.sourceKind) + } + return AutomationCapabilityDecision(capabilities: capabilities, reason: reason) + } + + private static func denied( + _ reason: String, + context: AutomationCapabilityContext + ) -> AutomationCapabilityDecision { + AutomationCapabilityDecision( + capabilities: inspectionCapabilities.intersection(context.implementedCapabilities), + reason: reason + ) + } + + private static func unavailableReason(for sourceKind: AutomationSourceKind) -> String { + switch sourceKind { + case .crontab: + "Current-user crontab management is unavailable until recoverable source access is installed." + case .legacyLoginItem: + "Legacy login-item management is unavailable until recoverable source access is installed." + case .launchAgent: + "Some LaunchAgent management operations are not installed." + case .launchDaemon: + "System-wide launchd management is not installed." + case .serviceManagement: + "Modern background-item management is not installed." + } + } + + private static func canonicalPathReason(for sourceKind: AutomationSourceKind) -> String { + switch sourceKind { + case .launchAgent: + "The source resolves outside your LaunchAgents folder." + case .crontab: + "The recoverable source does not represent your current-user crontab." + case .legacyLoginItem: + "The recoverable source does not represent your current-user login items." + case .launchDaemon: + "The source resolves outside the approved launchd domain." + case .serviceManagement: + "The source resolves outside the approved background-item domain." + } + } +} diff --git a/Sources/DevScopeCore/AutomationEventDetector.swift b/Sources/DevScopeCore/AutomationEventDetector.swift new file mode 100644 index 0000000..5102088 --- /dev/null +++ b/Sources/DevScopeCore/AutomationEventDetector.swift @@ -0,0 +1,394 @@ +import Foundation + +public enum AutomationEvent: Equatable, Sendable { + case crossedLongRunningThreshold( + process: ProcessIdentity, + recordID: AutomationRecord.ID? + ) + case unexpectedExit(recordID: AutomationRecord.ID, process: ProcessIdentity) + case repeatedFailure(recordID: AutomationRecord.ID, observedExitCount: Int) +} + +public enum AutomationNotificationPreference: CaseIterable, Hashable, Sendable { + case crossedLongRunningThreshold + case unexpectedExit + case repeatedFailure +} + +public struct AutomationNotificationPreferences: Equatable, Sendable { + public var crossedLongRunningThreshold: Bool + public var unexpectedExit: Bool + public var repeatedFailure: Bool + + public init( + crossedLongRunningThreshold: Bool = false, + unexpectedExit: Bool = false, + repeatedFailure: Bool = false + ) { + self.crossedLongRunningThreshold = crossedLongRunningThreshold + self.unexpectedExit = unexpectedExit + self.repeatedFailure = repeatedFailure + } + + public subscript(preference: AutomationNotificationPreference) -> Bool { + get { + switch preference { + case .crossedLongRunningThreshold: crossedLongRunningThreshold + case .unexpectedExit: unexpectedExit + case .repeatedFailure: repeatedFailure + } + } + set { + switch preference { + case .crossedLongRunningThreshold: crossedLongRunningThreshold = newValue + case .unexpectedExit: unexpectedExit = newValue + case .repeatedFailure: repeatedFailure = newValue + } + } + } +} + +public enum AutomationNotificationAuthorizationAction: Equatable, Sendable { + case none + case requestAuthorization +} + +public struct AutomationNotificationContent: Equatable, Sendable { + public let title: String + public let body: String + + public init(title: String, body: String) { + self.title = title + self.body = body + } +} + +public struct AutomationNotificationPolicy: Sendable { + private enum AuthorizationState: Sendable { + case unknown + case requesting + case granted + case denied + } + + private enum EventIdentity: Hashable, Sendable { + case crossedThreshold(ProcessIdentity, AutomationRecord.ID?) + case unexpectedExit(AutomationRecord.ID, ProcessIdentity) + case repeatedFailure(AutomationRecord.ID, Int) + } + + public private(set) var preferences = AutomationNotificationPreferences() + private let maximumRetainedEventIdentities: Int + private var authorizationState = AuthorizationState.unknown + private var retainedEventIdentities: Set = [] + private var retainedEventOrder: [EventIdentity] = [] + + public init(maximumRetainedEventIdentities: Int = 256) { + self.maximumRetainedEventIdentities = max(1, maximumRetainedEventIdentities) + } + + public mutating func setPreference( + _ preference: AutomationNotificationPreference, + isEnabled: Bool + ) -> AutomationNotificationAuthorizationAction { + let wasEnabled = preferences[preference] + preferences[preference] = isEnabled + guard !wasEnabled, isEnabled, + authorizationState == .unknown || authorizationState == .denied else { return .none } + authorizationState = .requesting + return .requestAuthorization + } + + public mutating func recordAuthorizationResult(granted: Bool) { + authorizationState = granted ? .granted : .denied + } + + public mutating func notification( + for event: AutomationEvent + ) -> AutomationNotificationContent? { + guard authorizationState == .granted, + preferences[preference(for: event)] else { return nil } + let identity = identity(for: event) + guard retainedEventIdentities.insert(identity).inserted else { return nil } + retainedEventOrder.append(identity) + while retainedEventOrder.count > maximumRetainedEventIdentities { + retainedEventIdentities.remove(retainedEventOrder.removeFirst()) + } + return safeContent(for: event) + } + + public mutating func recordDeliveryFailure(for event: AutomationEvent) { + let failedIdentity = identity(for: event) + retainedEventIdentities.remove(failedIdentity) + retainedEventOrder.removeAll { $0 == failedIdentity } + } + + private func preference( + for event: AutomationEvent + ) -> AutomationNotificationPreference { + switch event { + case .crossedLongRunningThreshold: .crossedLongRunningThreshold + case .unexpectedExit: .unexpectedExit + case .repeatedFailure: .repeatedFailure + } + } + + private func identity(for event: AutomationEvent) -> EventIdentity { + switch event { + case let .crossedLongRunningThreshold(process, recordID): + .crossedThreshold(safeIdentity(process), recordID) + case let .unexpectedExit(recordID, process): + .unexpectedExit(recordID, safeIdentity(process)) + case let .repeatedFailure(recordID, observedExitCount): + .repeatedFailure(recordID, observedExitCount) + } + } + + private func safeIdentity(_ identity: ProcessIdentity) -> ProcessIdentity { + ProcessIdentity(pid: identity.pid, birthToken: identity.birthToken) + } + + private func safeContent(for event: AutomationEvent) -> AutomationNotificationContent { + switch event { + case .crossedLongRunningThreshold: + AutomationNotificationContent( + title: "Long-running process detected", + body: "A verified process crossed your long-running threshold." + ) + case .unexpectedExit: + AutomationNotificationContent( + title: "Automation stopped unexpectedly", + body: "A verified user automation exited unexpectedly." + ) + case .repeatedFailure: + AutomationNotificationContent( + title: "Automation is repeatedly stopping", + body: "A verified user automation stopped repeatedly." + ) + } + } +} + +public struct AutomationEventDetector: Sendable { + private struct PendingExit: Sendable { + let identity: ProcessIdentity + var completeAbsenceCount: Int + } + + private var pendingExits: [AutomationRecord.ID: PendingExit] = [:] + private var verifiedExitHistory: [AutomationRecord.ID: [Date]] = [:] + private var repeatedFailureActive: Set = [] + + public init() {} + + public mutating func events( + previous: AutomationPresentationSnapshot?, + current: AutomationPresentationSnapshot, + now: Date + ) -> [AutomationEvent] { + pruneFailureHistory(now: now) + guard let previous else { + pendingExits.removeAll() + return [] + } + guard previous.isProcessSnapshotComplete, current.isProcessSnapshotComplete else { + pendingExits.removeAll() + return [] + } + + let thresholdEvents = crossedThresholdEvents(previous: previous, current: current) + let exitEvents = verifiedExitEvents(previous: previous, current: current) + var repeatedEvents: [AutomationEvent] = [] + for event in exitEvents { + guard case let .unexpectedExit(recordID, _) = event else { continue } + if let repeated = recordVerifiedExit(recordID: recordID, now: now) { + repeatedEvents.append(repeated) + } + } + return thresholdEvents + exitEvents + repeatedEvents.sorted(by: eventPrecedes) + } + + private func crossedThresholdEvents( + previous: AutomationPresentationSnapshot, + current: AutomationPresentationSnapshot + ) -> [AutomationEvent] { + current.longRunningProcessIdentities + .subtracting(previous.longRunningProcessIdentities) + .compactMap { identity -> AutomationEvent? in + guard identity.birthToken != nil else { return nil } + return .crossedLongRunningThreshold( + process: safeEventIdentity(identity), + recordID: uniqueStrongRecordID(for: identity, in: current) + ) + } + .sorted(by: eventPrecedes) + } + + private mutating func verifiedExitEvents( + previous: AutomationPresentationSnapshot, + current: AutomationPresentationSnapshot + ) -> [AutomationEvent] { + let currentRecords = Dictionary( + current.inventory.records.map { ($0.id, $0) }, + uniquingKeysWith: { first, _ in first } + ) + let currentLinksByRecord = Dictionary( + grouping: current.allLinksByProcessID.values.flatMap { $0 }, + by: \.recordID + ) + var emitted: [AutomationEvent] = [] + + for recordID in pendingExits.keys.sorted(by: idPrecedes) { + guard var pending = pendingExits[recordID], + previousSourceWasHealthy(recordID: recordID, snapshot: previous), + canConfirmExit( + recordID: recordID, + identity: pending.identity, + current: current, + currentRecords: currentRecords, + currentLinksByRecord: currentLinksByRecord + ) else { + pendingExits.removeValue(forKey: recordID) + continue + } + pending.completeAbsenceCount += 1 + if pending.completeAbsenceCount >= 2 { + emitted.append(.unexpectedExit( + recordID: recordID, + process: safeEventIdentity(pending.identity) + )) + pendingExits.removeValue(forKey: recordID) + } else { + pendingExits[recordID] = pending + } + } + + let priorLinks = previous.allLinksByProcessID.values.flatMap { $0 }.sorted { lhs, rhs in + if lhs.recordID != rhs.recordID { return idPrecedes(lhs.recordID, rhs.recordID) } + return lhs.processIdentity.pid < rhs.processIdentity.pid + } + for link in priorLinks { + guard pendingExits[link.recordID] == nil, + !emitted.contains(where: { eventRecordID($0) == link.recordID }), + link.strength == .strong, + link.processIdentity.birthToken != nil, + uniqueStrongRecordID(for: link.processIdentity, in: previous) == link.recordID, + previousSourceWasHealthy(recordID: link.recordID, snapshot: previous), + canConfirmExit( + recordID: link.recordID, + identity: link.processIdentity, + current: current, + currentRecords: currentRecords, + currentLinksByRecord: currentLinksByRecord + ) else { continue } + pendingExits[link.recordID] = PendingExit( + identity: link.processIdentity, + completeAbsenceCount: 1 + ) + } + return emitted.sorted(by: eventPrecedes) + } + + private func uniqueStrongRecordID( + for identity: ProcessIdentity, + in snapshot: AutomationPresentationSnapshot + ) -> AutomationRecord.ID? { + let recordIDs = Set( + (snapshot.allLinksByProcessID[identity.pid] ?? []) + .filter { $0.strength == .strong && $0.processIdentity == identity } + .map(\.recordID) + ) + return recordIDs.count == 1 ? recordIDs.first : nil + } + + private func previousSourceWasHealthy( + recordID: AutomationRecord.ID, + snapshot: AutomationPresentationSnapshot + ) -> Bool { + guard let record = snapshot.inventory.records.first(where: { $0.id == recordID }) else { + return false + } + return snapshot.inventory.health[record.sourceKind]?.state == .healthy + } + + private func canConfirmExit( + recordID: AutomationRecord.ID, + identity: ProcessIdentity, + current: AutomationPresentationSnapshot, + currentRecords: [AutomationRecord.ID: AutomationRecord], + currentLinksByRecord: [AutomationRecord.ID: [AutomationProcessLink]] + ) -> Bool { + guard let record = currentRecords[recordID], + record.ownership == .user, + record.enabledState == .enabled, + record.schedule.triggers.contains(.keepAlive), + current.inventory.health[record.sourceKind]?.state == .healthy, + currentLinksByRecord[recordID]?.isEmpty != false else { return false } + return current.processIdentitiesByID[identity.pid] == nil + } + + private mutating func recordVerifiedExit( + recordID: AutomationRecord.ID, + now: Date + ) -> AutomationEvent? { + var history = verifiedExitHistory[recordID] ?? [] + history.append(now) + history.sort() + verifiedExitHistory[recordID] = history + guard history.count >= 3, !repeatedFailureActive.contains(recordID) else { return nil } + repeatedFailureActive.insert(recordID) + return .repeatedFailure(recordID: recordID, observedExitCount: history.count) + } + + private mutating func pruneFailureHistory(now: Date) { + for recordID in verifiedExitHistory.keys { + let retained = (verifiedExitHistory[recordID] ?? []).filter { timestamp in + let age = now.timeIntervalSince(timestamp) + return age >= 0 && age <= 600 + } + if retained.isEmpty { + verifiedExitHistory.removeValue(forKey: recordID) + } else { + verifiedExitHistory[recordID] = retained + } + if retained.count < 3 { repeatedFailureActive.remove(recordID) } + } + } + + private func safeEventIdentity(_ identity: ProcessIdentity) -> ProcessIdentity { + ProcessIdentity(pid: identity.pid, birthToken: identity.birthToken) + } + + private func eventRecordID(_ event: AutomationEvent) -> AutomationRecord.ID? { + switch event { + case let .crossedLongRunningThreshold(_, recordID): recordID + case let .unexpectedExit(recordID, _): recordID + case let .repeatedFailure(recordID, _): recordID + } + } + + private func eventPrecedes(_ lhs: AutomationEvent, _ rhs: AutomationEvent) -> Bool { + let lhsKey = eventKey(lhs) + let rhsKey = eventKey(rhs) + if lhsKey.rank != rhsKey.rank { return lhsKey.rank < rhsKey.rank } + if lhsKey.recordID != rhsKey.recordID { + return lhsKey.recordID.utf8.lexicographicallyPrecedes(rhsKey.recordID.utf8) + } + return lhsKey.pid < rhsKey.pid + } + + private func eventKey(_ event: AutomationEvent) -> (rank: Int, recordID: String, pid: Int32) { + switch event { + case let .crossedLongRunningThreshold(process, recordID): + (0, recordID?.rawValue ?? "", process.pid) + case let .unexpectedExit(recordID, process): + (1, recordID.rawValue, process.pid) + case let .repeatedFailure(recordID, _): + (2, recordID.rawValue, 0) + } + } + + private func idPrecedes(_ lhs: AutomationRecord.ID, _ rhs: AutomationRecord.ID) -> Bool { + lhs.rawValue.utf8.lexicographicallyPrecedes(rhs.rawValue.utf8) + } +} diff --git a/Sources/DevScopeCore/AutomationExecutors.swift b/Sources/DevScopeCore/AutomationExecutors.swift new file mode 100644 index 0000000..ce245de --- /dev/null +++ b/Sources/DevScopeCore/AutomationExecutors.swift @@ -0,0 +1,1007 @@ +import CryptoKit +import Darwin +import Foundation + +public enum AutomationExecutorError: Error, Equatable, Sendable { + case unsupportedSource + case unsupportedOperation + case missingSource + case invalidSource + case commandFailed(executable: String, status: Int32) + case commandUnavailable(executable: String) + case processControlUnavailable + case postconditionNotVerified +} + +public struct AutomationTerminationVerificationPolicy: Equatable, Sendable { + public let maximumAttempts: Int + public let interval: Duration + + public init(maximumAttempts: Int = 40, interval: Duration = .milliseconds(100)) { + self.maximumAttempts = max(1, maximumAttempts) + self.interval = max(.zero, interval) + } +} + +public typealias AutomationVerificationSleep = @Sendable (Duration) async throws -> Void + +private func oldBirthExited( + _ expected: ProcessIdentity, + resolver: ProcessKiller.IdentityResolver, + policy: AutomationTerminationVerificationPolicy, + sleep: AutomationVerificationSleep +) async throws -> Bool { + for attempt in 0.. AutomationExecutorResult { + try await apply(operation, to: record, linkedProcesses: [], proposedSourceURL: nil) + } + + public func apply( + _ operation: AutomationOperation, + to record: AutomationRecord, + linkedProcesses: [ClassifiedDevProcess], + proposedSourceURL: URL? + ) async throws -> AutomationExecutorResult { + guard record.kind == .launchAgent, + record.sourceKind == .launchAgent, + record.ownership == .user, + record.ownerUID == guiUID, + isValidLaunchdLabel(record.label) + else { throw AutomationExecutorError.unsupportedSource } + + switch operation { + case .startNow: + let loaded: Bool + switch record.loadState { + case .loaded: + loaded = true + case .unloaded: + loaded = false + case .unknown: + loaded = try await verifiedLoadedState(for: record) + } + if !loaded { + guard let sourceURL = canonicalSourceURL(for: record) else { + throw AutomationExecutorError.missingSource + } + try await requireLaunchctlSuccess(["bootstrap", domainTarget, sourceURL.path]) + } + try await requireLaunchctlSuccess(["kickstart", serviceTarget(for: record)]) + let evidence = loaded + ? "launchd accepted the typed kickstart request." + : "launchd loaded the canonical source and accepted the typed kickstart request." + return AutomationExecutorResult( + postconditions: [.targetResolved, .currentRunStarted], + evidence: [evidence] + ) + case .disable: + try await requireLaunchctlSuccess(["disable", serviceTarget(for: record)]) + return AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled], + evidence: ["launchd accepted the typed disable request."] + ) + case .enable: + guard let sourceURL = canonicalSourceURL(for: record) else { + throw AutomationExecutorError.missingSource + } + let loaded = try await verifiedLoadedState(for: record) + try await requireLaunchctlSuccess(["enable", serviceTarget(for: record)]) + if !loaded { + try await requireLaunchctlSuccess(["bootstrap", domainTarget, sourceURL.path]) + } + return AutomationExecutorResult( + postconditions: [.futureLaunchesEnabled, .targetResolved], + evidence: [ + loaded + ? "launchd accepted enable for the resolved service target." + : "launchd accepted enable and bootstrap for the canonical source.", + ] + ) + case .stopCurrentRun: + try await requireLaunchctlSuccess(["bootout", serviceTarget(for: record)]) + return AutomationExecutorResult( + postconditions: [.noLinkedProcess], + evidence: ["launchd unloaded the exact user service target."] + ) + case .disableAndStop: + try await requireLaunchctlSuccess(["disable", serviceTarget(for: record)]) + try await requireLaunchctlSuccess(["bootout", serviceTarget(for: record)]) + return AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled, .noLinkedProcess], + evidence: ["launchd disabled and unloaded the exact user service target."] + ) + case .edit: + let proof = try sourceProof(at: proposedSourceURL) + if record.loadState == .loaded { + try await requireLaunchctlSuccess(["bootout", serviceTarget(for: record)]) + try await requireLaunchctlSuccess(["bootstrap", domainTarget, proof.url.path]) + } + return AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(proof.checksum)], + evidence: ["The validated LaunchAgent source was installed."] + ) + case .duplicate(let payload): + let proof = try sourceProof(at: proposedSourceURL) + guard isValidLaunchdLabel(payload.label), payload.label != record.label else { + throw AutomationExecutorError.invalidSource + } + try await requireLaunchctlSuccess(["disable", "gui/\(guiUID)/\(payload.label)"]) + return AutomationExecutorResult( + postconditions: [ + .sourceInstalled, + .sourceChecksum(proof.checksum), + .futureLaunchesDisabled, + ], + evidence: ["The validated LaunchAgent copy was installed disabled and unloaded."] + ) + case .importRecord: + let proof = try sourceProof(at: proposedSourceURL) + return AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(proof.checksum)], + evidence: ["The validated LaunchAgent source was installed without loading it."] + ) + case .restore: + let proof = try sourceProof(at: proposedSourceURL) + return AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(proof.checksum)], + evidence: ["The validated LaunchAgent recovery source was installed."] + ) + case .remove: + let target = serviceTarget(for: record) + let loaded = try await verifiedLoadedState(for: record) + if loaded { + try await requireLaunchctlSuccess(["bootout", target]) + } else { + guard let stillExistingSourceURL = proposedSourceURL?.standardizedFileURL else { + throw AutomationExecutorError.missingSource + } + try await requireLaunchctlSuccess([ + "bootout", domainTarget, stillExistingSourceURL.path, + ]) + } + let finalResolution = try await runLaunchctlPrint(target) + guard LaunchctlServiceTargetClassifier.classify( + finalResolution, + label: record.label, + guiUID: guiUID + ) == .absent else { + throw AutomationExecutorError.postconditionNotVerified + } + return AutomationExecutorResult( + postconditions: [.sourceRemoved, .targetUnresolved], + evidence: ["The LaunchAgent source was removed and its service target no longer resolves."] + ) + default: + throw AutomationExecutorError.unsupportedOperation + } + } + + public func restorePreTransactionState( + _ state: AutomationPreTransactionState, + for record: AutomationRecord, + recovery: AutomationVerifiedRecoveryInput, + linkedProcesses: [ClassifiedDevProcess], + expectedAppliedState: AutomationExecutorAppliedState? + ) async throws -> AutomationExecutorResult { + guard record.kind == .launchAgent, + record.sourceKind == .launchAgent, + record.ownership == .user, + record.ownerUID == guiUID, + isValidLaunchdLabel(record.label), + let sourceURL = canonicalSourceURL(for: record) + else { throw AutomationExecutorError.unsupportedSource } + switch state.enabledState { + case .enabled: + try await requireLaunchctlSuccess(["enable", serviceTarget(for: record)]) + case .disabled: + try await requireLaunchctlSuccess(["disable", serviceTarget(for: record)]) + case .unknown: + throw AutomationExecutorError.postconditionNotVerified + } + let isLoaded = try await verifiedLoadedState(for: record) + switch state.loadState { + case .loaded where !isLoaded: + try await requireLaunchctlSuccess(["bootstrap", domainTarget, sourceURL.path]) + case .unloaded where isLoaded: + try await requireLaunchctlSuccess(["bootout", serviceTarget(for: record)]) + case .unknown: + throw AutomationExecutorError.postconditionNotVerified + default: + break + } + return AutomationExecutorResult( + postconditions: [.preTransactionStateRestored(state)], + evidence: ["launchd accepted the exact enabled and loaded recovery state."] + ) + } + + private func serviceTarget(for record: AutomationRecord) -> String { + "gui/\(guiUID)/\(record.label)" + } + + private func isValidLaunchdLabel(_ label: String) -> Bool { + !label.isEmpty && !label.contains("/") && !label.contains("\0") + } + + private var domainTarget: String { "gui/\(guiUID)" } + + private func canonicalSourceURL(for record: AutomationRecord) -> URL? { + guard let sourceURL = record.sourceURL, sourceURL.path.hasPrefix("/") else { return nil } + return sourceURL.standardizedFileURL + } + + private func runLaunchctl(_ arguments: [String]) async throws -> AutomationCommandResult { + do { + return try await runner.run(AutomationCommand( + executable: "/bin/launchctl", + arguments: arguments + )) + } catch { + throw AutomationExecutorError.commandUnavailable(executable: "/bin/launchctl") + } + } + + private func verifiedLoadedState(for record: AutomationRecord) async throws -> Bool { + let result = try await runLaunchctlPrint(serviceTarget(for: record)) + switch LaunchctlServiceTargetClassifier.classify( + result, + label: record.label, + guiUID: guiUID + ) { + case .loaded: + return true + case .absent: + return false + case .unknown: + throw AutomationExecutorError.commandFailed( + executable: "/bin/launchctl", + status: result.status + ) + } + } + + private func runLaunchctlPrint(_ target: String) async throws -> AutomationCommandResult { + do { + return try await runner.run(AutomationCommand( + executable: "/bin/launchctl", + arguments: ["print", target], + environment: ["LC_ALL": "C"] + )) + } catch { + throw AutomationExecutorError.commandUnavailable(executable: "/bin/launchctl") + } + } + + private func requireLaunchctlSuccess(_ arguments: [String]) async throws { + let result = try await runLaunchctl(arguments) + guard result.status == 0 else { + throw AutomationExecutorError.commandFailed( + executable: "/bin/launchctl", + status: result.status + ) + } + } + + private func sourceProof(at url: URL?) throws -> (url: URL, checksum: String) { + guard let fileSystem, let url else { throw AutomationExecutorError.missingSource } + let canonicalURL = url.standardizedFileURL + let data: Data + do { + data = try fileSystem.read(canonicalURL) + } catch { + throw AutomationExecutorError.missingSource + } + guard (try? LaunchdPlistParser.parse( + data: data, + sourceURL: canonicalURL, + ownerUID: guiUID, + ownership: .user + )) != nil else { throw AutomationExecutorError.invalidSource } + return (canonicalURL, Self.checksum(data)) + } + + private static func checksum(_ data: Data) -> String { + SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined() + } +} + +public struct CronAutomationExecutor: AutomationMutationApplying { + public typealias ProcessSnapshot = @Sendable () async throws -> [DevProcess] + private let runner: any AutomationCommandRunning + private let fileSystem: any AutomationFileSystem + private let currentUID: uid_t + private let processKiller: ProcessKiller + private let currentProcessID: Int32 + private let processIdentityResolver: ProcessKiller.IdentityResolver + private let processSnapshot: ProcessSnapshot + private let now: @Sendable () -> Date + private let terminationVerificationPolicy: AutomationTerminationVerificationPolicy + private let verificationSleep: AutomationVerificationSleep + + public init( + runner: any AutomationCommandRunning, + fileSystem: any AutomationFileSystem, + currentUID: uid_t + ) { + self.init( + runner: runner, + fileSystem: fileSystem, + currentUID: currentUID, + processKiller: ProcessKiller(), + currentProcessID: getpid(), + processIdentityResolver: ProcessKiller.resolveLiveIdentity, + processSnapshot: { throw AutomationExecutorError.processControlUnavailable }, + now: Date.init, + terminationVerificationPolicy: AutomationTerminationVerificationPolicy(), + verificationSleep: { try await Task.sleep(for: $0) } + ) + } + + public init( + runner: any AutomationCommandRunning, + fileSystem: any AutomationFileSystem, + currentUID: uid_t, + processSnapshot: @escaping ProcessSnapshot + ) { + self.init( + runner: runner, + fileSystem: fileSystem, + currentUID: currentUID, + processKiller: ProcessKiller(), + currentProcessID: getpid(), + processIdentityResolver: ProcessKiller.resolveLiveIdentity, + processSnapshot: processSnapshot, + now: Date.init, + terminationVerificationPolicy: AutomationTerminationVerificationPolicy(), + verificationSleep: { try await Task.sleep(for: $0) } + ) + } + + init( + runner: any AutomationCommandRunning, + fileSystem: any AutomationFileSystem, + currentUID: uid_t, + processKiller: ProcessKiller, + currentProcessID: Int32, + processIdentityResolver: @escaping ProcessKiller.IdentityResolver, + processSnapshot: @escaping ProcessSnapshot, + now: @escaping @Sendable () -> Date, + terminationVerificationPolicy: AutomationTerminationVerificationPolicy = .init(), + verificationSleep: @escaping AutomationVerificationSleep = { try await Task.sleep(for: $0) } + ) { + self.runner = runner + self.fileSystem = fileSystem + self.currentUID = currentUID + self.processKiller = processKiller + self.currentProcessID = currentProcessID + self.processIdentityResolver = processIdentityResolver + self.processSnapshot = processSnapshot + self.now = now + self.terminationVerificationPolicy = terminationVerificationPolicy + self.verificationSleep = verificationSleep + } + + public func apply( + _ operation: AutomationOperation, + to record: AutomationRecord + ) async throws -> AutomationExecutorResult { + try await apply(operation, to: record, linkedProcesses: [], proposedSourceURL: nil) + } + + public func apply( + _ operation: AutomationOperation, + to record: AutomationRecord, + linkedProcesses: [ClassifiedDevProcess], + proposedSourceURL: URL? + ) async throws -> AutomationExecutorResult { + guard record.kind == .cron, + record.sourceKind == .crontab, + record.ownership == .user, + record.ownerUID == currentUID + else { throw AutomationExecutorError.unsupportedSource } + + switch operation { + case .startNow: + throw AutomationExecutorError.unsupportedOperation + case .confirmedRunToCompletion(let confirmation): + guard confirmation.recordID == record.id, + let sourceChecksum = record.sourceChecksum, + confirmation.sourceChecksum == sourceChecksum, + let command = record.commandSignature, + !command.isEmpty, + confirmation.exactCommand == command, + !command.contains("\0") + else { throw AutomationExecutorError.invalidSource } + let shell = record.environment["SHELL"] ?? "/bin/sh" + guard shell.hasPrefix("/"), !shell.contains("\0") else { + throw AutomationExecutorError.invalidSource + } + let result = try await run(AutomationCommand( + executable: shell, + arguments: ["-c", command], + environment: record.environment + )) + guard result.status == 0 else { + throw AutomationExecutorError.commandFailed(executable: shell, status: result.status) + } + return AutomationExecutorResult( + postconditions: [.targetResolved, .runCompleted], + evidence: ["The confirmed cron command completed with exit status 0."] + ) + case .edit, .duplicate, .importRecord, .restore: + let checksum = try await installDocument(at: proposedSourceURL) + return AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(checksum)], + evidence: ["The complete current-user crontab document was installed and verified."], + appliedState: .cronLiveDocument(normalizedChecksum: checksum) + ) + case .enable: + let checksum = try await installDocument(at: proposedSourceURL) + return AutomationExecutorResult( + postconditions: [ + .sourceInstalled, + .sourceChecksum(checksum), + .futureLaunchesEnabled, + ], + evidence: ["The complete current-user crontab document was installed and verified."], + appliedState: .cronLiveDocument(normalizedChecksum: checksum) + ) + case .disable: + let checksum = try await installDocument(at: proposedSourceURL) + return AutomationExecutorResult( + postconditions: [ + .sourceInstalled, + .sourceChecksum(checksum), + .futureLaunchesDisabled, + ], + evidence: ["The complete current-user crontab document was installed and verified."], + appliedState: .cronLiveDocument(normalizedChecksum: checksum) + ) + case .stopCurrentRun: + try await stopStronglyLinkedProcesses(linkedProcesses, for: record) + return AutomationExecutorResult( + postconditions: [.noLinkedProcess], + evidence: ["Every exact cron carrier birth identity exited after SIGTERM."] + ) + case .disableAndStop: + let checksum = try await installDocument(at: proposedSourceURL) + do { + try await stopStronglyLinkedProcesses(linkedProcesses, for: record) + } catch { + throw AutomationExecutorMutationFailure( + error: (error as? AutomationExecutorError) ?? .processControlUnavailable, + appliedState: .cronLiveDocument(normalizedChecksum: checksum) + ) + } + return AutomationExecutorResult( + postconditions: [ + .sourceInstalled, + .sourceChecksum(checksum), + .futureLaunchesDisabled, + .noLinkedProcess, + ], + evidence: ["The complete crontab was installed and every controlled cron carrier exited."], + appliedState: .cronLiveDocument(normalizedChecksum: checksum) + ) + case .remove: + let checksum = try await installDocument(at: proposedSourceURL) + return AutomationExecutorResult( + postconditions: [.sourceRemoved, .sourceChecksum(checksum)], + evidence: ["The complete current-user crontab document without the selected entry was installed and verified."], + appliedState: .cronLiveDocument(normalizedChecksum: checksum) + ) + default: + throw AutomationExecutorError.unsupportedOperation + } + } + + public func restorePreTransactionState( + _ state: AutomationPreTransactionState, + for record: AutomationRecord, + recovery: AutomationVerifiedRecoveryInput, + linkedProcesses: [ClassifiedDevProcess], + expectedAppliedState: AutomationExecutorAppliedState? + ) async throws -> AutomationExecutorResult { + guard record.kind == .cron, + record.sourceKind == .crontab, + record.ownership == .user, + record.ownerUID == currentUID, + state.sourceExisted, + state.loadState == .unknown + else { throw AutomationExecutorError.postconditionNotVerified } + guard case .cronLiveDocument(let ownedChecksum) = expectedAppliedState else { + throw AutomationExecutorError.postconditionNotVerified + } + guard Self.rawChecksum(recovery.data) == recovery.checksum else { + throw AutomationExecutorError.postconditionNotVerified + } + let prepared = try prepareDocument( + data: recovery.data, + nextTo: recovery.backupURL, + expectedRawChecksum: recovery.checksum + ) + let document = CronParser.parse(String(decoding: prepared.data, as: UTF8.self)) + let restoredEnabledState = document.entries.first(where: { + CronParser.recordID(for: $0, ownerUID: currentUID) == record.id + }).map { $0.isEnabled ? AutomationEnabledState.enabled : .disabled } + guard restoredEnabledState == state.enabledState else { + throw AutomationExecutorError.postconditionNotVerified + } + + let live = try await run(AutomationCommand( + executable: "/usr/bin/crontab", + arguments: ["-l"], + environment: ["LC_ALL": "C"] + )) + guard live.status == 0, + CronDocumentChecksum.checksum(live.standardOutput) == ownedChecksum + else { throw AutomationExecutorError.postconditionNotVerified } + + // crontab(1) exposes no atomic compare-and-swap. Keep this install as the immediately + // following external operation after ownership proof; an unavoidable process-launch gap remains. + let installResult = try await run(AutomationCommand( + executable: "/usr/bin/crontab", + arguments: [prepared.temporaryURL.path] + )) + guard installResult.status == 0 else { + throw AutomationExecutorError.commandFailed( + executable: "/usr/bin/crontab", + status: installResult.status + ) + } + let listed = try await run(AutomationCommand( + executable: "/usr/bin/crontab", + arguments: ["-l"], + environment: ["LC_ALL": "C"] + )) + guard listed.status == 0, + CronDocumentChecksum.checksum(listed.standardOutput) == prepared.checksum + else { + throw AutomationExecutorError.postconditionNotVerified + } + var postconditions: Set = [ + .sourceChecksum(prepared.checksum), + ] + if state.linkedProcesses.isEmpty { + postconditions.insert(.preTransactionStateRestored(state)) + } + let result = AutomationExecutorResult( + postconditions: postconditions, + evidence: [ + "The authenticated crontab backup was staged before the final ownership proof, then installed and verified; crontab provides no atomic compare-and-swap.", + ] + ) + try removeStagedFile(prepared.staged) + return result + } + + private func run(_ command: AutomationCommand) async throws -> AutomationCommandResult { + do { + return try await runner.run(command) + } catch { + throw AutomationExecutorError.commandUnavailable(executable: command.executable) + } + } + + private func installDocument(at sourceURL: URL?) async throws -> String { + let prepared = try prepareDocument(at: sourceURL) + let checksum = try await installPreparedDocument(prepared) + try removeStagedFile(prepared.staged) + return checksum + } + + private struct PreparedCronDocument { + let data: Data + let staged: AutomationStagedFile + let checksum: String + + var temporaryURL: URL { staged.url } + } + + private func prepareDocument(at sourceURL: URL?) throws -> PreparedCronDocument { + guard let sourceURL else { throw AutomationExecutorError.missingSource } + let data: Data + do { + data = try fileSystem.read(sourceURL.standardizedFileURL) + } catch { + throw AutomationExecutorError.missingSource + } + return try prepareDocument(data: data, nextTo: sourceURL, expectedRawChecksum: nil) + } + + private func prepareDocument( + data: Data, + nextTo sourceURL: URL, + expectedRawChecksum: String? + ) throws -> PreparedCronDocument { + guard expectedRawChecksum == nil || Self.rawChecksum(data) == expectedRawChecksum, + let text = String(data: data, encoding: .utf8), + CronParser.parse(text).invalidLines.isEmpty + else { throw AutomationExecutorError.invalidSource } + + let staged: AutomationStagedFile + do { + let parent = sourceURL.deletingLastPathComponent().standardizedFileURL + let parentMetadata = try fileSystem.metadata(for: parent) + guard let authorization = AutomationDirectoryAuthorization( + directoryURL: parent, + resourceIdentifier: parentMetadata.resourceIdentifier + ) else { throw AutomationExecutorError.missingSource } + staged = try fileSystem.writeStagedFile( + nextTo: sourceURL.standardizedFileURL, + data: data, + permissions: 0o600, + authorization: authorization + ) + } catch { + throw AutomationExecutorError.missingSource + } + let stagedData: Data + do { + stagedData = try fileSystem.read(staged.url) + } catch { + try removeStagedFile(staged) + throw AutomationExecutorError.missingSource + } + guard stagedData == data, + expectedRawChecksum == nil || Self.rawChecksum(stagedData) == expectedRawChecksum + else { + try removeStagedFile(staged) + throw AutomationExecutorError.postconditionNotVerified + } + guard let intendedChecksum = CronDocumentChecksum.checksum(data) else { + try removeStagedFile(staged) + throw AutomationExecutorError.invalidSource + } + return PreparedCronDocument( + data: data, + staged: staged, + checksum: intendedChecksum + ) + } + + private func removeStagedFile(_ staged: AutomationStagedFile) throws { + switch try fileSystem.removeItem(staged.authorization) { + case .committed: + return + case .unchanged: + throw AutomationFilePartialMutation( + kind: .remove, + commitState: .unknown, + observedFiles: [staged.authorization], + recoveryHandle: staged.authorization, + resultURL: staged.url + ) + } + } + + private static func rawChecksum(_ data: Data) -> String { + SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined() + } + + private func installPreparedDocument(_ prepared: PreparedCronDocument) async throws -> String { + let installResult = try await run(AutomationCommand( + executable: "/usr/bin/crontab", + arguments: [prepared.temporaryURL.path] + )) + guard installResult.status == 0 else { + throw AutomationExecutorError.commandFailed( + executable: "/usr/bin/crontab", + status: installResult.status + ) + } + + let appliedState = AutomationExecutorAppliedState.cronLiveDocument( + normalizedChecksum: prepared.checksum + ) + + let listed: AutomationCommandResult + do { + listed = try await run(AutomationCommand( + executable: "/usr/bin/crontab", + arguments: ["-l"], + environment: ["LC_ALL": "C"] + )) + } catch let error as AutomationExecutorError { + throw AutomationExecutorMutationFailure(error: error, appliedState: appliedState) + } + guard listed.status == 0, + CronDocumentChecksum.checksum(listed.standardOutput) + == prepared.checksum + else { + throw AutomationExecutorMutationFailure( + error: .postconditionNotVerified, + appliedState: appliedState + ) + } + return prepared.checksum + } + + private func stopStronglyLinkedProcesses( + _ linkedProcesses: [ClassifiedDevProcess], + for record: AutomationRecord + ) async throws { + guard !linkedProcesses.isEmpty, + linkedProcesses.allSatisfy({ $0.process.birthToken != nil }) + else { throw AutomationExecutorError.processControlUnavailable } + let freshProcesses: [DevProcess] + do { + freshProcesses = try await processSnapshot() + } catch { + throw AutomationExecutorError.processControlUnavailable + } + let expected = Set(linkedProcesses.map { ProcessIdentity(process: $0.process) }) + let proven = Set(AutomationProcessCorrelator.links( + records: [record], + processes: freshProcesses, + now: now() + ).filter { $0.strength == .strong }.map(\.processIdentity)) + guard expected.isSubset(of: proven) else { + throw AutomationExecutorError.processControlUnavailable + } + for process in linkedProcesses.sorted(by: { $0.process.pid < $1.process.pid }) { + _ = try processKiller.terminate(process, currentProcessID: currentProcessID) + } + for identity in expected { + guard try await oldBirthExited( + identity, + resolver: processIdentityResolver, + policy: terminationVerificationPolicy, + sleep: verificationSleep + ) else { + throw AutomationExecutorError.postconditionNotVerified + } + } + } +} + +public struct LegacyLoginItemAutomationExecutor: AutomationMutationApplying { + private static let fixedMutationProgram = """ + on run argv + if (count of argv) is not 3 then error "invalid arguments" + set requestedAction to item 1 of argv + set targetPath to item 2 of argv + set hiddenText to item 3 of argv + if hiddenText is "true" then + set hiddenAtLogin to true + else if hiddenText is "false" then + set hiddenAtLogin to false + else + error "invalid hidden state" + end if + tell application "System Events" + if requestedAction is "add" then + if not (exists login item 1 whose path is targetPath) then + make new login item at end with properties {path:targetPath, hidden:hiddenAtLogin} + end if + else if requestedAction is "remove" then + delete every login item whose path is targetPath + else + error "invalid action" + end if + end tell + end run + """ + + private let runner: any AutomationCommandRunning + private let listing: any LegacyLoginItemListing + private let currentUID: uid_t + + public init( + runner: any AutomationCommandRunning, + listing: any LegacyLoginItemListing, + currentUID: uid_t + ) { + self.runner = runner + self.listing = listing + self.currentUID = currentUID + } + + public func apply( + _ operation: AutomationOperation, + to record: AutomationRecord + ) async throws -> AutomationExecutorResult { + try await apply(operation, to: record, linkedProcesses: [], proposedSourceURL: nil) + } + + public func apply( + _ operation: AutomationOperation, + to record: AutomationRecord, + linkedProcesses: [ClassifiedDevProcess], + proposedSourceURL: URL? + ) async throws -> AutomationExecutorResult { + guard record.kind == .loginItem, + record.sourceKind == .legacyLoginItem, + record.ownership == .user, + record.ownerUID == currentUID, + let path = targetPath(for: record) + else { throw AutomationExecutorError.unsupportedSource } + + switch operation { + case .enable: + try await mutate("add", path: path, isHidden: false) + guard try await contains(path) else { + throw AutomationExecutorError.postconditionNotVerified + } + return AutomationExecutorResult( + postconditions: [.futureLaunchesEnabled, .sourceInstalled], + evidence: ["The current-user legacy login item is present."] + ) + case .disable: + try await mutate("remove", path: path, isHidden: false) + guard try await !contains(path) else { + throw AutomationExecutorError.postconditionNotVerified + } + return AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled], + evidence: ["The current-user legacy login item is absent without claiming its process stopped."] + ) + case .remove: + try await mutate("remove", path: path, isHidden: false) + guard try await !contains(path) else { + throw AutomationExecutorError.postconditionNotVerified + } + return AutomationExecutorResult( + postconditions: [.sourceRemoved], + evidence: ["The current-user legacy login item is absent."] + ) + case .startNow: + let result = try await run(AutomationCommand( + executable: "/usr/bin/open", + arguments: ["--", path] + )) + guard result.status == 0 else { + throw AutomationExecutorError.commandFailed(executable: "/usr/bin/open", status: result.status) + } + return AutomationExecutorResult( + postconditions: [.targetResolved, .currentRunStarted], + evidence: ["macOS accepted the typed open request for the login-item path."] + ) + default: + throw AutomationExecutorError.unsupportedOperation + } + } + + public func restorePreTransactionState( + _ state: AutomationPreTransactionState, + for record: AutomationRecord, + recovery: AutomationVerifiedRecoveryInput, + linkedProcesses: [ClassifiedDevProcess], + expectedAppliedState: AutomationExecutorAppliedState? + ) async throws -> AutomationExecutorResult { + guard record.kind == .loginItem, + record.sourceKind == .legacyLoginItem, + record.ownership == .user, + record.ownerUID == currentUID, + state.loadState == .unknown, + state.linkedProcesses.isEmpty, + let path = targetPath(for: record) + else { throw AutomationExecutorError.postconditionNotVerified } + guard Self.rawChecksum(recovery.data) == recovery.checksum else { + throw AutomationExecutorError.postconditionNotVerified + } + let recoveryDescriptor = try recoveryDescriptor( + recovery.data, + for: record, + path: path + ) + switch state.enabledState { + case .enabled: + try await mutate("add", path: path, isHidden: recoveryDescriptor.isHidden) + guard try await descriptor(for: path)?.isHidden == recoveryDescriptor.isHidden else { + throw AutomationExecutorError.postconditionNotVerified + } + case .disabled: + try await mutate("remove", path: path, isHidden: recoveryDescriptor.isHidden) + guard try await !contains(path) else { + throw AutomationExecutorError.postconditionNotVerified + } + case .unknown: + throw AutomationExecutorError.postconditionNotVerified + } + return AutomationExecutorResult( + postconditions: [.preTransactionStateRestored(state)], + evidence: ["The exact current-user legacy login-item presence state was restored."] + ) + } + + private func targetPath(for record: AutomationRecord) -> String? { + guard let path = record.executable, path.hasPrefix("/"), !path.contains("\0") else { + return nil + } + return URL(fileURLWithPath: path).standardizedFileURL.path + } + + private func mutate(_ action: String, path: String, isHidden: Bool) async throws { + let result = try await run(AutomationCommand( + executable: "/usr/bin/osascript", + arguments: ["-e", Self.fixedMutationProgram, "--", action, path, isHidden ? "true" : "false"] + )) + guard result.status == 0 else { + throw AutomationExecutorError.commandFailed(executable: "/usr/bin/osascript", status: result.status) + } + } + + private func contains(_ path: String) async throws -> Bool { + try await descriptor(for: path) != nil + } + + private func descriptor(for path: String) async throws -> LegacyLoginItemDescriptor? { + do { + return try await listing.currentUserLoginItems().first { + URL(fileURLWithPath: $0.path).standardizedFileURL.path == path + } + } catch { + throw AutomationExecutorError.postconditionNotVerified + } + } + + private func recoveryDescriptor( + _ data: Data, + for record: AutomationRecord, + path: String + ) throws -> LegacyLoginItemRecoveryDescriptor { + guard let descriptor = try? JSONDecoder().decode( + LegacyLoginItemRecoveryDescriptor.self, + from: data + ), descriptor.version == 1, + descriptor.recordID == record.id.rawValue, + descriptor.ownerUID == currentUID, + descriptor.name == record.label, + descriptor.path.hasPrefix("/"), + !descriptor.path.contains("\0"), + URL(fileURLWithPath: descriptor.path).standardizedFileURL.path == path, + record.id == AutomationRecord.ID( + source: .legacyLoginItem, + ownerUID: currentUID, + label: descriptor.name, + sourcePath: path + ) + else { throw AutomationExecutorError.invalidSource } + return descriptor + } + + private func run(_ command: AutomationCommand) async throws -> AutomationCommandResult { + do { + return try await runner.run(command) + } catch { + throw AutomationExecutorError.commandUnavailable(executable: command.executable) + } + } + + private static func rawChecksum(_ data: Data) -> String { + SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined() + } +} diff --git a/Sources/DevScopeCore/AutomationInventoryService.swift b/Sources/DevScopeCore/AutomationInventoryService.swift new file mode 100644 index 0000000..693ec6e --- /dev/null +++ b/Sources/DevScopeCore/AutomationInventoryService.swift @@ -0,0 +1,615 @@ +import Foundation + +public struct AutomationInventorySnapshot: Equatable, Sendable { + public let generation: UInt64 + public let records: [AutomationRecord] + public let health: [AutomationSourceKind: AutomationSourceHealth] + public let refreshedAt: Date + + public init( + generation: UInt64, + records: [AutomationRecord], + health: [AutomationSourceKind: AutomationSourceHealth], + refreshedAt: Date + ) { + self.generation = generation + self.records = records + self.health = health + self.refreshedAt = refreshedAt + } +} + +public actor AutomationInventoryService { + private struct InFlightRefresh: Sendable { + let id: UInt64 + let task: Task + } + + private struct InFlightSourceRefresh: Sendable { + let id: UInt64 + let task: Task + } + + private let sources: [any AutomationSource] + private let minimumRefreshInterval: TimeInterval + private let sourceTimeout: Duration + private let now: @Sendable () -> Date + private var cachedSnapshot: AutomationInventorySnapshot? + private var inFlightRefresh: InFlightRefresh? + private var inFlightSourceRefreshes: [Int: InFlightSourceRefresh] = [:] + private var nextRefreshID: UInt64 = 0 + private var nextSourceRefreshID: UInt64 = 0 + + public init( + sources: [any AutomationSource], + minimumRefreshInterval: TimeInterval = 60, + sourceTimeout: Duration = .seconds(10), + now: @escaping @Sendable () -> Date = Date.init + ) { + self.sources = sources + self.minimumRefreshInterval = max(0, minimumRefreshInterval) + self.sourceTimeout = max(.milliseconds(1), sourceTimeout) + self.now = now + } + + public func refresh(force: Bool = false) async -> AutomationInventorySnapshot { + let refreshDate = now() + if !force, + let cachedSnapshot, + refreshDate.timeIntervalSince(cachedSnapshot.refreshedAt) < minimumRefreshInterval + { + return cachedSnapshot + } + if let inFlightRefresh { + return await complete(inFlightRefresh) + } + + let generation = (cachedSnapshot?.generation ?? 0) + 1 + let sourceTimeout = self.sourceTimeout + let task = Task { + let sourceSnapshots = await refreshSources( + timeout: sourceTimeout, + refreshedAt: refreshDate + ) + let health = Self.aggregatedHealth(sourceSnapshots) + let records = Self.deduplicated(sourceSnapshots.flatMap(\.records)) + return AutomationInventorySnapshot( + generation: generation, + records: records, + health: health, + refreshedAt: refreshDate + ) + } + nextRefreshID &+= 1 + let inFlight = InFlightRefresh(id: nextRefreshID, task: task) + inFlightRefresh = inFlight + return await complete(inFlight) + } + + public func refreshAfterCurrent() async -> AutomationInventorySnapshot { + if let inFlightRefresh { + _ = await complete(inFlightRefresh) + } + return await refresh(force: true) + } + + private func complete( + _ refresh: InFlightRefresh + ) async -> AutomationInventorySnapshot { + let snapshot = await refresh.task.value + if inFlightRefresh?.id == refresh.id { + cachedSnapshot = snapshot + inFlightRefresh = nil + } + return snapshot + } + + private func refreshSources( + timeout: Duration, + refreshedAt: Date + ) async -> [AutomationSourceSnapshot] { + let sources = self.sources + return await withTaskGroup( + of: (Int, AutomationSourceSnapshot).self, + returning: [AutomationSourceSnapshot].self + ) { group in + for (index, source) in sources.enumerated() { + group.addTask { [weak self] in + guard let self else { + return ( + index, + .failed( + kind: source.kind, + message: "Automation inventory service became unavailable.", + refreshedAt: refreshedAt + ) + ) + } + return ( + index, + await self.refreshSource( + at: index, + source, + timeout: timeout, + refreshedAt: refreshedAt + ) + ) + } + } + + var indexed: [(Int, AutomationSourceSnapshot)] = [] + for await value in group { + indexed.append(value) + } + return indexed.sorted { $0.0 < $1.0 }.map(\.1) + } + } + + private enum SourceRefreshOutcome: Sendable { + case snapshot(AutomationSourceSnapshot) + case timedOut + case cancelled + } + + private final class SourceRefreshResolution: @unchecked Sendable { + private let lock = NSLock() + private var continuation: CheckedContinuation? + + init(_ continuation: CheckedContinuation) { + self.continuation = continuation + } + + func resolve(_ outcome: SourceRefreshOutcome) { + let continuation = lock.withLock { + defer { self.continuation = nil } + return self.continuation + } + continuation?.resume(returning: outcome) + } + } + + private func refreshSource( + at index: Int, + _ source: any AutomationSource, + timeout: Duration, + refreshedAt: Date + ) async -> AutomationSourceSnapshot { + if inFlightSourceRefreshes[index] != nil { + return .failed( + kind: source.kind, + message: "A previous timed-out automation source refresh is still completing; a new call was not started.", + refreshedAt: refreshedAt + ) + } + + nextSourceRefreshID &+= 1 + let refreshID = nextSourceRefreshID + let sourceTask = Task { await source.snapshot() } + inFlightSourceRefreshes[index] = InFlightSourceRefresh(id: refreshID, task: sourceTask) + Task { [weak self] in + _ = await sourceTask.value + await self?.sourceRefreshCompleted(at: index, id: refreshID) + } + + let first = await Self.firstOutcome(of: sourceTask, timeout: timeout) + switch first { + case let .snapshot(snapshot): + sourceRefreshCompleted(at: index, id: refreshID) + return snapshot + case .timedOut: + return .failed( + kind: source.kind, + message: "Automation source refresh exceeded its configured time limit.", + refreshedAt: refreshedAt + ) + case .cancelled: + return .failed( + kind: source.kind, + message: "Automation source refresh was cancelled.", + refreshedAt: refreshedAt + ) + } + } + + private func sourceRefreshCompleted(at index: Int, id: UInt64) { + guard inFlightSourceRefreshes[index]?.id == id else { return } + inFlightSourceRefreshes[index] = nil + } + + private static func firstOutcome( + of sourceTask: Task, + timeout: Duration + ) async -> SourceRefreshOutcome { + await withCheckedContinuation { + (continuation: CheckedContinuation) in + let resolution = SourceRefreshResolution(continuation) + Task { + resolution.resolve(.snapshot(await sourceTask.value)) + } + Task { + do { + try await Task.sleep(for: timeout) + resolution.resolve(.timedOut) + } catch is CancellationError { + resolution.resolve(.cancelled) + } catch { + resolution.resolve(.cancelled) + } + sourceTask.cancel() + } + } + } + + private static func aggregatedHealth( + _ snapshots: [AutomationSourceSnapshot] + ) -> [AutomationSourceKind: AutomationSourceHealth] { + snapshots.reduce(into: [:]) { result, snapshot in + let kind = snapshot.health.kind + guard let existing = result[kind] else { + result[kind] = snapshot.health + return + } + result[kind] = worseHealth(existing, snapshot.health) + } + } + + private static func worseHealth( + _ lhs: AutomationSourceHealth, + _ rhs: AutomationSourceHealth + ) -> AutomationSourceHealth { + let lhsRank = healthRank(lhs.state) + let rhsRank = healthRank(rhs.state) + if lhsRank != rhsRank { return lhsRank > rhsRank ? lhs : rhs } + if lhs.refreshedAt != rhs.refreshedAt { return lhs.refreshedAt > rhs.refreshedAt ? lhs : rhs } + return (lhs.message ?? "") <= (rhs.message ?? "") ? lhs : rhs + } + + private static func healthRank(_ state: AutomationSourceHealthState) -> Int { + switch state { + case .healthy: 0 + case .partial: 1 + case .permissionRequired: 2 + case .failed: 3 + } + } + + private static func deduplicated(_ records: [AutomationRecord]) -> [AutomationRecord] { + var groups: [[AutomationRecord]] = [] + + for record in records.sorted(by: recordPrecedes) { + let matchingIndexes = groups.indices.filter { groupIndex in + groups[groupIndex].contains { exactIdentityMatches($0, record) } + } + var mergedGroup = [record] + var mergedIndexes: [Int] = [] + for index in matchingIndexes where !strongIdentityConflicts(groups[index], mergedGroup) { + mergedGroup.append(contentsOf: groups[index]) + mergedIndexes.append(index) + } + guard !mergedIndexes.isEmpty else { + groups.append([record]) + continue + } + + for index in mergedIndexes.reversed() { + groups.remove(at: index) + } + groups.append(mergedGroup) + } + + return groups + .map(mergedRecord) + .sorted(by: recordPrecedes) + } + + private static func exactIdentityMatches( + _ lhs: AutomationRecord, + _ rhs: AutomationRecord + ) -> Bool { + if let lhsBundle = nonempty(lhs.providerBundleIdentifier), + let rhsBundle = nonempty(rhs.providerBundleIdentifier), + lhsBundle == rhsBundle + { + return true + } + + if lhs.label == rhs.label, + !lhs.label.isEmpty, + let lhsExecutable = canonicalPath(lhs.executable), + let rhsExecutable = canonicalPath(rhs.executable), + lhsExecutable == rhsExecutable + { + return true + } + + if let lhsSource = lhs.sourceURL?.standardizedFileURL, + let rhsSource = rhs.sourceURL?.standardizedFileURL, + lhsSource == rhsSource + { + return true + } + return false + } + + private static func strongIdentityConflicts( + _ lhs: [AutomationRecord], + _ rhs: [AutomationRecord] + ) -> Bool { + valuesConflict( + Set(lhs.compactMap { nonempty($0.providerBundleIdentifier) }), + Set(rhs.compactMap { nonempty($0.providerBundleIdentifier) }) + ) || valuesConflict( + Set(lhs.compactMap(labelExecutableIdentity)), + Set(rhs.compactMap(labelExecutableIdentity)) + ) || valuesConflict( + Set(lhs.compactMap(canonicalSourceReference)), + Set(rhs.compactMap(canonicalSourceReference)) + ) + } + + private static func valuesConflict( + _ lhs: Set, + _ rhs: Set + ) -> Bool { + !lhs.isEmpty && !rhs.isEmpty && lhs != rhs + } + + private static func labelExecutableIdentity(_ record: AutomationRecord) -> String? { + guard !record.label.isEmpty, let executable = canonicalPath(record.executable) else { + return nil + } + return "\(record.label)\u{0}\(executable)" + } + + private static func canonicalSourceReference(_ record: AutomationRecord) -> URL? { + record.sourceURL?.standardizedFileURL + } + + private static func mergedRecord(_ records: [AutomationRecord]) -> AutomationRecord { + let primary = records.sorted(by: recordPrecedes).first! + let evidence = Array(Set(records.flatMap(\.evidence))).sorted { + if $0.strength != $1.strength { return $0.strength > $1.strength } + if $0.source != $1.source { return $0.source < $1.source } + return $0.detail < $1.detail + } + + return AutomationRecord( + id: primary.id, + kind: primary.kind, + sourceKind: primary.sourceKind, + label: primary.label, + displayName: primary.displayName, + providerBundleIdentifier: primary.providerBundleIdentifier, + ownerUID: primary.ownerUID, + ownership: primary.ownership, + executable: primary.executable, + arguments: primary.arguments, + commandSignature: primary.commandSignature, + environment: primary.environment, + workingDirectory: primary.workingDirectory, + schedule: primary.schedule, + sourceURL: primary.sourceURL, + sourceChecksum: primary.sourceChecksum, + enabledState: primary.enabledState, + loadState: primary.loadState, + approvalState: primary.approvalState, + state: primary.state, + evidence: evidence, + capabilities: primary.capabilities, + validationFindings: Array(Set(records.flatMap(\.validationFindings))).sorted() + ) + } + + private static func recordPrecedes(_ lhs: AutomationRecord, _ rhs: AutomationRecord) -> Bool { + let lhsRank = recordRank(lhs) + let rhsRank = recordRank(rhs) + if lhsRank != rhsRank { return lhsRank.lexicographicallyPrecedes(rhsRank) } + let lhsCapabilities = lhs.capabilities.map(\.rawValue).sorted(by: utf8Precedes) + let rhsCapabilities = rhs.capabilities.map(\.rawValue).sorted(by: utf8Precedes) + if lhsCapabilities != rhsCapabilities { + return utf8SequencePrecedes(lhsCapabilities, rhsCapabilities) + } + return recordOrderingKey(lhs).lexicographicallyPrecedes(recordOrderingKey(rhs)) + } + + private static func utf8SequencePrecedes(_ lhs: [String], _ rhs: [String]) -> Bool { + for (lhsValue, rhsValue) in zip(lhs, rhs) where lhsValue != rhsValue { + return utf8Precedes(lhsValue, rhsValue) + } + return lhs.count < rhs.count + } + + private static func utf8Precedes(_ lhs: String, _ rhs: String) -> Bool { + lhs.utf8.lexicographicallyPrecedes(rhs.utf8) + } + + // This total key exists only for in-memory comparisons. It must never be persisted, + // logged, or copied into diagnostics because it can contain commands and local paths. + private static func recordOrderingKey(_ record: AutomationRecord) -> [UInt8] { + var key = CanonicalOrderingKey() + + // Capability choice affects the surviving primary record, so compare the exact set + // before any descriptive or identity fields once the policy rank is tied. + key.append(record.capabilities.map(\.rawValue).sorted()) + key.append(record.sourceURL?.standardizedFileURL.path) + key.append(record.sourceKind.rawValue) + key.append(record.kind.rawValue) + key.append(record.ownership.rawValue) + key.append(record.state.rawValue) + key.append(record.enabledState.rawValue) + key.append(record.loadState.rawValue) + key.append(record.approvalState.rawValue) + key.append(canonicalPath(record.executable)) + key.append(record.arguments) + key.append(record.commandSignature) + key.append(record.providerBundleIdentifier) + key.append(record.ownerUID.map(UInt64.init)) + appendSchedule(record.schedule, to: &key) + appendCanonicalEvidence(record.evidence, to: &key) + key.append(record.validationFindings.sorted(by: utf8Precedes)) + + // Remaining exact value fields make the comparison total even when canonical forms + // above intentionally normalize equivalent paths or collection ordering. + key.append(record.label) + key.append(record.id.rawValue) + key.append(record.displayName) + key.append(record.executable) + appendEnvironment(record.environment, to: &key) + key.append(canonicalPath(record.workingDirectory)) + key.append(record.workingDirectory) + key.append(record.sourceURL?.absoluteString) + key.append(record.sourceURL?.relativeString) + key.append(record.sourceURL?.baseURL?.absoluteString) + key.append(record.sourceChecksum) + appendEvidenceInOriginalOrder(record.evidence, to: &key) + key.append(record.validationFindings) + return key.bytes + } + + private static func appendEnvironment( + _ environment: [String: String], + to key: inout CanonicalOrderingKey + ) { + let entries = environment.sorted { + if $0.key != $1.key { return $0.key.utf8.lexicographicallyPrecedes($1.key.utf8) } + return $0.value.utf8.lexicographicallyPrecedes($1.value.utf8) + } + key.appendCount(entries.count) + for entry in entries { + key.append(entry.key) + key.append(entry.value) + } + } + + private static func appendSchedule( + _ schedule: AutomationSchedule, + to key: inout CanonicalOrderingKey + ) { + key.appendCount(schedule.triggers.count) + for trigger in schedule.triggers { + switch trigger { + case .atLogin: + key.append("atLogin") + case .runAtLoad: + key.append("runAtLoad") + case let .interval(seconds): + key.append("interval") + key.append(Int64(seconds)) + case let .calendar(value): + key.append("calendar") + key.append(value) + case .keepAlive: + key.append("keepAlive") + case let .cron(value): + key.append("cron") + key.append(value) + case .demand: + key.append("demand") + } + } + key.append(schedule.summary) + } + + private static func appendCanonicalEvidence( + _ evidence: [AutomationEvidence], + to key: inout CanonicalOrderingKey + ) { + let sorted = evidence.sorted { + if $0.strength != $1.strength { return $0.strength.rawValue < $1.strength.rawValue } + if $0.source != $1.source { + return $0.source.utf8.lexicographicallyPrecedes($1.source.utf8) + } + return $0.detail.utf8.lexicographicallyPrecedes($1.detail.utf8) + } + appendEvidenceInOriginalOrder(sorted, to: &key) + } + + private static func appendEvidenceInOriginalOrder( + _ evidence: [AutomationEvidence], + to key: inout CanonicalOrderingKey + ) { + key.appendCount(evidence.count) + for item in evidence { + key.append(Int64(item.strength.rawValue)) + key.append(item.source) + key.append(item.detail) + } + } + + private static func recordRank(_ record: AutomationRecord) -> [Int] { + [ + record.ownership == .user ? 0 : 1, + -record.capabilities.count, + sourceRank(record.sourceKind), + ] + } + + private static func sourceRank(_ kind: AutomationSourceKind) -> Int { + switch kind { + case .launchAgent: 0 + case .legacyLoginItem: 1 + case .crontab: 2 + case .launchDaemon: 3 + case .serviceManagement: 4 + } + } + + private static func nonempty(_ value: String?) -> String? { + guard let value, !value.isEmpty else { return nil } + return value + } + + private static func canonicalPath(_ value: String?) -> String? { + guard let value = nonempty(value) else { return nil } + return URL(fileURLWithPath: value).standardizedFileURL.path + } +} + +private struct CanonicalOrderingKey { + private(set) var bytes: [UInt8] = [] + + mutating func append(_ value: String) { + let utf8 = Array(value.utf8) + append(UInt64(utf8.count)) + bytes.append(contentsOf: utf8) + } + + mutating func append(_ value: String?) { + guard let value else { + bytes.append(0) + return + } + bytes.append(1) + append(value) + } + + mutating func append(_ values: [String]) { + appendCount(values.count) + for value in values { + append(value) + } + } + + mutating func append(_ value: UInt64?) { + guard let value else { + bytes.append(0) + return + } + bytes.append(1) + append(value) + } + + mutating func append(_ value: Int64) { + append(UInt64(bitPattern: value) ^ (1 << 63)) + } + + mutating func appendCount(_ count: Int) { + append(UInt64(count)) + } + + private mutating func append(_ value: UInt64) { + for shift in stride(from: 56, through: 0, by: -8) { + bytes.append(UInt8(truncatingIfNeeded: value >> shift)) + } + } +} diff --git a/Sources/DevScopeCore/AutomationManager.swift b/Sources/DevScopeCore/AutomationManager.swift new file mode 100644 index 0000000..69dfbc9 --- /dev/null +++ b/Sources/DevScopeCore/AutomationManager.swift @@ -0,0 +1,4006 @@ +import CryptoKit +import Darwin +import Foundation + +public enum AutomationManagerConfigurationError: Error, Equatable, Sendable { + case destinationAuthorizationUnavailable + case recoverableSourceUnavailable + case exactStateRestorationUnavailable + case runtimeProcessVerificationUnavailable +} + +public struct AutomationRecoverableSource: Equatable, Sendable { + public let transactionURL: URL + public let data: Data + public let checksum: String + public let metadata: AutomationFileMetadata + + public init( + transactionURL: URL, + data: Data, + checksum: String, + metadata: AutomationFileMetadata + ) { + self.transactionURL = transactionURL + self.data = data + self.checksum = checksum + self.metadata = metadata + } +} + +public enum AutomationOperation: Equatable, Sendable { + case startNow + case confirmedRunToCompletion(AutomationRunToCompletionConfirmation) + case stopCurrentRun + case enable + case disable + case disableAndStop + case edit(AutomationEditPayload) + case duplicate(AutomationEditPayload) + case importRecord(AutomationImportPayload) + case exportRecord(redacted: Bool) + case remove + case restore(AutomationBackup.ID) +} + +public struct AutomationRunToCompletionConfirmation: Equatable, Sendable { + public let recordID: AutomationRecord.ID + public let sourceChecksum: String? + public let exactCommand: String + + public init( + recordID: AutomationRecord.ID, + sourceChecksum: String?, + exactCommand: String + ) { + self.recordID = recordID + self.sourceChecksum = sourceChecksum + self.exactCommand = exactCommand + } +} + +public struct AutomationEditPayload: Equatable, Sendable { + public let label: String + public let executable: String + public let arguments: [String] + public let environment: [String: String] + public let workingDirectory: String? + public let schedule: AutomationSchedule + public let rawRepresentation: Data? + public let destination: URL? + public let expectedDestinationChecksum: String? + + public init( + label: String, + executable: String, + arguments: [String], + environment: [String: String], + workingDirectory: String?, + schedule: AutomationSchedule, + rawRepresentation: Data?, + destination: URL? = nil, + expectedDestinationChecksum: String? = nil + ) { + self.label = label + self.executable = executable + self.arguments = arguments + self.environment = environment + self.workingDirectory = workingDirectory + self.schedule = schedule + self.rawRepresentation = rawRepresentation + self.destination = destination + self.expectedDestinationChecksum = expectedDestinationChecksum + } +} + +public struct AutomationImportPayload: Equatable, Sendable { + public let destination: URL + public let data: Data + public let expectedKind: AutomationKind + public let expectedDestinationChecksum: String? + + public init( + destination: URL, + data: Data, + expectedKind: AutomationKind, + expectedDestinationChecksum: String? = nil + ) { + self.destination = destination + self.data = data + self.expectedKind = expectedKind + self.expectedDestinationChecksum = expectedDestinationChecksum + } +} + +public struct AutomationBackup: Identifiable, Equatable, Sendable { + public struct ID: RawRepresentable, Hashable, Sendable { + public let rawValue: UUID + + public init(rawValue: UUID) { + self.rawValue = rawValue + } + } + + public let id: ID + public let recordID: AutomationRecord.ID + public let sourceURL: URL + public let backupURL: URL + public let checksum: String + public let createdAt: Date + public let sourceExisted: Bool + public let ownerUID: uid_t + public let kind: AutomationKind + public let sourceKind: AutomationSourceKind + public let authorizedRecordIDs: Set + public let parentResourceIdentifier: String? + + public init( + id: ID, + recordID: AutomationRecord.ID, + sourceURL: URL, + backupURL: URL, + checksum: String, + createdAt: Date, + sourceExisted: Bool = true, + ownerUID: uid_t = 0, + kind: AutomationKind = .launchAgent, + sourceKind: AutomationSourceKind = .launchAgent, + authorizedRecordIDs: Set? = nil, + parentResourceIdentifier: String? = nil + ) { + self.id = id + self.recordID = recordID + self.sourceURL = sourceURL + self.backupURL = backupURL + self.checksum = checksum + self.createdAt = createdAt + self.sourceExisted = sourceExisted + self.ownerUID = ownerUID + self.kind = kind + self.sourceKind = sourceKind + self.authorizedRecordIDs = authorizedRecordIDs ?? [recordID] + self.parentResourceIdentifier = parentResourceIdentifier + } +} + +public enum AutomationRollbackOutcome: Equatable, Sendable { + case notNeeded + case restored(AutomationBackup.ID) + case failed(String) +} + +public enum AutomationOperationStatus: Equatable, Sendable { + case succeeded + case rejected(String) + case failed(String) + case partialFailure(String) +} + +public struct AutomationExportArtifact: Equatable, Sendable { + public let suggestedFilename: String + public let mediaType: String + public let format: String + public let data: Data + public let isRedacted: Bool + + public init( + suggestedFilename: String, + mediaType: String, + format: String, + data: Data, + isRedacted: Bool + ) { + self.suggestedFilename = suggestedFilename + self.mediaType = mediaType + self.format = format + self.data = data + self.isRedacted = isRedacted + } +} + +public enum AutomationRecordExporter { + public static func artifact( + for record: AutomationRecord, + verifiedSourceData: Data?, + requestedRedaction: Bool + ) throws -> AutomationExportArtifact { + let protected = record.ownership != .user + || record.kind == .launchDaemon + || record.kind == .backgroundItem + || record.sourceKind == .launchDaemon + || record.sourceKind == .serviceManagement + let redacted = requestedRedaction || protected || verifiedSourceData == nil + if !redacted, let verifiedSourceData { + let presentation: (extension: String, mediaType: String, format: String) + switch record.kind { + case .launchAgent, .launchDaemon: + presentation = ("plist", "application/x-plist", "source.plist") + case .cron: + presentation = ("crontab", "text/plain", "source.crontab") + case .loginItem: + presentation = ("json", "application/json", "source.legacy-login-item") + case .backgroundItem: + presentation = ("json", "application/json", "inspection-manifest.v1") + } + return AutomationExportArtifact( + suggestedFilename: "devscope-\(record.sourceKind.rawValue)-\(record.id.rawValue.prefix(12)).\(presentation.extension)", + mediaType: presentation.mediaType, + format: presentation.format, + data: verifiedSourceData, + isRedacted: false + ) + } + + var payload: [String: Any] = [ + "schemaVersion": 1, + "recordID": record.id.rawValue, + "kind": record.kind.rawValue, + "sourceKind": record.sourceKind.rawValue, + "ownership": record.ownership.rawValue, + "enabledState": record.enabledState.rawValue, + "loadState": record.loadState.rawValue, + "approvalState": record.approvalState.rawValue, + "state": record.state.rawValue, + "redacted": redacted, + "importable": false, + "scheduleSummary": record.schedule.summary, + ] + payload["providerBundleIdentifier"] = record.providerBundleIdentifier + payload["sourceChecksum"] = record.sourceChecksum + payload["label"] = "" + payload["displayName"] = "" + payload["argumentCount"] = record.arguments.count + payload["environmentKeys"] = record.environment.keys.sorted() + payload["validationFindingCount"] = record.validationFindings.count + payload["evidenceCount"] = record.evidence.count + let data = try JSONSerialization.data( + withJSONObject: payload, + options: [.prettyPrinted, .sortedKeys] + ) + return AutomationExportArtifact( + suggestedFilename: "devscope-\(record.sourceKind.rawValue)-\(record.id.rawValue.prefix(12)).json", + mediaType: "application/json", + format: "inspection-manifest.v1", + data: data, + isRedacted: redacted + ) + } +} + +public struct AutomationOperationResult: Equatable, Sendable { + public let operation: AutomationOperation + public let status: AutomationOperationStatus + public let appliedSteps: [String] + public let verificationEvidence: [String] + public let rollback: AutomationRollbackOutcome + public let manualRecovery: String? + public let backup: AutomationBackup? + public let exportArtifact: AutomationExportArtifact? + public let fileMutationEvidence: AutomationFilePartialMutation? + public let rollbackFileMutationEvidence: AutomationFilePartialMutation? + + public init( + operation: AutomationOperation, + status: AutomationOperationStatus, + appliedSteps: [String], + verificationEvidence: [String], + rollback: AutomationRollbackOutcome, + manualRecovery: String?, + backup: AutomationBackup? = nil, + exportArtifact: AutomationExportArtifact? = nil, + fileMutationEvidence: AutomationFilePartialMutation? = nil, + rollbackFileMutationEvidence: AutomationFilePartialMutation? = nil + ) { + self.operation = operation + self.status = status + self.appliedSteps = appliedSteps + self.verificationEvidence = verificationEvidence + self.rollback = rollback + self.manualRecovery = manualRecovery + self.backup = backup + self.exportArtifact = exportArtifact + self.fileMutationEvidence = fileMutationEvidence + self.rollbackFileMutationEvidence = rollbackFileMutationEvidence + } +} + +public enum AutomationPostcondition: Equatable, Hashable, Sendable { + case targetResolved + case targetUnresolved + case currentRunStarted + case runCompleted + case noLinkedProcess + case futureLaunchesEnabled + case futureLaunchesDisabled + case sourceInstalled + case sourceRemoved + case sourceChecksum(String) + case preTransactionStateRestored(AutomationPreTransactionState) +} + +public struct AutomationExecutorResult: Equatable, Sendable { + public let postconditions: Set + public let evidence: [String] + public let appliedState: AutomationExecutorAppliedState? + + public init( + postconditions: Set, + evidence: [String], + appliedState: AutomationExecutorAppliedState? = nil + ) { + self.postconditions = postconditions + self.evidence = evidence + self.appliedState = appliedState + } +} + +public enum AutomationExecutorAppliedState: Equatable, Hashable, Sendable { + case cronLiveDocument(normalizedChecksum: String) +} + +public struct AutomationExecutorMutationFailure: Error, Equatable, Sendable { + public let error: AutomationExecutorError + public let appliedState: AutomationExecutorAppliedState + + public init(error: AutomationExecutorError, appliedState: AutomationExecutorAppliedState) { + self.error = error + self.appliedState = appliedState + } +} + +public struct AutomationVerifiedRecoveryInput: Equatable, Sendable { + public let backupID: AutomationBackup.ID + public let backupURL: URL + public let data: Data + public let checksum: String + + public init( + backupID: AutomationBackup.ID, + backupURL: URL, + data: Data, + checksum: String + ) { + self.backupID = backupID + self.backupURL = backupURL + self.data = data + self.checksum = checksum + } +} + +public struct AutomationLinkedProcessIdentity: Equatable, Hashable, Sendable { + public let processID: Int32 + public let birthToken: ProcessBirthToken + + public init(processID: Int32, birthToken: ProcessBirthToken) { + self.processID = processID + self.birthToken = birthToken + } +} + +public struct AutomationPreTransactionState: Equatable, Hashable, Sendable { + public let sourceExisted: Bool + public let enabledState: AutomationEnabledState + public let loadState: AutomationLoadState + public let linkedProcesses: Set + + public init( + sourceExisted: Bool = true, + enabledState: AutomationEnabledState, + loadState: AutomationLoadState, + linkedProcesses: Set + ) { + self.sourceExisted = sourceExisted + self.enabledState = enabledState + self.loadState = loadState + self.linkedProcesses = linkedProcesses + } +} + +public protocol AutomationMutationApplying: Sendable { + func apply( + _ operation: AutomationOperation, + to record: AutomationRecord, + linkedProcesses: [ClassifiedDevProcess], + proposedSourceURL: URL? + ) async throws -> AutomationExecutorResult + + func restorePreTransactionState( + _ state: AutomationPreTransactionState, + for record: AutomationRecord, + recovery: AutomationVerifiedRecoveryInput, + linkedProcesses: [ClassifiedDevProcess], + expectedAppliedState: AutomationExecutorAppliedState? + ) async throws -> AutomationExecutorResult +} + +public extension AutomationMutationApplying { + func restorePreTransactionState( + _ state: AutomationPreTransactionState, + for record: AutomationRecord, + recovery: AutomationVerifiedRecoveryInput, + linkedProcesses: [ClassifiedDevProcess], + expectedAppliedState: AutomationExecutorAppliedState? + ) async throws -> AutomationExecutorResult { + throw AutomationManagerConfigurationError.exactStateRestorationUnavailable + } +} + +public actor AutomationManager { + private let fileSystem: any AutomationFileSystem + private let executor: any AutomationMutationApplying + private let capabilityContext: @Sendable (AutomationRecord) throws -> AutomationCapabilityContext + private let destinationContext: @Sendable ( + AutomationRecord, + URL + ) throws -> AutomationCapabilityContext + private let recoverableSource: @Sendable ( + AutomationRecord + ) async throws -> AutomationRecoverableSource + private let refresh: @Sendable () async -> AutomationInventorySnapshot + private let refreshProcesses: @Sendable () async throws -> [DevProcess] + private let backupDirectory: URL + private let currentUID: uid_t + private let now: @Sendable () -> Date + private let makeUUID: @Sendable () -> UUID + private let evidenceRedactor: @Sendable (String) -> String + private var backupsByID: [AutomationBackup.ID: AutomationBackup] = [:] + private var manifestURLsByID: [AutomationBackup.ID: URL] = [:] + private var backupAuthorizationsByID: [AutomationBackup.ID: AutomationFileAuthorization] = [:] + private var manifestAuthorizationsByID: [AutomationBackup.ID: AutomationFileAuthorization] = [:] + private var manifestDataByID: [AutomationBackup.ID: Data] = [:] + private var recoveryRootResourceIdentifier: String? + private var transactionIsActive = false + private var transactionWaiters: [CheckedContinuation] = [] + + public init( + fileSystem: any AutomationFileSystem, + executor: any AutomationMutationApplying, + capabilityContext: @escaping @Sendable (AutomationRecord) throws -> AutomationCapabilityContext, + destinationContext: @escaping @Sendable ( + AutomationRecord, + URL + ) throws -> AutomationCapabilityContext = { _, _ in + throw AutomationManagerConfigurationError.destinationAuthorizationUnavailable + }, + recoverableSource: @escaping @Sendable ( + AutomationRecord + ) async throws -> AutomationRecoverableSource = { _ in + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + }, + refresh: @escaping @Sendable () async -> AutomationInventorySnapshot, + refreshProcesses: @escaping @Sendable () async throws -> [DevProcess] = { + throw AutomationManagerConfigurationError.runtimeProcessVerificationUnavailable + }, + backupDirectory: URL, + currentUID: uid_t = getuid(), + now: @escaping @Sendable () -> Date = Date.init, + makeUUID: @escaping @Sendable () -> UUID = UUID.init, + evidenceRedactor: @escaping @Sendable (String) -> String = { _ in + "Executor evidence redacted." + } + ) { + self.fileSystem = fileSystem + self.executor = executor + self.capabilityContext = capabilityContext + self.destinationContext = destinationContext + self.recoverableSource = recoverableSource + self.refresh = refresh + self.refreshProcesses = refreshProcesses + self.backupDirectory = backupDirectory + self.currentUID = currentUID + self.now = now + self.makeUUID = makeUUID + self.evidenceRedactor = evidenceRedactor + recoveryRootResourceIdentifier = try? fileSystem.metadata(for: backupDirectory) + .resourceIdentifier + let loaded = Self.loadManifests( + fileSystem: fileSystem, + backupDirectory: backupDirectory, + currentUID: currentUID + ) + backupsByID = loaded.backups + manifestURLsByID = loaded.manifestURLs + backupAuthorizationsByID = loaded.backupAuthorizations + manifestAuthorizationsByID = loaded.manifestAuthorizations + manifestDataByID = loaded.manifestData + } + + public func perform( + _ operation: AutomationOperation, + record: AutomationRecord, + expectedChecksum: String?, + linkedProcesses: [ClassifiedDevProcess] + ) async -> AutomationOperationResult { + await acquireTransaction() + defer { releaseTransaction() } + + if case let .exportRecord(requestedRedaction) = operation, + record.ownership != .user + || record.kind == .launchDaemon + || record.kind == .backgroundItem + || record.sourceKind == .launchDaemon + || record.sourceKind == .serviceManagement + { + guard expectedChecksum == record.sourceChecksum else { + return rejected(operation, "The automation record changed since it was inspected.") + } + let context: AutomationCapabilityContext + do { + context = try capabilityContext(record) + } catch { + return rejected(operation, "DevScope could not verify the automation record identity.") + } + let decision = AutomationCapabilityPolicy.decision(for: record, context: context) + guard decision.capabilities.contains(.exportRecord) else { + return rejected(operation, decision.reason ?? "Export is not available for this source.") + } + do { + let artifact = try AutomationRecordExporter.artifact( + for: record, + verifiedSourceData: nil, + requestedRedaction: requestedRedaction + ) + return AutomationOperationResult( + operation: operation.redactedForResult, + status: .succeeded, + appliedSteps: ["Prepared an automation export without mutating its source."], + verificationEvidence: ["The export artifact is bound to the inspected record generation."], + rollback: .notNeeded, + manualRecovery: nil, + exportArtifact: artifact + ) + } catch { + return failed(operation, "DevScope could not prepare the automation export.") + } + } + + let restorationBackup: AutomationBackup? + if case .restore(let id) = operation { + guard let backup = backupsByID[id], + backup.authorizedRecordIDs.contains(record.id), + backup.ownerUID == record.ownerUID, + backup.kind == record.kind, + backup.sourceKind == record.sourceKind + else { + return rejected(operation, "The selected recovery backup does not belong to this automation.") + } + restorationBackup = backup + } else { + restorationBackup = nil + } + + let sourceURL: URL + let sourceData: Data + let metadata: AutomationFileMetadata + let sourceExistedAtPreflight: Bool + do { + if let restorationBackup { + sourceURL = restorationBackup.sourceURL.standardizedFileURL + sourceExistedAtPreflight = fileSystem.itemExists(at: sourceURL) + if sourceExistedAtPreflight { + sourceData = try fileSystem.read(sourceURL) + metadata = try fileSystem.metadata(for: sourceURL) + } else { + sourceData = Data() + metadata = try fileSystem.metadata(for: sourceURL.deletingLastPathComponent()) + } + } else if !record.sourceKind.requiresRecoverableSource, let directURL = record.sourceURL { + sourceURL = directURL.standardizedFileURL + sourceData = try fileSystem.read(sourceURL) + metadata = try fileSystem.metadata(for: sourceURL) + sourceExistedAtPreflight = true + } else { + let captured = try await recoverableSource(record) + sourceURL = captured.transactionURL.standardizedFileURL + sourceData = captured.data + metadata = captured.metadata + guard captured.checksum == Self.sourceChecksum(captured.data, kind: record.kind), + try fileSystem.read(sourceURL) == captured.data, + try fileSystem.metadata(for: sourceURL) == captured.metadata + else { + return rejected(operation, "The recoverable source identity could not be established.") + } + sourceExistedAtPreflight = true + } + } catch let partial as AutomationFilePartialMutation { + _ = await refresh() + return AutomationOperationResult( + operation: operation.redactedForResult, + status: .partialFailure( + "HIGH SEVERITY: recoverable source materialization reached a partial filesystem state." + ), + appliedSteps: ["Recoverable source materialization reached a partial filesystem state."], + verificationEvidence: ["Refreshed automation truth before returning."], + rollback: .notNeeded, + manualRecovery: "Preserve and inspect the authorized recovery handle before retrying.", + fileMutationEvidence: partial + ) + } catch { + let reason: String + switch record.sourceKind { + case .crontab: + reason = "Current-user crontab management is unavailable until recoverable source access is installed." + case .legacyLoginItem: + reason = "Legacy login-item management is unavailable until recoverable source access is installed." + default: + reason = "DevScope could not re-read the automation source." + } + return rejected(operation, reason) + } + + let observedChecksum = Self.sourceChecksum(sourceData, kind: record.kind) + if restorationBackup != nil { + guard sourceExistedAtPreflight ? expectedChecksum == observedChecksum : expectedChecksum == nil else { + return rejected(operation, "The restore destination changed since it was inspected.") + } + } else if expectedChecksum != observedChecksum { + return rejected(operation, "The automation source changed since it was inspected.") + } + if case .confirmedRunToCompletion(let confirmation) = operation { + guard confirmation.recordID == record.id, + let confirmationChecksum = confirmation.sourceChecksum, + confirmationChecksum == record.sourceChecksum, + confirmationChecksum == observedChecksum + else { + return rejected(operation, "The confirmed cron command belongs to a stale source generation.") + } + } + + let context: AutomationCapabilityContext + do { + context = try restorationBackup == nil + ? capabilityContext(record) + : destinationContext(record, sourceURL) + } catch { + return rejected(operation, "DevScope could not verify the automation source identity.") + } + let authorizedMetadataURL = sourceExistedAtPreflight + ? sourceURL + : sourceURL.deletingLastPathComponent().standardizedFileURL + guard metadata.resourceIdentifier?.isEmpty == false, + metadata.ownerUID == context.sourceOwnerUID, + metadata.isSymbolicLink == context.isSymlink, + metadata.canonicalURL.standardizedFileURL.path == authorizedMetadataURL.path + else { + return rejected(operation, "The automation source identity changed during verification.") + } + let decision = AutomationCapabilityPolicy.decision(for: record, context: context) + guard decision.capabilities.contains(operation.requiredCapability) else { + return rejected(operation, decision.reason ?? "This operation is not available for the source.") + } + + if case let .exportRecord(requestedRedaction) = operation { + do { + let artifact = try AutomationRecordExporter.artifact( + for: record, + verifiedSourceData: sourceData, + requestedRedaction: requestedRedaction + ) + return AutomationOperationResult( + operation: operation.redactedForResult, + status: .succeeded, + appliedSteps: ["Prepared an automation export without mutating its source."], + verificationEvidence: ["The export uses the checksum-verified source generation."], + rollback: .notNeeded, + manualRecovery: nil, + exportArtifact: artifact + ) + } catch { + return failed(operation, "DevScope could not prepare the automation export.") + } + } + + let proposal: SourceProposal? + do { + proposal = try makeProposal( + for: operation, + record: record, + sourceURL: sourceURL, + selectedSourceData: sourceData + ) + } catch let error as ProposalError { + return rejected(operation, error.message) + } catch { + return rejected(operation, "The proposed automation source could not be prepared.") + } + + if let proposal, proposal.restoresSourcePresence, + !Self.isValidSource( + proposal.data, + kind: record.kind, + expectedLabel: proposal.intendedLabel, + sourceURL: proposal.destination, + ownerUID: context.currentUID + ) + { + return rejected(operation, "The proposed automation source is not valid.") + } + + let destinationSnapshot: DestinationSnapshot? + do { + destinationSnapshot = try proposal.map { + try verifyDestination( + $0, + record: record, + selectedSourceURL: sourceURL, + selectedSourceData: sourceData, + selectedMetadata: metadata + ) + } + } catch let error as ProposalError { + return rejected(operation, error.message) + } catch { + return rejected(operation, "DevScope could not authorize the intended destination.") + } + + let linkedIdentities = linkedProcesses.compactMap { classified -> AutomationLinkedProcessIdentity? in + guard let birthToken = classified.process.birthToken else { return nil } + return AutomationLinkedProcessIdentity( + processID: classified.process.pid, + birthToken: birthToken + ) + } + if operation.controlsLinkedProcesses, linkedIdentities.count != linkedProcesses.count { + return rejected(operation, "Every controlled process requires an exact birth identity.") + } + let preTransactionState = AutomationPreTransactionState( + sourceExisted: destinationSnapshot?.existed ?? sourceExistedAtPreflight, + enabledState: record.enabledState, + loadState: record.loadState, + linkedProcesses: Set(linkedIdentities) + ) + + let backup: AutomationBackup + do { + let recoveryData = destinationSnapshot?.data ?? (proposal == nil ? sourceData : Data()) + let recoveryURL = proposal?.destination ?? sourceURL + backup = try createBackup( + sourceData: recoveryData, + checksum: Self.checksum(recoveryData), + sourceURL: recoveryURL, + sourceExisted: destinationSnapshot?.existed ?? true, + record: record + ) + } catch let partial as AutomationFilePartialMutation { + _ = await refresh() + return AutomationOperationResult( + operation: operation.redactedForResult, + status: .partialFailure( + "HIGH SEVERITY: recovery evidence installation was only partially completed." + ), + appliedSteps: ["Recovery evidence installation reached a partial filesystem state."], + verificationEvidence: ["Refreshed automation truth before returning."], + rollback: .notNeeded, + manualRecovery: "Preserve and inspect the authorized recovery handle before retrying.", + fileMutationEvidence: partial + ) + } catch { + return failed( + operation, + "DevScope could not create owner-only recovery evidence.", + manualRecovery: "No changes were applied. Verify that the recovery folder is writable." + ) + } + + if restorationBackup == nil { + do { + if let revalidationFailure = try await revalidationFailure( + operation: operation, + record: record, + sourceURL: sourceURL, + expectedChecksum: observedChecksum, + initialMetadata: metadata + ) { + return failed( + operation, + revalidationFailure, + appliedSteps: ["Created owner-only recovery backup."], + manualRecovery: "No mutation was applied; inspect the source again before retrying.", + backup: backup + ) + } + } catch let partial as AutomationFilePartialMutation { + _ = await refresh() + return AutomationOperationResult( + operation: operation.redactedForResult, + status: .partialFailure( + "HIGH SEVERITY: recoverable source materialization reached a partial filesystem state." + ), + appliedSteps: [ + "Created owner-only recovery backup.", + "Recoverable source materialization reached a partial filesystem state.", + ], + verificationEvidence: ["Refreshed automation truth before returning."], + rollback: .notNeeded, + manualRecovery: "Preserve and inspect the authorized recovery handle before retrying.", + backup: backup, + fileMutationEvidence: partial + ) + } catch { + return failed( + operation, + "DevScope could not revalidate the automation source immediately before mutation.", + appliedSteps: ["Created owner-only recovery backup."], + manualRecovery: "No mutation was applied; inspect the source again before retrying.", + backup: backup + ) + } + } + if let proposal, let destinationSnapshot, + let failure = destinationRevalidationFailure( + proposal, + snapshot: destinationSnapshot, + record: record + ) + { + return failed( + operation, + failure, + appliedSteps: ["Created owner-only recovery backup."], + manualRecovery: "No mutation was applied; inspect the destination again before retrying.", + backup: backup + ) + } + + var proposedSourceURL: URL? + var sourceMutationStep: String? + var postMutationResourceIdentifier: String? + if let proposal, proposal.restoresSourcePresence { + do { + guard let destinationSnapshot else { + throw AutomationManagerConfigurationError.destinationAuthorizationUnavailable + } + let parentAuthorization = try Self.directoryAuthorization( + for: proposal.destination.deletingLastPathComponent(), + resourceIdentifier: destinationSnapshot.parentMetadata.resourceIdentifier + ) + let staged = try fileSystem.writeStagedFile( + nextTo: proposal.destination, + data: proposal.data, + permissions: 0o600, + authorization: parentAuthorization + ) + let stagedData = try fileSystem.read(staged.url) + guard Self.isValidSource( + stagedData, + kind: record.kind, + expectedLabel: proposal.intendedLabel, + sourceURL: proposal.destination, + ownerUID: context.currentUID + ), stagedData == proposal.data else { + try removeCleanupArtifact(staged.authorization) + return failed( + operation, + "The proposed automation source is not valid.", + appliedSteps: ["Created owner-only recovery backup."], + backup: backup + ) + } + if let failure = destinationRevalidationFailure( + proposal, + snapshot: destinationSnapshot, + record: record + ) { + try removeCleanupArtifact(staged.authorization) + return failed( + operation, + failure, + appliedSteps: ["Created owner-only recovery backup."], + backup: backup + ) + } + let destinationAuthorization = try Self.fileAuthorization( + for: proposal.destination, + directory: parentAuthorization, + resourceIdentifier: destinationSnapshot.metadata.resourceIdentifier, + existed: destinationSnapshot.existed + ) + let replacementReceipt = try committedReplacement( + at: destinationAuthorization, + with: staged + ) + guard replacementReceipt.bindingVerified else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + sourceMutationStep = "Installed validated source atomically." + let installedMetadata = try fileSystem.metadata(for: proposal.destination) + guard let installedIdentity = installedMetadata.resourceIdentifier, + !installedIdentity.isEmpty + else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + postMutationResourceIdentifier = installedIdentity + let installedAuthorization = try Self.fileAuthorization( + for: proposal.destination, + directory: parentAuthorization, + resourceIdentifier: installedIdentity, + existed: true + ) + guard try fileSystem.fileMatchesBinding( + installedAuthorization, + binding: staged.binding + ) else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + proposedSourceURL = proposal.destination + } catch let partial as AutomationFilePartialMutation { + postMutationResourceIdentifier = try? fileSystem.metadata(for: proposal.destination) + .resourceIdentifier + return await failureAfterMutation( + operation: operation, + record: record, + linkedProcesses: linkedProcesses, + backup: backup, + preTransactionState: preTransactionState, + proposal: proposal, + originalSourceMetadata: metadata, + postMutationResourceIdentifier: postMutationResourceIdentifier, + sourceMutationStep: "Filesystem reported a partial source replacement.", + phase: .sourceMutationFailed, + evidence: ["Filesystem mutation state was refreshed before executor use."], + expectedAppliedState: nil, + fileMutationEvidence: partial + ) + } catch { + if sourceMutationStep != nil { + postMutationResourceIdentifier = try? fileSystem.metadata(for: proposal.destination) + .resourceIdentifier + return await failureAfterMutation( + operation: operation, + record: record, + linkedProcesses: linkedProcesses, + backup: backup, + preTransactionState: preTransactionState, + proposal: proposal, + originalSourceMetadata: metadata, + postMutationResourceIdentifier: postMutationResourceIdentifier, + sourceMutationStep: sourceMutationStep, + phase: .sourceMutationFailed, + evidence: [], + expectedAppliedState: nil + ) + } + return failed( + operation, + "DevScope could not stage the proposed automation source.", + appliedSteps: ["Created owner-only recovery backup."], + backup: backup + ) + } + } else if let proposal { + do { + if fileSystem.itemExists(at: proposal.destination) { + guard try writeTargetIsAuthorized( + record: record, + url: proposal.destination, + expectedResourceIdentifier: destinationSnapshot?.metadata.resourceIdentifier, + expectedParentResourceIdentifier: destinationSnapshot?.parentMetadata.resourceIdentifier, + requiredCapability: .restore + ) else { + throw AutomationManagerConfigurationError.destinationAuthorizationUnavailable + } + let parentAuthorization = try Self.directoryAuthorization( + for: proposal.destination.deletingLastPathComponent(), + resourceIdentifier: destinationSnapshot?.parentMetadata.resourceIdentifier + ) + let destinationAuthorization = try Self.fileAuthorization( + for: proposal.destination, + directory: parentAuthorization, + resourceIdentifier: destinationSnapshot?.metadata.resourceIdentifier, + existed: true + ) + _ = try Self.committedReceipt(fileSystem.removeItem(destinationAuthorization)) + } + sourceMutationStep = "Restored the source's prior absence." + } catch let partial as AutomationFilePartialMutation { + return await failureAfterMutation( + operation: operation, + record: record, + linkedProcesses: linkedProcesses, + backup: backup, + preTransactionState: preTransactionState, + proposal: proposal, + originalSourceMetadata: metadata, + postMutationResourceIdentifier: nil, + sourceMutationStep: "Filesystem reported a partial removal while restoring absence.", + phase: .sourceMutationFailed, + evidence: ["Filesystem mutation state was refreshed before executor use."], + expectedAppliedState: nil, + fileMutationEvidence: partial + ) + } catch { + return failed( + operation, + "DevScope could not restore the prior absent source state.", + appliedSteps: ["Created owner-only recovery backup."], + backup: backup + ) + } + } + + if case .remove = operation, !record.sourceKind.requiresRecoverableSource { + do { + guard try writeTargetIsAuthorized( + record: record, + url: sourceURL, + expectedResourceIdentifier: metadata.resourceIdentifier, + expectedParentResourceIdentifier: backup.parentResourceIdentifier, + requiredCapability: .remove + ) else { + throw AutomationManagerConfigurationError.destinationAuthorizationUnavailable + } + let parentAuthorization = try Self.directoryAuthorization( + for: sourceURL.deletingLastPathComponent(), + resourceIdentifier: backup.parentResourceIdentifier + ) + let sourceAuthorization = try Self.fileAuthorization( + for: sourceURL, + directory: parentAuthorization, + resourceIdentifier: metadata.resourceIdentifier, + existed: true + ) + let trashReceipt = try Self.committedReceipt( + fileSystem.moveToTrash(sourceAuthorization) + ) + guard let trashURL = trashReceipt.resultURL else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + proposedSourceURL = trashURL + sourceMutationStep = "Moved the source to Trash." + } catch let partial as AutomationFilePartialMutation { + proposedSourceURL = partial.resultURL + return await failureAfterMutation( + operation: operation, + record: record, + linkedProcesses: linkedProcesses, + backup: backup, + preTransactionState: preTransactionState, + proposal: proposal, + originalSourceMetadata: metadata, + postMutationResourceIdentifier: nil, + sourceMutationStep: "Filesystem reported a partial Trash move.", + phase: .sourceMutationFailed, + evidence: ["Filesystem mutation state was refreshed before executor use."], + expectedAppliedState: nil, + fileMutationEvidence: partial + ) + } catch { + return failed( + operation, + "DevScope could not move the automation source to Trash.", + appliedSteps: ["Created owner-only recovery backup."], + backup: backup + ) + } + } + + let executorResult: AutomationExecutorResult + do { + executorResult = try await executor.apply( + operation, + to: record, + linkedProcesses: linkedProcesses, + proposedSourceURL: proposedSourceURL + ) + } catch let partial as AutomationFilePartialMutation { + return await failureAfterMutation( + operation: operation, + record: record, + linkedProcesses: linkedProcesses, + backup: backup, + preTransactionState: preTransactionState, + proposal: proposal, + originalSourceMetadata: metadata, + postMutationResourceIdentifier: postMutationResourceIdentifier, + sourceMutationStep: sourceMutationStep, + phase: .executorAttempted, + evidence: ["Executor filesystem cleanup reached a partial state."], + expectedAppliedState: nil, + fileMutationEvidence: partial + ) + } catch { + let expectedAppliedState = (error as? AutomationExecutorMutationFailure)?.appliedState + return await failureAfterMutation( + operation: operation, + record: record, + linkedProcesses: linkedProcesses, + backup: backup, + preTransactionState: preTransactionState, + proposal: proposal, + originalSourceMetadata: metadata, + postMutationResourceIdentifier: postMutationResourceIdentifier, + sourceMutationStep: sourceMutationStep, + phase: .executorAttempted, + evidence: [], + expectedAppliedState: expectedAppliedState + ) + } + + let refreshed = await refresh() + let refreshedProcesses: [DevProcess] + if operation.requiresFreshProcessSnapshot { + do { + refreshedProcesses = try await refreshProcesses() + } catch { + return await failureAfterMutation( + operation: operation, + record: record, + linkedProcesses: linkedProcesses, + backup: backup, + preTransactionState: preTransactionState, + proposal: proposal, + originalSourceMetadata: metadata, + postMutationResourceIdentifier: postMutationResourceIdentifier, + sourceMutationStep: sourceMutationStep, + phase: .verificationFailed, + evidence: ["Live process verification was unavailable after the operation."], + expectedAppliedState: executorResult.appliedState + ) + } + } else { + refreshedProcesses = [] + } + let verification = verify( + operation, + record: record, + executorResult: executorResult, + snapshot: refreshed, + proposal: proposal, + transactionSourceURL: sourceURL, + controlledIdentities: preTransactionState.linkedProcesses, + liveProcesses: refreshedProcesses + ) + guard verification.satisfied else { + return await failureAfterMutation( + operation: operation, + record: record, + linkedProcesses: linkedProcesses, + backup: backup, + preTransactionState: preTransactionState, + proposal: proposal, + originalSourceMetadata: metadata, + postMutationResourceIdentifier: postMutationResourceIdentifier, + sourceMutationStep: sourceMutationStep, + phase: .verificationFailed, + evidence: executorResult.evidence.map(evidenceRedactor) + verification.evidence, + expectedAppliedState: executorResult.appliedState + ) + } + + if let verifiedRecordID = verification.verifiedRecordID, + !backup.authorizedRecordIDs.contains(verifiedRecordID) { + do { + try authorizeRecoverySuccessor(verifiedRecordID, for: backup.id) + } catch let partial as AutomationFilePartialMutation { + return await failureAfterMutation( + operation: operation, + record: record, + linkedProcesses: linkedProcesses, + backup: backup, + preTransactionState: preTransactionState, + proposal: proposal, + originalSourceMetadata: metadata, + postMutationResourceIdentifier: postMutationResourceIdentifier, + sourceMutationStep: sourceMutationStep, + phase: .verificationFailed, + evidence: verification.evidence + ["Recovery identity persistence reached a partial filesystem state."], + expectedAppliedState: executorResult.appliedState, + fileMutationEvidence: partial + ) + } catch { + return await failureAfterMutation( + operation: operation, + record: record, + linkedProcesses: linkedProcesses, + backup: backup, + preTransactionState: preTransactionState, + proposal: proposal, + originalSourceMetadata: metadata, + postMutationResourceIdentifier: postMutationResourceIdentifier, + sourceMutationStep: sourceMutationStep, + phase: .verificationFailed, + evidence: verification.evidence + ["Recovery identity could not be persisted."], + expectedAppliedState: executorResult.appliedState + ) + } + } + + return AutomationOperationResult( + operation: operation.redactedForResult, + status: .succeeded, + appliedSteps: [ + "Created owner-only recovery backup.", + sourceMutationStep, + "Applied typed automation operation.", + "Refreshed automation inventory.", + "Verified operation postconditions.", + ].compactMap { $0 }, + verificationEvidence: executorResult.evidence.map(evidenceRedactor) + verification.evidence, + rollback: .notNeeded, + manualRecovery: nil, + backup: backup.redactedForResult + ) + } + + public func restorationManifests() -> [AutomationBackup] { + return backupsByID.values.sorted { lhs, rhs in + if lhs.createdAt != rhs.createdAt { return lhs.createdAt < rhs.createdAt } + return lhs.id.rawValue.uuidString < rhs.id.rawValue.uuidString + }.map(\.redactedForResult) + } + + private func acquireTransaction() async { + guard transactionIsActive else { + transactionIsActive = true + return + } + await withCheckedContinuation { continuation in + transactionWaiters.append(continuation) + } + } + + private func releaseTransaction() { + guard !transactionWaiters.isEmpty else { + transactionIsActive = false + return + } + transactionWaiters.removeFirst().resume() + } + + private func createBackup( + sourceData: Data, + checksum: String, + sourceURL: URL, + sourceExisted: Bool, + record: AutomationRecord + ) throws -> AutomationBackup { + let id = AutomationBackup.ID(rawValue: makeUUID()) + let parentMetadata = try fileSystem.metadata(for: sourceURL.deletingLastPathComponent()) + guard parentMetadata.resourceIdentifier?.isEmpty == false else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + let backupURL = backupDirectory.appendingPathComponent( + "\(id.rawValue.uuidString.lowercased()).backup", + isDirectory: false + ) + guard backupsByID[id] == nil, !fileSystem.itemExists(at: backupURL) else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + try fileSystem.createDirectory(backupDirectory, permissions: 0o700) + let rootMetadata = try fileSystem.metadata(for: backupDirectory) + guard rootMetadata.canonicalURL.standardizedFileURL.path == backupDirectory.standardizedFileURL.path, + rootMetadata.ownerUID == currentUID, + !rootMetadata.isSymbolicLink, + rootMetadata.resourceIdentifier?.isEmpty == false, + rootMetadata.permissions == 0o700 + else { throw AutomationManagerConfigurationError.recoverableSourceUnavailable } + if let expectedRootIdentity = recoveryRootResourceIdentifier, + rootMetadata.resourceIdentifier != expectedRootIdentity { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + recoveryRootResourceIdentifier = rootMetadata.resourceIdentifier + let rootAuthorization = try Self.directoryAuthorization( + for: backupDirectory, + resourceIdentifier: rootMetadata.resourceIdentifier + ) + let stagedBackup = try fileSystem.writeStagedFile( + nextTo: backupURL, + data: sourceData, + permissions: 0o600, + authorization: rootAuthorization + ) + guard recoveryRootIsAuthorized() else { + try removeCleanupArtifact(stagedBackup.authorization) + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + let absentBackup = try Self.fileAuthorization( + for: backupURL, + directory: rootAuthorization, + resourceIdentifier: nil, + existed: false + ) + _ = try committedReplacement(at: absentBackup, with: stagedBackup) + let backupResourceIdentifier: String + guard case .existing(let stagedBackupIdentity) = stagedBackup.authorization.expectation else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + backupResourceIdentifier = stagedBackupIdentity + let backupAuthorization = try Self.fileAuthorization( + for: backupURL, + directory: rootAuthorization, + resourceIdentifier: backupResourceIdentifier, + existed: true + ) + guard recoveryArtifactIsVerified( + at: backupURL, + expectedData: sourceData, + expectedChecksum: checksum, + requiresPropertyList: false + ) else { + try removeCleanupArtifact(backupAuthorization) + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + let backup = AutomationBackup( + id: id, + recordID: record.id, + sourceURL: sourceURL, + backupURL: backupURL, + checksum: checksum, + createdAt: now(), + sourceExisted: sourceExisted, + ownerUID: record.ownerUID ?? 0, + kind: record.kind, + sourceKind: record.sourceKind, + parentResourceIdentifier: parentMetadata.resourceIdentifier + ) + let manifestURL = backupDirectory.appendingPathComponent( + "\(id.rawValue.uuidString.lowercased()).manifest.plist", + isDirectory: false + ) + var installedManifestAuthorization: AutomationFileAuthorization? + var installedManifestData: Data? + do { + let manifestData = try Self.manifestData( + for: backup, + ownerUID: record.ownerUID ?? 0 + ) + let stagedManifest = try fileSystem.writeStagedFile( + nextTo: manifestURL, + data: manifestData, + permissions: 0o600, + authorization: rootAuthorization + ) + guard recoveryRootIsAuthorized() else { + try removeCleanupArtifact(stagedManifest.authorization) + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + let absentManifest = try Self.fileAuthorization( + for: manifestURL, + directory: rootAuthorization, + resourceIdentifier: nil, + existed: false + ) + let manifestReceipt = try committedReplacement(at: absentManifest, with: stagedManifest) + installedManifestAuthorization = manifestReceipt.primaryFile + installedManifestData = manifestData + guard recoveryArtifactIsVerified( + at: manifestURL, + expectedData: manifestData, + expectedChecksum: Self.checksum(manifestData), + requiresPropertyList: true + ) else { throw AutomationManagerConfigurationError.recoverableSourceUnavailable } + } catch { + let originalPartial = error as? AutomationFilePartialMutation + var cleanupCandidates = [installedManifestAuthorization, backupAuthorization].compactMap { + $0 + } + if let originalPartial { + for candidate in originalPartial.recoveryHandles + where candidate.directory.directoryURL.standardizedFileURL + == backupDirectory.standardizedFileURL + && !cleanupCandidates.contains(candidate) { + cleanupCandidates.append(candidate) + } + } + do { + try cleanupRecoveryArtifacts(cleanupCandidates) + } catch let cleanupPartial as AutomationFilePartialMutation { + let observedSurvivors = currentAuthorizedSurvivors( + cleanupCandidates + (originalPartial?.observedFiles ?? []) + + cleanupPartial.observedFiles + cleanupPartial.recoveryHandles + ) + let recoverySurvivors = currentAuthorizedSurvivors( + cleanupCandidates + (originalPartial?.recoveryHandles ?? []) + + cleanupPartial.recoveryHandles + ) + throw AutomationFilePartialMutation( + kind: originalPartial?.kind ?? .remove, + commitState: .unknown, + observedFiles: observedSurvivors, + recoveryHandle: recoverySurvivors.first, + recoveryHandles: recoverySurvivors, + resultURL: originalPartial?.resultURL ?? manifestURL + ) + } + if let originalPartial { + let observedSurvivors = currentAuthorizedSurvivors( + cleanupCandidates + originalPartial.observedFiles + ) + let recoverySurvivors = currentAuthorizedSurvivors( + cleanupCandidates + originalPartial.recoveryHandles + ) + throw AutomationFilePartialMutation( + kind: originalPartial.kind, + commitState: originalPartial.commitState, + observedFiles: observedSurvivors, + recoveryHandle: recoverySurvivors.first, + recoveryHandles: recoverySurvivors, + resultURL: originalPartial.resultURL + ) + } + throw error + } + guard let installedManifestAuthorization, let installedManifestData else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + backupsByID[id] = backup + manifestURLsByID[id] = manifestURL + backupAuthorizationsByID[id] = backupAuthorization + manifestAuthorizationsByID[id] = installedManifestAuthorization + manifestDataByID[id] = installedManifestData + try pruneRecoveryEvidence(preserving: id) + return backup + } + + private static func manifestData( + for backup: AutomationBackup, + ownerUID: uid_t + ) throws -> Data { + try PropertyListSerialization.data( + fromPropertyList: [ + "version": 2, + "id": backup.id.rawValue.uuidString.lowercased(), + "recordID": backup.recordID.rawValue, + "sourcePath": backup.sourceURL.path, + "backupPath": backup.backupURL.path, + "checksum": backup.checksum, + "createdAt": backup.createdAt.timeIntervalSince1970, + "sourceExisted": backup.sourceExisted, + "ownerUID": Int(ownerUID), + "kind": backup.kind.rawValue, + "sourceKind": backup.sourceKind.rawValue, + "authorizedRecordIDs": backup.authorizedRecordIDs.map(\.rawValue).sorted(), + "parentResourceIdentifier": backup.parentResourceIdentifier ?? "", + ], + format: .binary, + options: 0 + ) + } + + private static func loadManifests( + fileSystem: any AutomationFileSystem, + backupDirectory: URL, + currentUID: uid_t + ) -> ( + backups: [AutomationBackup.ID: AutomationBackup], + manifestURLs: [AutomationBackup.ID: URL], + backupAuthorizations: [AutomationBackup.ID: AutomationFileAuthorization], + manifestAuthorizations: [AutomationBackup.ID: AutomationFileAuthorization], + manifestData: [AutomationBackup.ID: Data] + ) { + let recoveryRoot = backupDirectory.standardizedFileURL + guard let rootMetadata = try? fileSystem.metadata(for: recoveryRoot), + rootMetadata.canonicalURL.standardizedFileURL.path == recoveryRoot.path, + rootMetadata.ownerUID == currentUID, + !rootMetadata.isSymbolicLink, + let rootResourceIdentifier = rootMetadata.resourceIdentifier, + !rootResourceIdentifier.isEmpty, + let rootAuthorization = AutomationDirectoryAuthorization( + directoryURL: recoveryRoot, + resourceIdentifier: rootResourceIdentifier + ), + rootMetadata.permissions == 0o700, + let urls = try? fileSystem.plistURLs(in: recoveryRoot) + else { return ([:], [:], [:], [:], [:]) } + var backups: [AutomationBackup.ID: AutomationBackup] = [:] + var manifestURLs: [AutomationBackup.ID: URL] = [:] + var backupAuthorizations: [AutomationBackup.ID: AutomationFileAuthorization] = [:] + var manifestAuthorizations: [AutomationBackup.ID: AutomationFileAuthorization] = [:] + var manifestData: [AutomationBackup.ID: Data] = [:] + for manifestURL in urls where manifestURL.lastPathComponent.hasSuffix(".manifest.plist") { + guard let data = try? fileSystem.read(manifestURL), + let value = try? PropertyListSerialization.propertyList( + from: data, + options: [], + format: nil + ), + let dictionary = value as? [String: Any], + Set(dictionary.keys) == Set([ + "version", "id", "recordID", "sourcePath", "backupPath", "checksum", + "createdAt", "sourceExisted", "ownerUID", "kind", "sourceKind", + "authorizedRecordIDs", "parentResourceIdentifier", + ]), + dictionary["version"] as? Int == 2, + let rawID = dictionary["id"] as? String, + let uuid = UUID(uuidString: rawID), + manifestURL.standardizedFileURL == recoveryRoot.appendingPathComponent( + "\(uuid.uuidString.lowercased()).manifest.plist" + ).standardizedFileURL, + let recordID = dictionary["recordID"] as? String, + let sourcePath = dictionary["sourcePath"] as? String, + let backupPath = dictionary["backupPath"] as? String, + let checksum = dictionary["checksum"] as? String, + let createdAt = dictionary["createdAt"] as? TimeInterval, + let sourceExisted = dictionary["sourceExisted"] as? Bool, + let ownerNumber = dictionary["ownerUID"] as? Int, + let kindRaw = dictionary["kind"] as? String, + let kind = AutomationKind(rawValue: kindRaw), + let sourceKindRaw = dictionary["sourceKind"] as? String, + let sourceKind = AutomationSourceKind(rawValue: sourceKindRaw), + let authorizedRawIDs = dictionary["authorizedRecordIDs"] as? [String], + let parentResourceIdentifier = dictionary["parentResourceIdentifier"] as? String, + ownerNumber == Int(currentUID), + let derivedBackupURL = Optional(recoveryRoot.appendingPathComponent( + "\(uuid.uuidString.lowercased()).backup" + ).standardizedFileURL), + URL(fileURLWithPath: backupPath).standardizedFileURL == derivedBackupURL, + fileSystem.itemExists(at: derivedBackupURL), + let manifestMetadata = try? fileSystem.metadata(for: manifestURL), + let backupMetadata = try? fileSystem.metadata(for: derivedBackupURL), + manifestMetadata.canonicalURL.standardizedFileURL == manifestURL.standardizedFileURL, + backupMetadata.canonicalURL.standardizedFileURL == derivedBackupURL, + !manifestMetadata.isSymbolicLink, + !backupMetadata.isSymbolicLink, + manifestMetadata.ownerUID == currentUID, + backupMetadata.ownerUID == currentUID, + let manifestIdentity = manifestMetadata.resourceIdentifier, + !manifestIdentity.isEmpty, + let backupIdentity = backupMetadata.resourceIdentifier, + !backupIdentity.isEmpty, + manifestMetadata.permissions == 0o600, + backupMetadata.permissions == 0o600, + let backupData = try? fileSystem.read(derivedBackupURL), + checksum == Self.checksum(backupData), + let manifestAuthorization = AutomationFileAuthorization( + fileURL: manifestURL, + directory: rootAuthorization, + expectation: .existing(resourceIdentifier: manifestIdentity) + ), + let backupAuthorization = AutomationFileAuthorization( + fileURL: derivedBackupURL, + directory: rootAuthorization, + expectation: .existing(resourceIdentifier: backupIdentity) + ) + else { continue } + let id = AutomationBackup.ID(rawValue: uuid) + let backup = AutomationBackup( + id: id, + recordID: AutomationRecord.ID(rawValue: recordID), + sourceURL: URL(fileURLWithPath: sourcePath), + backupURL: derivedBackupURL, + checksum: checksum, + createdAt: Date(timeIntervalSince1970: createdAt), + sourceExisted: sourceExisted, + ownerUID: uid_t(ownerNumber), + kind: kind, + sourceKind: sourceKind, + authorizedRecordIDs: Set(authorizedRawIDs.map(AutomationRecord.ID.init(rawValue:))), + parentResourceIdentifier: parentResourceIdentifier.isEmpty ? nil : parentResourceIdentifier + ) + backups[id] = backup + manifestURLs[id] = manifestURL + manifestAuthorizations[id] = manifestAuthorization + backupAuthorizations[id] = backupAuthorization + manifestData[id] = data + } + return (backups, manifestURLs, backupAuthorizations, manifestAuthorizations, manifestData) + } + + private func authorizeRecoverySuccessor( + _ recordID: AutomationRecord.ID, + for backupID: AutomationBackup.ID + ) throws { + guard let backup = backupsByID[backupID], + let manifestURL = manifestURLsByID[backupID], + let manifestAuthorization = manifestAuthorizationsByID[backupID], + let backupAuthorization = backupAuthorizationsByID[backupID], + let previousManifestData = manifestDataByID[backupID] + else { throw AutomationManagerConfigurationError.recoverableSourceUnavailable } + let updated = AutomationBackup( + id: backup.id, + recordID: backup.recordID, + sourceURL: backup.sourceURL, + backupURL: backup.backupURL, + checksum: backup.checksum, + createdAt: backup.createdAt, + sourceExisted: backup.sourceExisted, + ownerUID: backup.ownerUID, + kind: backup.kind, + sourceKind: backup.sourceKind, + authorizedRecordIDs: backup.authorizedRecordIDs.union([recordID]), + parentResourceIdentifier: backup.parentResourceIdentifier + ) + let manifestData = try Self.manifestData(for: updated, ownerUID: backup.ownerUID) + let rootAuthorization = manifestAuthorization.directory + let artifactsRemainExact = rootAuthorization == backupAuthorization.directory + && recoveryRootIsAuthorized() + && authorizationIsCurrent(manifestAuthorization) + && authorizationIsCurrent(backupAuthorization) + && recoveryArtifactIsVerified( + at: manifestURL, + expectedData: previousManifestData, + expectedChecksum: Self.checksum(previousManifestData), + requiresPropertyList: true + ) + && ((try? fileSystem.read(backup.backupURL)).map { + recoveryArtifactIsVerified( + at: backup.backupURL, + expectedData: $0, + expectedChecksum: backup.checksum, + requiresPropertyList: false + ) + } ?? false) + guard artifactsRemainExact else { + let observed = [manifestURL, backup.backupURL].compactMap { + try? currentRecoveryAuthorization(at: $0, authorization: rootAuthorization) + } + reconcileRecoveryMapsFromRetainedAuthority() + throw AutomationFilePartialMutation( + kind: .replace, + commitState: .unknown, + observedFiles: observed, + recoveryHandle: nil, + resultURL: manifestURL + ) + } + let staged = try fileSystem.writeStagedFile( + nextTo: manifestURL, + data: manifestData, + permissions: 0o600, + authorization: rootAuthorization + ) + guard recoveryRootIsAuthorized() else { + try removeCleanupArtifact(staged.authorization) + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + let installedAuthorization = try Self.fileAuthorization( + for: manifestURL, + directory: rootAuthorization, + resourceIdentifier: staged.binding.resourceIdentifier, + existed: true + ) + let replacementReceipt: AutomationFileMutationReceipt + do { + replacementReceipt = try committedReplacement(at: manifestAuthorization, with: staged) + } catch let partial as AutomationFilePartialMutation { + let restored: AutomationFileAuthorization + do { + guard let compensationDestination = currentAuthorizedSurvivors( + [manifestAuthorization, installedAuthorization] + ).first else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + restored = try compensateRecoveryArtifact( + previousManifestData, + at: manifestURL, + authorization: rootAuthorization, + destination: compensationDestination + ) + retainRestoredManifest( + for: backupID, + authorization: restored, + data: previousManifestData + ) + try cleanupRecoveryArtifacts(partial.recoveryHandles.filter { + $0.fileURL.standardizedFileURL == staged.url.standardizedFileURL + && $0.directory == rootAuthorization + && $0.expectation == staged.authorization.expectation + }) + } catch let compensation as AutomationFilePartialMutation { + throw aggregatePartialMutations( + partial, + compensation, + at: manifestURL, + authorization: rootAuthorization + ) + } catch { + throw aggregatePartialMutations( + partial, + nil, + at: manifestURL, + authorization: rootAuthorization + ) + } + let observedSurvivors = currentAuthorizedSurvivors( + [restored] + partial.observedFiles + ) + let recoverySurvivors = currentAuthorizedSurvivors( + [restored] + partial.recoveryHandles + ) + throw AutomationFilePartialMutation( + kind: .replace, + commitState: partial.commitState, + observedFiles: observedSurvivors, + recoveryHandle: recoverySurvivors.first, + recoveryHandles: recoverySurvivors, + resultURL: manifestURL + ) + } + guard let exactInstalled = replacementReceipt.primaryFile, + exactInstalled == installedAuthorization, + authorizationIsCurrent(exactInstalled), + recoveryRootIsAuthorized(), + authorizationIsCurrent(backupAuthorization), + let finalBackupData = try? fileSystem.read(backup.backupURL), + recoveryArtifactIsVerified( + at: backup.backupURL, + expectedData: finalBackupData, + expectedChecksum: backup.checksum, + requiresPropertyList: false + ), + authorizationIsCurrent(backupAuthorization), + recoveryArtifactIsVerified( + at: manifestURL, + expectedData: manifestData, + expectedChecksum: Self.checksum(manifestData), + requiresPropertyList: true + ), + authorizationIsCurrent(exactInstalled) + else { + let knownInstalled = currentAuthorizedSurvivors([installedAuthorization]) + let committed = AutomationFilePartialMutation( + kind: .replace, + commitState: .committed, + observedFiles: knownInstalled, + recoveryHandle: knownInstalled.first, + resultURL: manifestURL + ) + let restored: AutomationFileAuthorization + do { + guard authorizationIsCurrent(installedAuthorization) else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + restored = try compensateRecoveryArtifact( + previousManifestData, + at: manifestURL, + authorization: rootAuthorization, + destination: installedAuthorization + ) + retainRestoredManifest( + for: backupID, + authorization: restored, + data: previousManifestData + ) + } catch let compensation as AutomationFilePartialMutation { + let aggregate = aggregatePartialMutations( + committed, + compensation, + at: manifestURL, + authorization: rootAuthorization + ) + reconcileRecoveryMapsFromRetainedAuthority() + throw aggregate + } catch { + let aggregate = aggregatePartialMutations( + committed, + nil, + at: manifestURL, + authorization: rootAuthorization + ) + reconcileRecoveryMapsFromRetainedAuthority() + throw aggregate + } + let ambientBackup = try? currentRecoveryAuthorization( + at: backup.backupURL, + authorization: rootAuthorization + ) + reconcileRecoveryMapsFromRetainedAuthority() + throw AutomationFilePartialMutation( + kind: .replace, + commitState: .committed, + observedFiles: [restored] + [ambientBackup].compactMap { $0 }, + recoveryHandle: restored, + resultURL: manifestURL + ) + } + backupsByID[backupID] = updated + manifestAuthorizationsByID[backupID] = exactInstalled + manifestDataByID[backupID] = manifestData + } + + private func retainRestoredManifest( + for backupID: AutomationBackup.ID, + authorization: AutomationFileAuthorization, + data: Data + ) { + manifestAuthorizationsByID[backupID] = authorization + manifestDataByID[backupID] = data + } + + private func compensateRecoveryArtifact( + _ data: Data, + at url: URL, + authorization rootAuthorization: AutomationDirectoryAuthorization, + destination: AutomationFileAuthorization + ) throws -> AutomationFileAuthorization { + let staged = try fileSystem.writeStagedFile( + nextTo: url, + data: data, + permissions: 0o600, + authorization: rootAuthorization + ) + let receipt = try committedReplacement(at: destination, with: staged) + guard let installed = receipt.primaryFile, + installed.expectation == staged.authorization.expectation, + authorizationIsCurrent(installed), + recoveryArtifactIsVerified( + at: url, + expectedData: data, + expectedChecksum: Self.checksum(data), + requiresPropertyList: true + ) else { throw AutomationManagerConfigurationError.recoverableSourceUnavailable } + return installed + } + + private func currentRecoveryAuthorization( + at url: URL, + authorization rootAuthorization: AutomationDirectoryAuthorization + ) throws -> AutomationFileAuthorization { + let metadata = try fileSystem.metadata(for: url) + return try Self.fileAuthorization( + for: url, + directory: rootAuthorization, + resourceIdentifier: metadata.resourceIdentifier, + existed: true + ) + } + + private func aggregatePartialMutations( + _ primary: AutomationFilePartialMutation, + _ secondary: AutomationFilePartialMutation?, + at url: URL, + authorization rootAuthorization: AutomationDirectoryAuthorization + ) -> AutomationFilePartialMutation { + var observed: [AutomationFileAuthorization] = [] + for candidate in primary.observedFiles + (secondary?.observedFiles ?? []) + where authorizationIsCurrent(candidate) && !observed.contains(candidate) { + observed.append(candidate) + } + var handles: [AutomationFileAuthorization] = [] + for candidate in primary.recoveryHandles + (secondary?.recoveryHandles ?? []) + where authorizationIsCurrent(candidate) && !handles.contains(candidate) { + handles.append(candidate) + } + if let current = try? currentRecoveryAuthorization( + at: url, + authorization: rootAuthorization + ) { + if !observed.contains(current) { observed.append(current) } + } + return AutomationFilePartialMutation( + kind: .replace, + commitState: .unknown, + observedFiles: observed, + recoveryHandle: handles.first, + recoveryHandles: handles, + resultURL: url + ) + } + + private func recoveryArtifactIsVerified( + at url: URL, + expectedData: Data, + expectedChecksum: String, + requiresPropertyList: Bool + ) -> Bool { + let artifact = url.standardizedFileURL + let root = backupDirectory.standardizedFileURL + guard recoveryRootIsAuthorized(), + artifact.deletingLastPathComponent().standardizedFileURL.path == root.path, + let metadata = try? fileSystem.metadata(for: artifact), + metadata.canonicalURL.standardizedFileURL.path == artifact.path, + metadata.ownerUID == currentUID, + !metadata.isSymbolicLink, + metadata.resourceIdentifier?.isEmpty == false, + metadata.permissions == 0o600, + let actualData = try? fileSystem.read(artifact), + actualData == expectedData, + Self.checksum(actualData) == expectedChecksum + else { return false } + if requiresPropertyList { + return (try? PropertyListSerialization.propertyList( + from: actualData, + options: [], + format: nil + )) != nil + } + return true + } + + private func pruneRecoveryEvidence(preserving currentID: AutomationBackup.ID?) throws { + guard recoveryRootIsAuthorized() else { return } + let rootMetadata = try fileSystem.metadata(for: backupDirectory) + let rootAuthorization = try Self.directoryAuthorization( + for: backupDirectory, + resourceIdentifier: rootMetadata.resourceIdentifier + ) + let cutoff = now().addingTimeInterval(-30 * 24 * 60 * 60) + let ordered = backupsByID.values.sorted { lhs, rhs in + if lhs.createdAt != rhs.createdAt { return lhs.createdAt > rhs.createdAt } + return lhs.id.rawValue.uuidString > rhs.id.rawValue.uuidString + } + var retained = Set(ordered.prefix(20).map(\.id)) + if let currentID { retained.insert(currentID) } + for backup in ordered where backup.createdAt < cutoff || !retained.contains(backup.id) { + guard backup.id != currentID else { continue } + guard let manifestURL = manifestURLsByID[backup.id], + let manifestAuthorization = manifestAuthorizationsByID[backup.id], + let backupAuthorization = backupAuthorizationsByID[backup.id], + let manifestData = manifestDataByID[backup.id] + else { + reconcileRecoveryMapsFromRetainedAuthority() + continue + } + let backupData = try? fileSystem.read(backup.backupURL) + guard manifestAuthorization.directory == rootAuthorization, + backupAuthorization.directory == rootAuthorization, + authorizationIsCurrent(manifestAuthorization), + authorizationIsCurrent(backupAuthorization), + recoveryArtifactIsVerified( + at: manifestURL, + expectedData: manifestData, + expectedChecksum: Self.checksum(manifestData), + requiresPropertyList: true + ), + let backupData, + recoveryArtifactIsVerified( + at: backup.backupURL, + expectedData: backupData, + expectedChecksum: backup.checksum, + requiresPropertyList: false + ) + else { + // A fresh occupant is evidence only. Retained-authority reconciliation drops + // the invalid pair; no destructive authority comes from the ambient namespace. + reconcileRecoveryMapsFromRetainedAuthority() + continue + } + var manifestDeletionCommitted = false + do { + try removeCleanupArtifact(manifestAuthorization) + manifestDeletionCommitted = true + try removeCleanupArtifact(backupAuthorization) + } catch { + let primaryPartial = error as? AutomationFilePartialMutation + if !manifestDeletionCommitted { + reconcileRecoveryMapsFromRetainedAuthority() + if let primaryPartial { throw primaryPartial } + throw error + } + var compensationPartial: AutomationFilePartialMutation? + var restoredManifest: AutomationFileAuthorization? + if !fileSystem.itemExists(at: manifestURL), + authorizationIsCurrent(backupAuthorization) { + do { + let absentManifest = try Self.fileAuthorization( + for: manifestURL, + directory: rootAuthorization, + resourceIdentifier: nil, + existed: false + ) + restoredManifest = try compensateRecoveryArtifact( + manifestData, + at: manifestURL, + authorization: rootAuthorization, + destination: absentManifest + ) + if let restoredManifest { + retainRestoredManifest( + for: backup.id, + authorization: restoredManifest, + data: manifestData + ) + } + if let primaryPartial { + try cleanupRecoveryArtifacts(primaryPartial.recoveryHandles.filter { + $0.fileURL.standardizedFileURL != manifestURL.standardizedFileURL + && $0.expectation == backupAuthorization.expectation + && $0.directory == rootAuthorization + }) + } + } catch let partial as AutomationFilePartialMutation { + compensationPartial = partial + } catch { + compensationPartial = AutomationFilePartialMutation( + kind: .replace, + commitState: .unknown, + observedFiles: [], + recoveryHandle: nil, + resultURL: manifestURL + ) + } + } + let currentArtifacts = [ + try? currentRecoveryAuthorization( + at: manifestURL, + authorization: rootAuthorization + ), + try? currentRecoveryAuthorization( + at: backup.backupURL, + authorization: rootAuthorization + ), + ].compactMap { $0 } + let knownRecoveryCandidates = [ + restoredManifest, + authorizationIsCurrent(manifestAuthorization) ? manifestAuthorization : nil, + authorizationIsCurrent(backupAuthorization) ? backupAuthorization : nil, + ].compactMap { $0 } + let observedSurvivors = currentAuthorizedSurvivors( + currentArtifacts + + knownRecoveryCandidates + + (primaryPartial?.observedFiles ?? []) + + (primaryPartial?.recoveryHandles ?? []) + + (compensationPartial?.observedFiles ?? []) + + (compensationPartial?.recoveryHandles ?? []) + ) + let recoverySurvivors = currentAuthorizedSurvivors( + knownRecoveryCandidates + + (primaryPartial?.recoveryHandles ?? []) + + (compensationPartial?.recoveryHandles ?? []) + ) + reconcileRecoveryMapsFromRetainedAuthority() + throw AutomationFilePartialMutation( + kind: .remove, + commitState: .unknown, + observedFiles: observedSurvivors, + recoveryHandle: recoverySurvivors.first, + recoveryHandles: recoverySurvivors, + resultURL: manifestURL + ) + } + if !fileSystem.itemExists(at: backup.backupURL), + !fileSystem.itemExists(at: manifestURL) + { + backupsByID.removeValue(forKey: backup.id) + manifestURLsByID.removeValue(forKey: backup.id) + backupAuthorizationsByID.removeValue(forKey: backup.id) + manifestAuthorizationsByID.removeValue(forKey: backup.id) + manifestDataByID.removeValue(forKey: backup.id) + } else { + let observedSurvivors = currentAuthorizedSurvivors([ + try? currentRecoveryAuthorization(at: manifestURL, authorization: rootAuthorization), + try? currentRecoveryAuthorization(at: backup.backupURL, authorization: rootAuthorization), + ].compactMap { $0 }) + let recoverySurvivors = currentAuthorizedSurvivors([ + manifestAuthorization, + backupAuthorization, + ]) + reconcileRecoveryMapsFromRetainedAuthority() + throw AutomationFilePartialMutation( + kind: .remove, + commitState: .unknown, + observedFiles: observedSurvivors, + recoveryHandle: recoverySurvivors.first, + recoveryHandles: recoverySurvivors, + resultURL: manifestURL + ) + } + } + } + + private func reconcileRecoveryMapsFromRetainedAuthority() { + guard recoveryRootIsAuthorized() else { + backupsByID.removeAll() + manifestURLsByID.removeAll() + backupAuthorizationsByID.removeAll() + manifestAuthorizationsByID.removeAll() + manifestDataByID.removeAll() + return + } + var retainedBackups: [AutomationBackup.ID: AutomationBackup] = [:] + var retainedManifestURLs: [AutomationBackup.ID: URL] = [:] + var retainedBackupAuthorizations: [ + AutomationBackup.ID: AutomationFileAuthorization + ] = [:] + var retainedManifestAuthorizations: [ + AutomationBackup.ID: AutomationFileAuthorization + ] = [:] + var retainedManifestData: [AutomationBackup.ID: Data] = [:] + for (id, backup) in backupsByID { + guard let manifestURL = manifestURLsByID[id], + let backupAuthorization = backupAuthorizationsByID[id], + let manifestAuthorization = manifestAuthorizationsByID[id], + let manifestData = manifestDataByID[id], + authorizationIsCurrent(backupAuthorization), + authorizationIsCurrent(manifestAuthorization), + let backupData = try? fileSystem.read(backup.backupURL), + recoveryArtifactIsVerified( + at: backup.backupURL, + expectedData: backupData, + expectedChecksum: backup.checksum, + requiresPropertyList: false + ), + recoveryArtifactIsVerified( + at: manifestURL, + expectedData: manifestData, + expectedChecksum: Self.checksum(manifestData), + requiresPropertyList: true + ) + else { continue } + retainedBackups[id] = backup + retainedManifestURLs[id] = manifestURL + retainedBackupAuthorizations[id] = backupAuthorization + retainedManifestAuthorizations[id] = manifestAuthorization + retainedManifestData[id] = manifestData + } + backupsByID = retainedBackups + manifestURLsByID = retainedManifestURLs + backupAuthorizationsByID = retainedBackupAuthorizations + manifestAuthorizationsByID = retainedManifestAuthorizations + manifestDataByID = retainedManifestData + } + + private func recoveryRootIsAuthorized() -> Bool { + guard let expectedIdentity = recoveryRootResourceIdentifier, + let metadata = try? fileSystem.metadata(for: backupDirectory), + metadata.canonicalURL.standardizedFileURL.path == backupDirectory.standardizedFileURL.path, + metadata.ownerUID == currentUID, + !metadata.isSymbolicLink, + metadata.resourceIdentifier == expectedIdentity, + metadata.permissions == 0o700 + else { return false } + return true + } + + private func removeCleanupArtifact(_ authorization: AutomationFileAuthorization) throws { + let outcome: AutomationFileMutationOutcome + do { + outcome = try fileSystem.removeItem(authorization) + } catch let partial as AutomationFilePartialMutation { + throw partial + } catch { + throw error + } + switch outcome { + case .committed: + return + case .unchanged: + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + } + + private func cleanupRecoveryArtifacts( + _ authorizations: [AutomationFileAuthorization] + ) throws { + var committedDeletion = false + var partials: [AutomationFilePartialMutation] = [] + var firstRawError: Error? + for authorization in authorizations where fileSystem.itemExists(at: authorization.fileURL) { + do { + switch try fileSystem.removeItem(authorization) { + case .committed: + committedDeletion = true + case .unchanged: + firstRawError = firstRawError + ?? AutomationManagerConfigurationError.recoverableSourceUnavailable + } + } catch let partial as AutomationFilePartialMutation { + partials.append(partial) + } catch { + firstRawError = firstRawError ?? error + } + } + if !partials.isEmpty || committedDeletion && firstRawError != nil { + var observed = partials.flatMap(\.observedFiles) + var handles = partials.flatMap(\.recoveryHandles) + for authorization in authorizations { + guard authorizationIsCurrent(authorization) else { continue } + if !observed.contains(authorization) { observed.append(authorization) } + if !handles.contains(authorization) { handles.append(authorization) } + } + throw AutomationFilePartialMutation( + kind: .remove, + commitState: .unknown, + observedFiles: observed, + recoveryHandle: handles.first, + recoveryHandles: handles, + resultURL: nil + ) + } + if let firstRawError { throw firstRawError } + } + + private func currentAuthorizedSurvivors( + _ candidates: [AutomationFileAuthorization] + ) -> [AutomationFileAuthorization] { + var survivors: [AutomationFileAuthorization] = [] + for candidate in candidates { + guard authorizationIsCurrent(candidate), !survivors.contains(candidate) + else { continue } + survivors.append(candidate) + } + return survivors + } + + private func authorizationIsCurrent(_ authorization: AutomationFileAuthorization) -> Bool { + guard case .existing(let expectedIdentity) = authorization.expectation, + let directoryMetadata = try? fileSystem.metadata( + for: authorization.directory.directoryURL + ), + directoryMetadata.resourceIdentifier == authorization.directory.resourceIdentifier, + let metadata = try? fileSystem.metadata(for: authorization.fileURL), + metadata.resourceIdentifier == expectedIdentity + else { return false } + return true + } + + private func revalidationFailure( + operation: AutomationOperation, + record: AutomationRecord, + sourceURL: URL, + expectedChecksum: String, + initialMetadata: AutomationFileMetadata + ) async throws -> String? { + do { + let sourceData: Data + let metadata: AutomationFileMetadata + if record.sourceKind.requiresRecoverableSource { + let captured = try await recoverableSource(record) + guard captured.transactionURL.standardizedFileURL.path == sourceURL.standardizedFileURL.path, + captured.checksum == Self.sourceChecksum(captured.data, kind: record.kind), + try fileSystem.read(sourceURL) == captured.data, + try fileSystem.metadata(for: sourceURL) == captured.metadata + else { + return "The recoverable source identity changed immediately before mutation." + } + sourceData = captured.data + metadata = captured.metadata + } else { + sourceData = try fileSystem.read(sourceURL) + metadata = try fileSystem.metadata(for: sourceURL) + } + guard Self.sourceChecksum(sourceData, kind: record.kind) == expectedChecksum else { + return "The automation source changed immediately before mutation." + } + let context = try capabilityContext(record) + guard let resourceIdentifier = metadata.resourceIdentifier, + !resourceIdentifier.isEmpty, + metadata.canonicalURL.standardizedFileURL.path == sourceURL.standardizedFileURL.path, + metadata.ownerUID == context.sourceOwnerUID, + metadata.isSymbolicLink == context.isSymlink, + resourceIdentifier == initialMetadata.resourceIdentifier + else { + return "The automation source identity changed immediately before mutation." + } + let decision = AutomationCapabilityPolicy.decision(for: record, context: context) + guard decision.capabilities.contains(operation.requiredCapability) else { + return decision.reason ?? "This operation is no longer available for the source." + } + return nil + } catch let partial as AutomationFilePartialMutation { + throw partial + } catch { + return "DevScope could not revalidate the automation source immediately before mutation." + } + } + + private func verifyDestination( + _ proposal: SourceProposal, + record: AutomationRecord, + selectedSourceURL: URL, + selectedSourceData: Data, + selectedMetadata: AutomationFileMetadata + ) throws -> DestinationSnapshot { + let destination = proposal.destination.standardizedFileURL + let parent = destination.deletingLastPathComponent().standardizedFileURL + let parentMetadata = try fileSystem.metadata(for: parent) + let context = try destinationContext(record, destination) + guard context.canonicalPathIsApproved else { + throw ProposalError("The intended destination resolves outside the approved automation folder.") + } + guard !context.isManaged else { + throw ProposalError("The intended destination is managed by your organization.") + } + guard !context.isSymlink else { + throw ProposalError("The intended destination is a symbolic link.") + } + guard context.sourceOwnerUID == context.currentUID else { + throw ProposalError("The intended destination is not owned by the current user.") + } + guard parentMetadata.canonicalURL.standardizedFileURL.path == parent.path, + parentMetadata.ownerUID == context.currentUID, + !parentMetadata.isSymbolicLink, + parentMetadata.resourceIdentifier?.isEmpty == false + else { + throw ProposalError("The intended destination parent identity could not be established.") + } + + if destination == selectedSourceURL.standardizedFileURL, + fileSystem.itemExists(at: destination) { + guard selectedMetadata.ownerUID == context.currentUID, + selectedMetadata.resourceIdentifier != nil + else { + throw ProposalError("The intended destination identity could not be established.") + } + return DestinationSnapshot( + existed: true, + data: selectedSourceData, + metadata: selectedMetadata, + parentMetadata: parentMetadata + ) + } + + if fileSystem.itemExists(at: destination) { + let data = try fileSystem.read(destination) + let metadata = try fileSystem.metadata(for: destination) + guard metadata.canonicalURL.standardizedFileURL.path == destination.path, + metadata.ownerUID == context.currentUID, + !metadata.isSymbolicLink, + let resourceIdentifier = metadata.resourceIdentifier, + !resourceIdentifier.isEmpty + else { + throw ProposalError("The intended destination identity could not be established.") + } + guard let expected = proposal.expectedDestinationChecksum, + expected == Self.checksum(data) + else { + throw ProposalError("The intended destination changed since it was inspected.") + } + return DestinationSnapshot( + existed: true, + data: data, + metadata: metadata, + parentMetadata: parentMetadata + ) + } + + return DestinationSnapshot( + existed: false, + data: nil, + metadata: parentMetadata, + parentMetadata: parentMetadata + ) + } + + private func destinationRevalidationFailure( + _ proposal: SourceProposal, + snapshot: DestinationSnapshot, + record: AutomationRecord + ) -> String? { + do { + let destination = proposal.destination.standardizedFileURL + let context = try destinationContext(record, destination) + guard context.canonicalPathIsApproved, + context.sourceOwnerUID == context.currentUID, + !context.isSymlink, + !context.isManaged + else { + return "The intended destination authorization changed immediately before mutation." + } + let parent = destination.deletingLastPathComponent().standardizedFileURL + let currentParentMetadata = try fileSystem.metadata(for: parent) + guard currentParentMetadata.canonicalURL.standardizedFileURL.path == parent.path, + currentParentMetadata.ownerUID == snapshot.parentMetadata.ownerUID, + !currentParentMetadata.isSymbolicLink, + currentParentMetadata.resourceIdentifier == snapshot.parentMetadata.resourceIdentifier + else { + return "The intended destination parent changed immediately before mutation." + } + if snapshot.existed { + guard fileSystem.itemExists(at: destination), + let previousData = snapshot.data + else { + return "The intended destination changed immediately before mutation." + } + let data = try fileSystem.read(destination) + let metadata = try fileSystem.metadata(for: destination) + guard data == previousData, + metadata.canonicalURL.standardizedFileURL.path == destination.path, + metadata.ownerUID == snapshot.metadata.ownerUID, + metadata.isSymbolicLink == snapshot.metadata.isSymbolicLink, + metadata.resourceIdentifier != nil, + metadata.resourceIdentifier == snapshot.metadata.resourceIdentifier + else { + return "The intended destination identity changed immediately before mutation." + } + } else { + guard !fileSystem.itemExists(at: destination) else { + return "The intended destination appeared immediately before mutation." + } + let metadata = currentParentMetadata + guard metadata.canonicalURL.standardizedFileURL.path == parent.path, + metadata.ownerUID == snapshot.metadata.ownerUID, + !metadata.isSymbolicLink, + metadata.resourceIdentifier != nil, + metadata.resourceIdentifier == snapshot.metadata.resourceIdentifier + else { + return "The intended destination parent changed immediately before mutation." + } + } + return nil + } catch { + return "DevScope could not revalidate the intended destination immediately before mutation." + } + } + + private func makeProposal( + for operation: AutomationOperation, + record: AutomationRecord, + sourceURL: URL, + selectedSourceData: Data + ) throws -> SourceProposal? { + switch operation { + case .edit(let payload): + try Self.validate(payload) + let data = try Self.sourceData(for: payload, kind: record.kind) + try Self.validate( + data: data, + against: payload, + kind: record.kind, + sourceURL: sourceURL, + ownerUID: record.ownerUID ?? 0 + ) + let cronBinding = record.kind == .cron + ? try Self.cronProposalBinding( + originalData: selectedSourceData, + intendedData: data, + record: record, + payload: payload + ) + : nil + return SourceProposal( + data: data, + destination: sourceURL, + intendedLabel: payload.label, + intendedRecordID: cronBinding?.intendedRecordID, + cronBinding: cronBinding, + intendedChecksum: Self.sourceChecksum(data, kind: record.kind), + expectedDestinationChecksum: payload.expectedDestinationChecksum + ) + case .duplicate(let payload): + try Self.validate(payload) + guard let destination = payload.destination else { + throw ProposalError("A distinct duplicate destination is required.") + } + let data = try Self.sourceData(for: payload, kind: record.kind) + try Self.validate( + data: data, + against: payload, + kind: record.kind, + sourceURL: destination, + ownerUID: record.ownerUID ?? 0 + ) + let cronBinding = record.kind == .cron + ? try Self.cronDuplicateBinding( + originalData: selectedSourceData, + intendedData: data, + record: record, + payload: payload + ) + : nil + return SourceProposal( + data: data, + destination: destination, + intendedLabel: payload.label, + intendedRecordID: cronBinding?.intendedRecordID, + cronBinding: cronBinding, + intendedChecksum: Self.sourceChecksum(data, kind: record.kind), + expectedDestinationChecksum: payload.expectedDestinationChecksum + ) + case .importRecord(let payload): + guard payload.expectedKind == record.kind else { + throw ProposalError("The imported source kind does not match the selected automation.") + } + let sourceParent = sourceURL.deletingLastPathComponent().standardizedFileURL + let destinationParent = payload.destination.deletingLastPathComponent().standardizedFileURL + guard sourceParent == destinationParent else { + throw ProposalError("The import destination is outside the approved automation folder.") + } + if record.kind == .cron { + try Self.validateCronDocument(payload.data, against: record) + } + return SourceProposal( + data: payload.data, + destination: payload.destination, + intendedLabel: Self.parsedLabel( + data: payload.data, + kind: payload.expectedKind, + sourceURL: payload.destination, + ownerUID: record.ownerUID ?? 0 + ), + intendedRecordID: record.kind == .cron ? record.id : nil, + cronBinding: record.kind == .cron + ? try Self.cronImportBinding(data: payload.data, record: record) + : nil, + intendedChecksum: Self.sourceChecksum(payload.data, kind: record.kind), + expectedDestinationChecksum: payload.expectedDestinationChecksum + ) + case .restore(let id): + guard let backup = backupsByID[id], backup.authorizedRecordIDs.contains(record.id) else { + throw ProposalError("The selected recovery backup does not belong to this automation.") + } + let data = try fileSystem.read(backup.backupURL) + guard Self.checksum(data) == backup.checksum else { + throw ProposalError("The recovery backup failed its integrity check.") + } + let cronBinding = backup.kind == .cron && backup.sourceExisted + ? try Self.cronRestoreBinding(data: data, backup: backup, selectedRecord: record) + : nil + return SourceProposal( + data: data, + destination: sourceURL, + intendedLabel: Self.parsedLabel( + data: data, + kind: backup.kind, + sourceURL: sourceURL, + ownerUID: backup.ownerUID + ), + intendedRecordID: cronBinding?.intendedRecordID, + cronBinding: cronBinding, + intendedChecksum: Self.sourceChecksum(data, kind: backup.kind), + expectedDestinationChecksum: nil, + restoresSourcePresence: backup.sourceExisted + ) + case .enable where record.kind == .cron: + return try Self.cronEnabledStateProposal( + selectedSourceData, + sourceURL: sourceURL, + record: record, + isEnabled: true + ) + case .disable where record.kind == .cron, + .disableAndStop where record.kind == .cron: + return try Self.cronEnabledStateProposal( + selectedSourceData, + sourceURL: sourceURL, + record: record, + isEnabled: false + ) + case .remove where record.kind == .cron: + return try Self.cronRemovalProposal( + selectedSourceData, + sourceURL: sourceURL, + record: record + ) + case .startNow, .confirmedRunToCompletion, .stopCurrentRun, .enable, .disable, .disableAndStop, + .exportRecord, .remove: + return nil + } + } + + private static func cronEnabledStateProposal( + _ data: Data, + sourceURL: URL, + record: AutomationRecord, + isEnabled: Bool + ) throws -> SourceProposal { + guard let text = String(data: data, encoding: .utf8) else { + throw ProposalError("The complete current-user crontab is not valid UTF-8.") + } + let document = CronParser.parse(text) + let matches = document.entries.filter { cronEntry($0, matches: record) } + guard matches.count == 1, let selected = matches.first, + selected.lineNumber > 0, + selected.lineNumber <= document.originalLines.count + else { + throw ProposalError("The selected cron entry is missing or ambiguous in the displayed document generation.") + } + var lines = document.originalLines + let disabledPrefix = "# devscope-disabled: " + let trimmed = lines[selected.lineNumber - 1].trimmingCharacters(in: .whitespaces) + if isEnabled { + guard !selected.isEnabled, trimmed.hasPrefix(disabledPrefix) else { + throw ProposalError("The selected cron entry is already enabled or changed externally.") + } + lines[selected.lineNumber - 1] = String(trimmed.dropFirst(disabledPrefix.count)) + } else { + guard selected.isEnabled else { + throw ProposalError("The selected cron entry is already disabled or changed externally.") + } + lines[selected.lineNumber - 1] = disabledPrefix + trimmed + } + let intendedData = Data(lines.joined(separator: "\n").utf8) + let intended = CronParser.parse(String(decoding: intendedData, as: UTF8.self)) + let intendedMatches = intended.entries.filter { + CronParser.recordID(for: $0, ownerUID: record.ownerUID ?? 0) == record.id + && $0.command == selected.command + && $0.schedule.triggers == selected.schedule.triggers + && $0.environment == selected.environment + && $0.isEnabled == isEnabled + } + guard intended.invalidLines.isEmpty, + intendedMatches.count == 1, + let intendedEntry = intendedMatches.first + else { throw ProposalError("The rendered current-user crontab is not valid.") } + let binding = CronProposalBinding( + selectedRecordID: record.id, + intendedRecordID: CronParser.recordID(for: intendedEntry, ownerUID: record.ownerUID ?? 0), + command: intendedEntry.command, + schedule: intendedEntry.schedule, + environment: intendedEntry.environment, + enabledState: isEnabled ? .enabled : .disabled + ) + return SourceProposal( + data: intendedData, + destination: sourceURL, + intendedLabel: nil, + intendedRecordID: binding.intendedRecordID, + cronBinding: binding, + cronRemovalBinding: nil, + intendedChecksum: sourceChecksum(intendedData, kind: .cron), + expectedDestinationChecksum: nil + ) + } + + private static func cronRemovalProposal( + _ data: Data, + sourceURL: URL, + record: AutomationRecord + ) throws -> SourceProposal { + guard let text = String(data: data, encoding: .utf8) else { + throw ProposalError("The complete current-user crontab is not valid UTF-8.") + } + let document = CronParser.parse(text) + let indexedMatches = document.entries.enumerated().filter { cronEntry($0.element, matches: record) } + guard indexedMatches.count == 1, let selected = indexedMatches.first, + selected.element.lineNumber > 0, + selected.element.lineNumber <= document.originalLines.count + else { + throw ProposalError("The selected cron entry is missing or ambiguous in the displayed document generation.") + } + let fingerprint = cronFingerprint(selected.element) + var lines = document.originalLines + lines.remove(at: selected.element.lineNumber - 1) + let intendedData = Data(lines.joined(separator: "\n").utf8) + let intended = CronParser.parse(String(decoding: intendedData, as: UTF8.self)) + guard intended.invalidLines.isEmpty else { + throw ProposalError("The rendered current-user crontab is not valid.") + } + let binding = CronRemovalBinding( + command: fingerprint.command, + schedule: selected.element.schedule, + environment: selected.element.environment, + enabledState: selected.element.isEnabled ? .enabled : .disabled, + remainingMatchingCount: intended.entries.filter { cronFingerprint($0) == fingerprint }.count + ) + return SourceProposal( + data: intendedData, + destination: sourceURL, + intendedLabel: nil, + intendedRecordID: nil, + cronBinding: nil, + cronRemovalBinding: binding, + intendedChecksum: sourceChecksum(intendedData, kind: .cron), + expectedDestinationChecksum: nil + ) + } + + private static func cronEntry(_ entry: CronEntry, matches record: AutomationRecord) -> Bool { + CronParser.recordID(for: entry, ownerUID: record.ownerUID ?? 0) == record.id + && entry.command == record.commandSignature + && entry.schedule.triggers == record.schedule.triggers + && entry.environment == record.environment + && (entry.isEnabled ? AutomationEnabledState.enabled : .disabled) == record.enabledState + } + + private static func validate(_ payload: AutomationEditPayload) throws { + guard !payload.label.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { + throw ProposalError("An automation label is required.") + } + guard payload.executable.hasPrefix("/"), !payload.executable.contains("\0") else { + throw ProposalError("The automation executable must be an absolute path.") + } + guard payload.arguments.allSatisfy({ !$0.contains("\0") }), + payload.environment.allSatisfy({ key, value in + !key.isEmpty && !key.contains("=") && !key.contains("\0") && !value.contains("\0") + }) + else { + throw ProposalError("The automation arguments or environment contain invalid values.") + } + } + + private static func sourceData( + for payload: AutomationEditPayload, + kind: AutomationKind + ) throws -> Data { + if let rawRepresentation = payload.rawRepresentation { + return rawRepresentation + } + guard kind != .cron else { + throw ProposalError("Cron edits require a complete rendered crontab document.") + } + var propertyList: [String: Any] = [ + "Label": payload.label, + "ProgramArguments": [payload.executable] + payload.arguments, + ] + for trigger in payload.schedule.triggers { + switch trigger { + case .atLogin, .runAtLoad: + propertyList["RunAtLoad"] = true + case .interval(let seconds): + guard propertyList["StartInterval"] == nil, seconds > 0 else { + throw ProposalError("Generated launchd sources support one positive interval.") + } + propertyList["StartInterval"] = seconds + case .keepAlive: + propertyList["KeepAlive"] = true + case .demand: + break + case .calendar, .cron: + throw ProposalError("This schedule requires a validated raw source representation.") + } + } + if !payload.environment.isEmpty { + propertyList["EnvironmentVariables"] = payload.environment + } + if let workingDirectory = payload.workingDirectory { + propertyList["WorkingDirectory"] = workingDirectory + } + return try PropertyListSerialization.data( + fromPropertyList: propertyList, + format: .xml, + options: 0 + ) + } + + private static func validate( + data: Data, + against payload: AutomationEditPayload, + kind: AutomationKind, + sourceURL: URL, + ownerUID: uid_t + ) throws { + guard kind != .cron else { + let document = CronParser.parse(String(decoding: data, as: UTF8.self)) + guard document.invalidLines.isEmpty else { + throw ProposalError("The rendered crontab contains invalid lines.") + } + let reviewedCommand = ([payload.executable] + payload.arguments).joined(separator: " ") + guard document.entries.contains(where: { + $0.command == reviewedCommand + && $0.schedule.triggers == payload.schedule.triggers + && $0.environment == payload.environment + }) else { + throw ProposalError("The rendered crontab does not match the reviewed command, schedule, and environment.") + } + return + } + let parsed: AutomationRecord + do { + parsed = try LaunchdPlistParser.parse( + data: data, + sourceURL: sourceURL, + ownerUID: ownerUID, + ownership: .user + ) + } catch { + throw ProposalError("The launchd property list is not semantically valid.") + } + guard parsed.label == payload.label, + parsed.executable == payload.executable, + parsed.arguments == payload.arguments, + parsed.schedule.triggers == payload.schedule.triggers + else { + throw ProposalError("The raw source does not match the reviewed automation fields.") + } + guard let propertyList = try? PropertyListSerialization.propertyList( + from: data, + options: [], + format: nil + ) as? [String: Any] + else { + throw ProposalError("The launchd property list is not semantically valid.") + } + let environment = propertyList["EnvironmentVariables"] as? [String: String] ?? [:] + let workingDirectory = propertyList["WorkingDirectory"] as? String + guard environment == payload.environment, + workingDirectory == payload.workingDirectory + else { + throw ProposalError("The raw source does not match the reviewed environment or directory.") + } + } + + private static func validateCronDocument( + _ data: Data, + against record: AutomationRecord + ) throws { + let document = CronParser.parse(String(decoding: data, as: UTF8.self)) + guard document.invalidLines.isEmpty, + document.entries.contains(where: { + CronParser.recordID(for: $0, ownerUID: record.ownerUID ?? 0) == record.id + && $0.command == record.commandSignature + && $0.schedule.triggers == record.schedule.triggers + && $0.environment == record.environment + }) + else { + throw ProposalError("The imported crontab does not contain the reviewed target entry.") + } + } + + private static func cronProposalBinding( + originalData: Data, + intendedData: Data, + record: AutomationRecord, + payload: AutomationEditPayload + ) throws -> CronProposalBinding { + let ownerUID = record.ownerUID ?? 0 + let original = CronParser.parse(String(decoding: originalData, as: UTF8.self)) + let selected = original.entries.enumerated().filter { + CronParser.recordID(for: $0.element, ownerUID: ownerUID) == record.id + && $0.element.command == record.commandSignature + && $0.element.schedule.triggers == record.schedule.triggers + && $0.element.environment == record.environment + && ($0.element.isEnabled ? AutomationEnabledState.enabled : .disabled) == record.enabledState + } + guard selected.count == 1, let selectedMatch = selected.first else { + throw ProposalError("The selected cron entry is missing or ambiguous in the displayed document generation.") + } + + let command = ([payload.executable] + payload.arguments).joined(separator: " ") + let intendedDocument = CronParser.parse(String(decoding: intendedData, as: UTF8.self)) + let intended = intendedDocument.entries.enumerated().filter { + $0.element.command == command + && $0.element.schedule.triggers == payload.schedule.triggers + && $0.element.environment == payload.environment + } + guard intended.count == 1, let intendedMatch = intended.first else { + throw ProposalError("The intended cron entry is missing or ambiguous in the rendered document.") + } + var originalCounts = cronEntryCounts(original.entries) + var intendedCounts = cronEntryCounts(intendedDocument.entries) + guard removeOne(cronFingerprint(selectedMatch.element), from: &originalCounts), + removeOne(cronFingerprint(intendedMatch.element), from: &intendedCounts), + originalCounts == intendedCounts + else { + throw ProposalError( + "A cron edit must replace exactly the selected entry and preserve unrelated entries." + ) + } + guard selectedMatch.offset == intendedMatch.offset else { + throw ProposalError("The intended cron entry does not replace the selected occurrence.") + } + var originalRemainder = original.entries.map(cronFingerprint) + var intendedRemainder = intendedDocument.entries.map(cronFingerprint) + originalRemainder.remove(at: selectedMatch.offset) + intendedRemainder.remove(at: intendedMatch.offset) + guard originalRemainder == intendedRemainder + else { + throw ProposalError( + "A cron edit must replace exactly the selected entry and preserve unrelated entries." + ) + } + let entry = intendedMatch.element + return CronProposalBinding( + selectedRecordID: record.id, + intendedRecordID: CronParser.recordID(for: entry, ownerUID: ownerUID), + command: entry.command, + schedule: entry.schedule, + environment: entry.environment, + enabledState: entry.isEnabled ? .enabled : .disabled + ) + } + + private static func cronDuplicateBinding( + originalData: Data, + intendedData: Data, + record: AutomationRecord, + payload: AutomationEditPayload + ) throws -> CronProposalBinding { + let ownerUID = record.ownerUID ?? 0 + let original = CronParser.parse(String(decoding: originalData, as: UTF8.self)) + let intended = CronParser.parse(String(decoding: intendedData, as: UTF8.self)) + let selected = original.entries.filter { + CronParser.recordID(for: $0, ownerUID: ownerUID) == record.id + && $0.command == record.commandSignature + && $0.schedule.triggers == record.schedule.triggers + && $0.environment == record.environment + && ($0.isEnabled ? AutomationEnabledState.enabled : .disabled) == record.enabledState + } + guard selected.count == 1 else { + throw ProposalError("The selected cron entry is missing or ambiguous in the displayed document generation.") + } + + let before = cronEntryCounts(original.entries) + let after = cronEntryCounts(intended.entries) + let removedCount = before.reduce(into: 0) { total, pair in + total += max(0, pair.value - after[pair.key, default: 0]) + } + let added = after.flatMap { pair in + Array(repeating: pair.key, count: max(0, pair.value - before[pair.key, default: 0])) + } + let reviewedCommand = ([payload.executable] + payload.arguments).joined(separator: " ") + guard removedCount == 0, + added.count == 1, + let addedFingerprint = added.first, + addedFingerprint.command == reviewedCommand, + addedFingerprint.scheduleExpression == cronExpression(from: payload.schedule), + addedFingerprint.environment == cronEnvironmentFingerprint(payload.environment) + else { + throw ProposalError("A cron duplicate must add exactly one reviewed entry and preserve the document.") + } + + let originalIDs = Set(original.entries.map { + CronParser.recordID(for: $0, ownerUID: ownerUID) + }) + let candidates = intended.entries.filter { + cronFingerprint($0) == addedFingerprint + && !originalIDs.contains(CronParser.recordID(for: $0, ownerUID: ownerUID)) + } + guard candidates.count == 1, + let entry = candidates.first, + CronParser.recordID(for: entry, ownerUID: ownerUID) != record.id + else { + throw ProposalError("The added cron entry identity is not distinct.") + } + return CronProposalBinding( + selectedRecordID: record.id, + intendedRecordID: CronParser.recordID(for: entry, ownerUID: ownerUID), + command: entry.command, + schedule: entry.schedule, + environment: entry.environment, + enabledState: entry.isEnabled ? .enabled : .disabled + ) + } + + private static func cronRestoreBinding( + data: Data, + backup: AutomationBackup, + selectedRecord: AutomationRecord + ) throws -> CronProposalBinding { + let matches = CronParser.parse(String(decoding: data, as: UTF8.self)).entries.filter { + CronParser.recordID(for: $0, ownerUID: backup.ownerUID) == backup.recordID + } + guard matches.count == 1, let entry = matches.first else { + throw ProposalError("The recovery crontab does not contain one exact recovery-lineage entry.") + } + return CronProposalBinding( + selectedRecordID: selectedRecord.id, + intendedRecordID: backup.recordID, + command: entry.command, + schedule: entry.schedule, + environment: entry.environment, + enabledState: entry.isEnabled ? .enabled : .disabled + ) + } + + private static func cronEntryCounts( + _ entries: [CronEntry] + ) -> [CronEntryFingerprint: Int] { + entries.reduce(into: [:]) { counts, entry in + counts[cronFingerprint(entry), default: 0] += 1 + } + } + + private static func removeOne( + _ fingerprint: CronEntryFingerprint, + from counts: inout [CronEntryFingerprint: Int] + ) -> Bool { + guard let count = counts[fingerprint], count > 0 else { return false } + if count == 1 { + counts.removeValue(forKey: fingerprint) + } else { + counts[fingerprint] = count - 1 + } + return true + } + + private static func cronFingerprint(_ entry: CronEntry) -> CronEntryFingerprint { + CronEntryFingerprint( + scheduleExpression: entry.scheduleExpression, + command: entry.command, + environment: cronEnvironmentFingerprint(entry.environment), + isEnabled: entry.isEnabled + ) + } + + private static func cronEnvironmentFingerprint( + _ environment: [String: String] + ) -> [String] { + environment.keys.sorted().map { key in + key + "\u{0}" + (environment[key] ?? "") + } + } + + private static func cronExpression(from schedule: AutomationSchedule) -> String? { + guard schedule.triggers.count == 1, + case .cron(let expression) = schedule.triggers[0] + else { return nil } + return expression + } + + private static func cronImportBinding( + data: Data, + record: AutomationRecord + ) throws -> CronProposalBinding { + let ownerUID = record.ownerUID ?? 0 + let matches = CronParser.parse(String(decoding: data, as: UTF8.self)).entries.filter { + CronParser.recordID(for: $0, ownerUID: ownerUID) == record.id + && $0.command == record.commandSignature + && $0.schedule.triggers == record.schedule.triggers + && $0.environment == record.environment + && ($0.isEnabled ? AutomationEnabledState.enabled : .disabled) == record.enabledState + } + guard matches.count == 1, let entry = matches.first else { + throw ProposalError("The imported crontab does not contain one exact reviewed target entry.") + } + return CronProposalBinding( + selectedRecordID: record.id, + intendedRecordID: record.id, + command: entry.command, + schedule: entry.schedule, + environment: entry.environment, + enabledState: entry.isEnabled ? .enabled : .disabled + ) + } + + private static func parsedLabel( + data: Data, + kind: AutomationKind, + sourceURL: URL, + ownerUID: uid_t + ) -> String? { + guard kind != .cron else { return nil } + return try? LaunchdPlistParser.parse( + data: data, + sourceURL: sourceURL, + ownerUID: ownerUID, + ownership: .user + ).label + } + + private static func isValidSource( + _ data: Data, + kind: AutomationKind, + expectedLabel: String?, + sourceURL: URL, + ownerUID: uid_t + ) -> Bool { + if kind == .cron { + guard let text = String(data: data, encoding: .utf8) else { return false } + return CronParser.parse(text).invalidLines.isEmpty + } + guard let parsed = try? LaunchdPlistParser.parse( + data: data, + sourceURL: sourceURL, + ownerUID: ownerUID, + ownership: .user + ) else { return false } + return expectedLabel == nil || parsed.label == expectedLabel + } + + private func verify( + _ operation: AutomationOperation, + record: AutomationRecord, + executorResult: AutomationExecutorResult, + snapshot: AutomationInventorySnapshot, + proposal: SourceProposal?, + transactionSourceURL: URL, + controlledIdentities: Set, + liveProcesses: [DevProcess] + ) -> (satisfied: Bool, evidence: [String], verifiedRecordID: AutomationRecord.ID?) { + let requiredPostconditions: Set + if case .restore = operation, proposal?.restoresSourcePresence == false { + requiredPostconditions = [.sourceRemoved] + } else { + requiredPostconditions = operation.requiredPostconditions + } + guard executorResult.postconditions.isSuperset(of: requiredPostconditions) else { + return (false, ["The executor did not prove every required postcondition."], nil) + } + let refreshedRecord = snapshot.records.first { $0.id == record.id } + switch operation { + case .disable, .disableAndStop: + if record.sourceKind == .legacyLoginItem { + guard snapshot.health[.legacyLoginItem]?.state == .healthy else { + return (false, ["Authoritative legacy login-item health is not healthy."], nil) + } + guard !snapshot.records.contains(where: { Self.sameLegacyLoginItem($0, record) }) else { + return (false, ["The selected legacy login item is still present."], nil) + } + return (true, ["Refreshed login items confirm the selected path is absent."], nil) + } + guard refreshedRecord?.enabledState == .disabled else { + return (false, ["Refreshed inventory still reports future launches as enabled or unknown."], nil) + } + if case .disableAndStop = operation, + !runtimeStopIsVerified( + record: record, + snapshot: snapshot, + controlledIdentities: controlledIdentities, + liveProcesses: liveProcesses + ) { + return (false, ["Refreshed process truth still contains a controlled or relaunched process."], nil) + } + if record.kind == .cron { + guard let proposal, + executorResult.postconditions.contains(.sourceChecksum(proposal.intendedChecksum)), + refreshedRecord?.sourceChecksum == proposal.intendedChecksum, + fileSystem.itemExists(at: transactionSourceURL), + (try? Self.sourceChecksum(fileSystem.read(transactionSourceURL), kind: record.kind)) == proposal.intendedChecksum + else { + return (false, ["The refreshed current-user crontab does not match the complete disabled document."], nil) + } + } + return (true, ["Refreshed inventory confirms future launches are disabled."], nil) + case .enable: + guard refreshedRecord?.enabledState == .enabled else { + return (false, ["Refreshed inventory does not report future launches as enabled."], nil) + } + if record.kind == .cron { + guard let proposal, + executorResult.postconditions.contains(.sourceChecksum(proposal.intendedChecksum)), + refreshedRecord?.sourceChecksum == proposal.intendedChecksum, + fileSystem.itemExists(at: transactionSourceURL), + (try? Self.sourceChecksum(fileSystem.read(transactionSourceURL), kind: record.kind)) == proposal.intendedChecksum + else { + return (false, ["The refreshed current-user crontab does not match the complete enabled document."], nil) + } + } + return (true, ["Refreshed inventory confirms future launches are enabled."], nil) + case .confirmedRunToCompletion: + guard record.kind == .cron else { + return (false, ["Run-to-completion confirmation is only supported for cron records."], nil) + } + return (true, ["The explicitly confirmed cron command completed successfully."], nil) + case .startNow: + guard let refreshedRecord else { + return (false, ["Refreshed inventory no longer contains the requested automation."], nil) + } + let hasStrongFreshLink = AutomationProcessCorrelator.links( + records: [refreshedRecord], + processes: liveProcesses, + now: now() + ).contains { link in + link.recordID == refreshedRecord.id + && link.strength == .strong + && link.processIdentity.birthToken != nil + } + guard hasStrongFreshLink else { + return (false, ["Fresh process truth does not contain a strong birth-identified automation link."], nil) + } + return ( + true, + ["Fresh process truth confirms a strong link to the requested automation."], + refreshedRecord.id + ) + case .remove: + if record.kind == .cron { + guard let proposal, + let binding = proposal.cronRemovalBinding, + executorResult.postconditions.contains(.sourceChecksum(proposal.intendedChecksum)), + fileSystem.itemExists(at: transactionSourceURL), + (try? Self.sourceChecksum(fileSystem.read(transactionSourceURL), kind: record.kind)) == proposal.intendedChecksum + else { + return (false, ["The remaining current-user crontab document could not be verified."], nil) + } + let matchingRecords = snapshot.records.filter { + $0.kind == .cron + && $0.sourceKind == .crontab + && $0.ownerUID == record.ownerUID + && $0.commandSignature == binding.command + && $0.schedule.triggers == binding.schedule.triggers + && $0.environment == binding.environment + && $0.enabledState == binding.enabledState + && $0.sourceChecksum == proposal.intendedChecksum + } + guard matchingRecords.count == binding.remainingMatchingCount, + snapshot.records.filter({ + $0.kind == .cron && $0.sourceKind == .crontab && $0.ownerUID == record.ownerUID + }).allSatisfy({ $0.sourceChecksum == proposal.intendedChecksum }) + else { + return (false, ["The selected cron occurrence is still present or the remaining document changed."], nil) + } + return (true, ["Refreshed current-user crontab confirms the selected occurrence was removed."], nil) + } + if record.sourceKind == .legacyLoginItem { + guard snapshot.health[.legacyLoginItem]?.state == .healthy else { + return (false, ["Authoritative legacy login-item health is not healthy."], nil) + } + guard !snapshot.records.contains(where: { Self.sameLegacyLoginItem($0, record) }), + fileSystem.itemExists(at: transactionSourceURL) + else { + return (false, ["The selected legacy login item is still present or recovery evidence disappeared."], nil) + } + return (true, ["Refreshed login items confirm only the selected path was removed."], nil) + } + if record.sourceKind == .launchAgent, + !executorResult.postconditions.contains(.targetUnresolved) { + return (false, ["launchd still resolves the removed service target."], nil) + } + let canonicalTransactionPath = transactionSourceURL.standardizedFileURL.path + let recreatedAtTransactionIdentity = snapshot.records.contains { candidate in + if let candidateURL = candidate.sourceURL { + return candidateURL.standardizedFileURL.path == canonicalTransactionPath + } + return record.sourceKind.requiresRecoverableSource + && candidate.sourceKind == record.sourceKind + && candidate.ownerUID == record.ownerUID + } + guard refreshedRecord == nil, + !fileSystem.itemExists(at: transactionSourceURL), + !recreatedAtTransactionIdentity + else { + return (false, ["The removed source or transaction identity still exists."], nil) + } + return (true, ["Refreshed inventory confirms the source was removed."], nil) + case .edit, .duplicate, .importRecord, .restore: + if case .restore = operation, proposal?.restoresSourcePresence == false { + let stillPresent = fileSystem.itemExists(at: transactionSourceURL) + || snapshot.records.contains { candidate in + candidate.sourceURL?.standardizedFileURL.path == transactionSourceURL.standardizedFileURL.path + } + return stillPresent + ? (false, ["Refreshed inventory still contains the source restored to absence."], nil) + : (true, ["Refreshed inventory confirms the prior absent source state."], nil) + } + guard let proposal, + executorResult.postconditions.contains(.sourceChecksum(proposal.intendedChecksum)) + else { + return (false, ["The executor did not prove the intended source checksum."], nil) + } + let installed = snapshot.records.first { candidate in + if record.kind == .cron { + guard let binding = proposal.cronBinding else { return false } + return binding.selectedRecordID == record.id + && candidate.id == binding.intendedRecordID + && candidate.kind == .cron + && candidate.sourceKind == .crontab + && candidate.ownerUID == record.ownerUID + && candidate.commandSignature == binding.command + && candidate.schedule.triggers == binding.schedule.triggers + && candidate.environment == binding.environment + && candidate.enabledState == binding.enabledState + && candidate.sourceChecksum == proposal.intendedChecksum + } + let destinationMatches = record.sourceKind.requiresRecoverableSource + ? candidate.sourceKind == record.sourceKind + : candidate.sourceURL?.standardizedFileURL.path == proposal.destination.standardizedFileURL.path + return destinationMatches + && candidate.label == proposal.intendedLabel + && candidate.sourceChecksum == proposal.intendedChecksum + } + guard let installed else { + return (false, ["Refreshed inventory does not contain the intended destination, label, and checksum."], nil) + } + if case .duplicate = operation, record.sourceKind == .launchAgent { + guard executorResult.postconditions.contains(.futureLaunchesDisabled), + installed.enabledState == .disabled, + installed.loadState == .unloaded + else { + return ( + false, + ["The duplicated LaunchAgent is not verified disabled and unloaded."], + nil + ) + } + } + return (true, ["Refreshed inventory confirms the intended destination, label, and checksum."], installed.id) + case .stopCurrentRun: + guard runtimeStopIsVerified( + record: record, + snapshot: snapshot, + controlledIdentities: controlledIdentities, + liveProcesses: liveProcesses + ) else { + return (false, ["Refreshed process truth still contains a controlled or relaunched process."], nil) + } + return (true, ["Refreshed process truth confirms no controlled process remains."], nil) + case .exportRecord: + return (true, ["The executor resolved the export target."], nil) + } + } + + private func runtimeStopIsVerified( + record: AutomationRecord, + snapshot: AutomationInventorySnapshot, + controlledIdentities: Set, + liveProcesses: [DevProcess] + ) -> Bool { + guard !liveProcesses.contains(where: { + $0.birthToken == nil && Self.isStrongBirthlessCandidate($0, for: record) + }) else { return false } + let liveIdentities = Set(liveProcesses.compactMap { process -> AutomationLinkedProcessIdentity? in + guard let birth = process.birthToken else { return nil } + return AutomationLinkedProcessIdentity(processID: process.pid, birthToken: birth) + }) + guard controlledIdentities.isDisjoint(with: liveIdentities) else { return false } + let candidateIDs = Set(snapshot.records.filter { + $0.id == record.id + || ($0.sourceKind == record.sourceKind && $0.ownerUID == record.ownerUID && $0.label == record.label) + }.map(\.id)) + return AutomationProcessCorrelator.links( + records: snapshot.records, + processes: liveProcesses, + now: now() + ).allSatisfy { !candidateIDs.contains($0.recordID) } + } + + private static func isStrongBirthlessCandidate( + _ process: DevProcess, + for record: AutomationRecord + ) -> Bool { + if record.kind == .cron { + guard let signature = record.commandSignature, + let argv = process.argumentVector, + argv.count == 3, + argv[1] == "-c" + else { return false } + return argv[2] == signature + } + let exactLabel = process.launchLabel == record.label + let exactExecutableAndArguments = record.executable.map { + URL(fileURLWithPath: process.executable).standardizedFileURL.path + == URL(fileURLWithPath: $0).standardizedFileURL.path + && process.argumentVector == [$0] + record.arguments + } ?? false + return exactLabel || exactExecutableAndArguments + } + + private func failureAfterMutation( + operation: AutomationOperation, + record: AutomationRecord, + linkedProcesses: [ClassifiedDevProcess], + backup: AutomationBackup, + preTransactionState: AutomationPreTransactionState, + proposal: SourceProposal?, + originalSourceMetadata: AutomationFileMetadata, + postMutationResourceIdentifier: String?, + sourceMutationStep: String?, + phase: TransactionFailurePhase, + evidence: [String], + expectedAppliedState: AutomationExecutorAppliedState?, + fileMutationEvidence: AutomationFilePartialMutation? = nil + ) async -> AutomationOperationResult { + var appliedSteps = ["Created owner-only recovery backup."] + if let sourceMutationStep { + appliedSteps.append(sourceMutationStep) + } + if phase == .verificationFailed { + appliedSteps.append("Applied typed automation operation.") + } + + do { + guard try rollbackTargetIsUnchanged( + operation: operation, + proposal: proposal, + backup: backup, + sourceMutationStep: sourceMutationStep, + originalSourceMetadata: originalSourceMetadata, + postMutationResourceIdentifier: postMutationResourceIdentifier + ) else { + throw RollbackError.sourceChangedExternally + } + guard try writeTargetIsAuthorized( + record: record, + url: backup.sourceURL, + expectedResourceIdentifier: postMutationResourceIdentifier, + expectedParentResourceIdentifier: backup.parentResourceIdentifier, + requiredCapability: .restore + ) else { + throw RollbackError.sourceChangedExternally + } + let backupData = try fileSystem.read(backup.backupURL) + guard Self.checksum(backupData) == backup.checksum else { + throw RollbackError.integrityMismatch + } + let verifiedRecovery = AutomationVerifiedRecoveryInput( + backupID: backup.id, + backupURL: backup.backupURL, + data: backupData, + checksum: backup.checksum + ) + if backup.sourceExisted { + let parentAuthorization = try Self.directoryAuthorization( + for: backup.sourceURL.deletingLastPathComponent(), + resourceIdentifier: backup.parentResourceIdentifier + ) + let staged = try fileSystem.writeStagedFile( + nextTo: backup.sourceURL, + data: verifiedRecovery.data, + permissions: 0o600, + authorization: parentAuthorization + ) + guard try writeTargetIsAuthorized( + record: record, + url: backup.sourceURL, + expectedResourceIdentifier: postMutationResourceIdentifier, + expectedParentResourceIdentifier: backup.parentResourceIdentifier, + requiredCapability: .restore + ) else { + try removeCleanupArtifact(staged.authorization) + throw RollbackError.sourceChangedExternally + } + let destinationExists = fileSystem.itemExists(at: backup.sourceURL) + let destinationResourceIdentifier = destinationExists + ? (postMutationResourceIdentifier ?? originalSourceMetadata.resourceIdentifier) + : nil + let destinationAuthorization = try Self.fileAuthorization( + for: backup.sourceURL, + directory: parentAuthorization, + resourceIdentifier: destinationResourceIdentifier, + existed: destinationExists + ) + _ = try committedReplacement(at: destinationAuthorization, with: staged) + } else if fileSystem.itemExists(at: backup.sourceURL) { + guard try writeTargetIsAuthorized( + record: record, + url: backup.sourceURL, + expectedResourceIdentifier: postMutationResourceIdentifier, + expectedParentResourceIdentifier: backup.parentResourceIdentifier, + requiredCapability: .restore + ) else { + throw RollbackError.sourceChangedExternally + } + let parentAuthorization = try Self.directoryAuthorization( + for: backup.sourceURL.deletingLastPathComponent(), + resourceIdentifier: backup.parentResourceIdentifier + ) + let sourceAuthorization = try Self.fileAuthorization( + for: backup.sourceURL, + directory: parentAuthorization, + resourceIdentifier: postMutationResourceIdentifier, + existed: true + ) + _ = try Self.committedReceipt(fileSystem.removeItem(sourceAuthorization)) + } + appliedSteps.append("Restored source from recovery backup.") + + if !preTransactionState.sourceExisted { + let recoveredSnapshot = await refresh() + let sourcePath = backup.sourceURL.standardizedFileURL.path + guard !fileSystem.itemExists(at: backup.sourceURL), + !recoveredSnapshot.records.contains(where: { + $0.sourceURL?.standardizedFileURL.path == sourcePath + }) + else { + throw RollbackError.priorStateUnverified + } + appliedSteps.append("Refreshed and verified the prior absent source state.") + return AutomationOperationResult( + operation: operation.redactedForResult, + status: Self.statusAfterRecoveredMutation( + fileMutationEvidence, + otherwise: "The automation operation failed; the prior absent source state was restored." + ), + appliedSteps: appliedSteps, + verificationEvidence: evidence, + rollback: .restored(backup.id), + manualRecovery: nil, + backup: backup.redactedForResult, + fileMutationEvidence: fileMutationEvidence + ) + } + + if phase == .sourceMutationFailed { + let recoveredSnapshot = await refresh() + guard let recoveredRecord = recoveredSnapshot.records.first(where: { $0.id == record.id }), + recoveredRecord.enabledState == preTransactionState.enabledState, + recoveredRecord.loadState == preTransactionState.loadState, + fileSystem.itemExists(at: backup.sourceURL), + Self.checksum(try fileSystem.read(backup.sourceURL)) == backup.checksum + else { throw RollbackError.priorStateUnverified } + appliedSteps.append( + record.kind == .cron + ? "Verified live cron state was never mutated." + : "Verified the prior source and loaded state remain intact." + ) + return AutomationOperationResult( + operation: operation.redactedForResult, + status: Self.statusAfterRecoveredMutation( + fileMutationEvidence, + otherwise: record.kind == .cron + ? "The automation operation failed before the live crontab was changed." + : "The automation operation failed; the prior source and loaded state were restored." + ), + appliedSteps: appliedSteps, + verificationEvidence: evidence, + rollback: .restored(backup.id), + manualRecovery: nil, + backup: backup.redactedForResult, + fileMutationEvidence: fileMutationEvidence + ) + } + + let priorStateResult = try await executor.restorePreTransactionState( + preTransactionState, + for: record, + recovery: verifiedRecovery, + linkedProcesses: linkedProcesses, + expectedAppliedState: expectedAppliedState + ) + guard priorStateResult.postconditions.contains( + .preTransactionStateRestored(preTransactionState) + ) else { + throw RollbackError.priorStateUnverified + } + appliedSteps.append("Re-applied the exact pre-transaction state.") + + let recoveredSnapshot = await refresh() + let recoveredProcesses = preTransactionState.linkedProcesses.isEmpty + ? [] + : try await refreshProcesses() + guard let recoveredRecord = recoveredSnapshot.records.first(where: { $0.id == record.id }), + recoveredRecord.enabledState == preTransactionState.enabledState, + recoveredRecord.loadState == preTransactionState.loadState + else { + throw RollbackError.priorStateUnverified + } + if !preTransactionState.linkedProcesses.isEmpty { + let links = AutomationProcessCorrelator.links( + records: recoveredSnapshot.records, + processes: recoveredProcesses, + now: now() + ).filter { $0.recordID == recoveredRecord.id } + let restoredIdentities = Set(links.compactMap { link -> AutomationLinkedProcessIdentity? in + guard let birth = link.processIdentity.birthToken else { return nil } + return AutomationLinkedProcessIdentity( + processID: link.processIdentity.pid, + birthToken: birth + ) + }) + guard restoredIdentities.count >= preTransactionState.linkedProcesses.count else { + throw RollbackError.priorStateUnverified + } + } + if backup.sourceExisted { + guard fileSystem.itemExists(at: backup.sourceURL), + Self.checksum(try fileSystem.read(backup.sourceURL)) == backup.checksum + else { + throw RollbackError.priorStateUnverified + } + } else { + guard !fileSystem.itemExists(at: backup.sourceURL) else { + throw RollbackError.priorStateUnverified + } + } + appliedSteps.append("Refreshed and verified exact recovery state.") + + return AutomationOperationResult( + operation: operation.redactedForResult, + status: Self.statusAfterRecoveredMutation( + fileMutationEvidence, + otherwise: "The automation operation failed; the prior source and loaded state were restored." + ), + appliedSteps: appliedSteps, + verificationEvidence: evidence, + rollback: .restored(backup.id), + manualRecovery: nil, + backup: backup.redactedForResult, + fileMutationEvidence: fileMutationEvidence + ) + } catch let rollbackPartial as AutomationFilePartialMutation { + _ = await refresh() + return AutomationOperationResult( + operation: operation.redactedForResult, + status: .partialFailure( + "HIGH SEVERITY: the operation failed and automatic rollback reached a partial filesystem state." + ), + appliedSteps: appliedSteps, + verificationEvidence: evidence, + rollback: .failed("Automatic rollback did not complete."), + manualRecovery: "Preserve the authorized filesystem recovery handles and use DevScope Restore before retrying.", + backup: backup.redactedForResult, + fileMutationEvidence: fileMutationEvidence ?? rollbackPartial, + rollbackFileMutationEvidence: fileMutationEvidence == nil ? nil : rollbackPartial + ) + } catch { + _ = await refresh() + return AutomationOperationResult( + operation: operation.redactedForResult, + status: .partialFailure( + "HIGH SEVERITY: the operation failed and automatic rollback did not complete." + ), + appliedSteps: appliedSteps, + verificationEvidence: evidence, + rollback: .failed("Automatic rollback did not complete."), + manualRecovery: "Use DevScope Restore with the recorded recovery identifier before retrying.", + backup: backup.redactedForResult, + fileMutationEvidence: fileMutationEvidence + ) + } + } + + private static func statusAfterRecoveredMutation( + _ partial: AutomationFilePartialMutation?, + otherwise message: String + ) -> AutomationOperationStatus { + if partial != nil { + return .partialFailure( + "HIGH SEVERITY: the filesystem reported a partial mutation; automatic rollback restored the prior state." + ) + } + return .failed(message) + } + + private func rollbackTargetIsUnchanged( + operation: AutomationOperation, + proposal: SourceProposal?, + backup: AutomationBackup, + sourceMutationStep: String?, + originalSourceMetadata: AutomationFileMetadata, + postMutationResourceIdentifier: String? + ) throws -> Bool { + if case .remove = operation, backup.sourceKind == .legacyLoginItem { + guard fileSystem.itemExists(at: backup.sourceURL) else { return false } + let data = try fileSystem.read(backup.sourceURL) + let metadata = try fileSystem.metadata(for: backup.sourceURL) + return Self.checksum(data) == backup.checksum + && metadata.resourceIdentifier == originalSourceMetadata.resourceIdentifier + } + if case .remove = operation { + return !fileSystem.itemExists(at: backup.sourceURL) + } + if proposal?.restoresSourcePresence == false, sourceMutationStep != nil { + return !fileSystem.itemExists(at: backup.sourceURL) + } + guard fileSystem.itemExists(at: backup.sourceURL) else { return false } + let data = try fileSystem.read(backup.sourceURL) + let metadata = try fileSystem.metadata(for: backup.sourceURL) + guard let resourceIdentifier = metadata.resourceIdentifier else { return false } + if sourceMutationStep != nil, let proposal { + return Self.sourceChecksum(data, kind: backup.kind) == proposal.intendedChecksum + && resourceIdentifier == postMutationResourceIdentifier + } + return Self.checksum(data) == backup.checksum + && resourceIdentifier == originalSourceMetadata.resourceIdentifier + } + + private func writeTargetIsAuthorized( + record: AutomationRecord, + url: URL, + expectedResourceIdentifier: String?, + expectedParentResourceIdentifier: String?, + requiredCapability: AutomationCapability + ) throws -> Bool { + let destination = url.standardizedFileURL + let context = try destinationContext(record, destination) + let decision = AutomationCapabilityPolicy.decision(for: record, context: context) + guard decision.capabilities.contains(requiredCapability), + context.canonicalPathIsApproved, + context.sourceOwnerUID == context.currentUID, + !context.isManaged, + !context.isSymlink + else { return false } + let parent = destination.deletingLastPathComponent().standardizedFileURL + let parentMetadata = try fileSystem.metadata(for: parent) + guard parentMetadata.canonicalURL.standardizedFileURL.path == parent.path, + parentMetadata.ownerUID == context.currentUID, + !parentMetadata.isSymbolicLink, + parentMetadata.resourceIdentifier?.isEmpty == false, + expectedParentResourceIdentifier == nil + || parentMetadata.resourceIdentifier == expectedParentResourceIdentifier + else { return false } + if fileSystem.itemExists(at: destination) { + let metadata = try fileSystem.metadata(for: destination) + guard metadata.canonicalURL.standardizedFileURL.path == destination.path, + metadata.ownerUID == context.currentUID, + !metadata.isSymbolicLink, + let identity = metadata.resourceIdentifier, + !identity.isEmpty + else { return false } + return expectedResourceIdentifier == nil || identity == expectedResourceIdentifier + } + return expectedResourceIdentifier == nil + } + + private func rejected( + _ operation: AutomationOperation, + _ message: String + ) -> AutomationOperationResult { + AutomationOperationResult( + operation: operation.redactedForResult, + status: .rejected(message), + appliedSteps: [], + verificationEvidence: [], + rollback: .notNeeded, + manualRecovery: nil + ) + } + + private func failed( + _ operation: AutomationOperation, + _ message: String, + appliedSteps: [String] = [], + evidence: [String] = [], + manualRecovery: String? = nil, + backup: AutomationBackup? = nil + ) -> AutomationOperationResult { + AutomationOperationResult( + operation: operation.redactedForResult, + status: .failed(message), + appliedSteps: appliedSteps, + verificationEvidence: evidence, + rollback: .notNeeded, + manualRecovery: manualRecovery, + backup: backup?.redactedForResult + ) + } + + private static func checksum(_ data: Data) -> String { + SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined() + } + + private static func sameLegacyLoginItem( + _ candidate: AutomationRecord, + _ selected: AutomationRecord + ) -> Bool { + guard candidate.sourceKind == .legacyLoginItem else { return false } + if candidate.id == selected.id { return true } + guard let candidatePath = candidate.executable, let selectedPath = selected.executable else { + return false + } + return URL(fileURLWithPath: candidatePath).standardizedFileURL.path + == URL(fileURLWithPath: selectedPath).standardizedFileURL.path + } + + private static func sourceChecksum(_ data: Data, kind: AutomationKind) -> String { + if kind == .cron, let normalized = CronDocumentChecksum.checksum(data) { + return normalized + } + return checksum(data) + } + + private static func directoryAuthorization( + for directory: URL, + resourceIdentifier: String? + ) throws -> AutomationDirectoryAuthorization { + guard let authorization = AutomationDirectoryAuthorization( + directoryURL: directory, + resourceIdentifier: resourceIdentifier + ) else { throw AutomationManagerConfigurationError.destinationAuthorizationUnavailable } + return authorization + } + + private static func committedReceipt( + _ outcome: AutomationFileMutationOutcome + ) throws -> AutomationFileMutationReceipt { + guard case .committed(let receipt) = outcome else { + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + } + return receipt + } + + private func committedReplacement( + at destination: AutomationFileAuthorization, + with staged: AutomationStagedFile + ) throws -> AutomationFileMutationReceipt { + let outcome: AutomationFileMutationOutcome + do { + outcome = try fileSystem.replaceItem(at: destination, with: staged) + } catch let partial as AutomationFilePartialMutation { + throw partial + } catch { + try failAfterCleaningStaged(staged, originalError: error) + } + switch outcome { + case .committed(let receipt): + return receipt + case .unchanged: + try failAfterCleaningStaged( + staged, + originalError: AutomationManagerConfigurationError.recoverableSourceUnavailable + ) + } + } + + private func failAfterCleaningStaged( + _ staged: AutomationStagedFile, + originalError: Error + ) throws -> Never { + do { + try removeCleanupArtifact(staged.authorization) + } catch { + let survivors = currentAuthorizedSurvivors([staged.authorization]) + guard !survivors.isEmpty else { throw originalError } + throw AutomationFilePartialMutation( + kind: .remove, + commitState: .unknown, + observedFiles: survivors, + recoveryHandle: survivors.first, + recoveryHandles: survivors, + resultURL: staged.url + ) + } + throw originalError + } + + private static func fileAuthorization( + for file: URL, + directory: AutomationDirectoryAuthorization, + resourceIdentifier: String?, + existed: Bool + ) throws -> AutomationFileAuthorization { + let expectation: AutomationFileExpectation + if existed { + guard let resourceIdentifier, !resourceIdentifier.isEmpty else { + throw AutomationManagerConfigurationError.destinationAuthorizationUnavailable + } + expectation = .existing(resourceIdentifier: resourceIdentifier) + } else { + expectation = .absent + } + guard let authorization = AutomationFileAuthorization( + fileURL: file, + directory: directory, + expectation: expectation + ) else { throw AutomationManagerConfigurationError.destinationAuthorizationUnavailable } + return authorization + } +} + +private extension AutomationOperation { + var redactedForResult: AutomationOperation { + switch self { + case .confirmedRunToCompletion(let confirmation): + .confirmedRunToCompletion(AutomationRunToCompletionConfirmation( + recordID: confirmation.recordID, + sourceChecksum: confirmation.sourceChecksum, + exactCommand: "" + )) + case .edit(let payload): + .edit(payload.redactedForResult) + case .duplicate(let payload): + .duplicate(payload.redactedForResult) + case .importRecord(let payload): + .importRecord(AutomationImportPayload( + destination: URL(fileURLWithPath: "/redacted"), + data: Data(), + expectedKind: payload.expectedKind, + expectedDestinationChecksum: payload.expectedDestinationChecksum + )) + default: + self + } + } + + var requiredCapability: AutomationCapability { + switch self { + case .startNow, .confirmedRunToCompletion: .startNow + case .stopCurrentRun: .stopCurrentRun + case .enable: .enable + case .disable: .disable + case .disableAndStop: .disableAndStop + case .edit: .edit + case .duplicate: .duplicate + case .importRecord: .importRecord + case .exportRecord: .exportRecord + case .remove: .remove + case .restore: .restore + } + } + + var controlsLinkedProcesses: Bool { + switch self { + case .stopCurrentRun, .disableAndStop: + true + default: + false + } + } + + var requiresFreshProcessSnapshot: Bool { + if controlsLinkedProcesses { return true } + if case .startNow = self { return true } + return false + } + + var requiredPostconditions: Set { + switch self { + case .startNow: [.targetResolved, .currentRunStarted] + case .confirmedRunToCompletion: [.targetResolved, .runCompleted] + case .stopCurrentRun: [.noLinkedProcess] + case .enable: [.futureLaunchesEnabled] + case .disable: [.futureLaunchesDisabled] + case .disableAndStop: [.futureLaunchesDisabled, .noLinkedProcess] + case .edit, .duplicate, .importRecord, .restore: [.sourceInstalled] + case .exportRecord: [.targetResolved] + case .remove: [.sourceRemoved] + } + } +} + +private extension AutomationEditPayload { + var redactedForResult: AutomationEditPayload { + AutomationEditPayload( + label: "", + executable: "/redacted", + arguments: arguments.map { _ in "" }, + environment: environment.mapValues { _ in "" }, + workingDirectory: workingDirectory == nil ? nil : "/redacted", + schedule: schedule, + rawRepresentation: nil, + destination: destination == nil ? nil : URL(fileURLWithPath: "/redacted"), + expectedDestinationChecksum: expectedDestinationChecksum + ) + } +} + +private extension AutomationBackup { + var redactedForResult: AutomationBackup { + AutomationBackup( + id: id, + recordID: recordID, + sourceURL: URL(fileURLWithPath: "/redacted"), + backupURL: URL(fileURLWithPath: "/redacted"), + checksum: checksum, + createdAt: createdAt, + sourceExisted: sourceExisted, + ownerUID: ownerUID, + kind: kind, + sourceKind: sourceKind, + authorizedRecordIDs: authorizedRecordIDs, + parentResourceIdentifier: nil + ) + } +} + +private struct SourceProposal { + let data: Data + let destination: URL + let intendedLabel: String? + let intendedRecordID: AutomationRecord.ID? + let cronBinding: CronProposalBinding? + let cronRemovalBinding: CronRemovalBinding? + let intendedChecksum: String + let expectedDestinationChecksum: String? + var restoresSourcePresence: Bool + + init( + data: Data, + destination: URL, + intendedLabel: String?, + intendedRecordID: AutomationRecord.ID?, + cronBinding: CronProposalBinding?, + cronRemovalBinding: CronRemovalBinding? = nil, + intendedChecksum: String, + expectedDestinationChecksum: String?, + restoresSourcePresence: Bool = true + ) { + self.data = data + self.destination = destination + self.intendedLabel = intendedLabel + self.intendedRecordID = intendedRecordID + self.cronBinding = cronBinding + self.cronRemovalBinding = cronRemovalBinding + self.intendedChecksum = intendedChecksum + self.expectedDestinationChecksum = expectedDestinationChecksum + self.restoresSourcePresence = restoresSourcePresence + } +} + +private struct CronProposalBinding { + let selectedRecordID: AutomationRecord.ID + let intendedRecordID: AutomationRecord.ID + let command: String + let schedule: AutomationSchedule + let environment: [String: String] + let enabledState: AutomationEnabledState +} + +private struct CronRemovalBinding { + let command: String + let schedule: AutomationSchedule + let environment: [String: String] + let enabledState: AutomationEnabledState + let remainingMatchingCount: Int +} + +private struct CronEntryFingerprint: Hashable { + let scheduleExpression: String + let command: String + let environment: [String] + let isEnabled: Bool +} + +private struct DestinationSnapshot { + let existed: Bool + let data: Data? + let metadata: AutomationFileMetadata + let parentMetadata: AutomationFileMetadata +} + +private struct ProposalError: Error { + let message: String + + init(_ message: String) { + self.message = message + } +} + +private extension AutomationSourceKind { + var requiresRecoverableSource: Bool { + self == .crontab || self == .legacyLoginItem + } +} + +private enum RollbackError: Error { + case integrityMismatch + case priorStateUnverified + case sourceChangedExternally +} + +private enum TransactionFailurePhase: Equatable { + case sourceMutationFailed + case executorAttempted + case verificationFailed +} diff --git a/Sources/DevScopeCore/AutomationModels.swift b/Sources/DevScopeCore/AutomationModels.swift new file mode 100644 index 0000000..26b685a --- /dev/null +++ b/Sources/DevScopeCore/AutomationModels.swift @@ -0,0 +1,254 @@ +import CryptoKit +import Darwin +import Foundation + +public enum AutomationKind: String, CaseIterable, Hashable, Sendable { + case launchAgent, launchDaemon, loginItem, backgroundItem, cron +} + +public enum AutomationSourceKind: String, CaseIterable, Hashable, Sendable { + case launchAgent, launchDaemon, serviceManagement, legacyLoginItem, crontab +} + +public enum AutomationOwnership: String, CaseIterable, Hashable, Sendable { + case user, thirdPartySystem, appleSystem, managed +} + +public enum AutomationState: String, CaseIterable, Hashable, Sendable { + case running, idle, disabled, needsApproval, invalid, unresolved +} + +public enum AutomationEnabledState: String, Hashable, Sendable { + case enabled, disabled, unknown +} + +public enum AutomationLoadState: String, Hashable, Sendable { + case loaded, unloaded, unknown +} + +public enum AutomationApprovalState: String, Sendable { + case approved, needsApproval, notApplicable, unknown +} + +public enum AutomationCapability: String, CaseIterable, Hashable, Sendable { + case startNow, stopCurrentRun, enable, disable, disableAndStop + case edit, duplicate, importRecord, exportRecord, remove, restore +} + +public struct AutomationSchedule: Equatable, Hashable, Sendable { + public enum Trigger: Equatable, Hashable, Sendable { + case atLogin + case runAtLoad + case interval(seconds: Int) + case calendar(String) + case keepAlive + case cron(String) + case demand + } + + public let triggers: [Trigger] + public let summary: String + + public init(triggers: [Trigger], summary: String) { + self.triggers = triggers + self.summary = summary + } +} + +public struct AutomationEvidence: Equatable, Hashable, Sendable { + public enum Strength: Int, Comparable, Sendable { + case weak = 0 + case strong = 1 + + public static func < (lhs: Self, rhs: Self) -> Bool { + lhs.rawValue < rhs.rawValue + } + } + + public let strength: Strength + public let source: String + public let detail: String + + public init(strength: Strength, source: String, detail: String) { + self.strength = strength + self.source = source + self.detail = detail + } +} + +public struct AutomationRecord: Identifiable, Equatable, Sendable { + public struct ID: RawRepresentable, Hashable, Sendable { + public let rawValue: String + + public init(rawValue: String) { + self.rawValue = rawValue + } + + public init( + source: AutomationSourceKind, + ownerUID: uid_t, + label: String, + sourcePath: String + ) { + let canonicalPath = URL(fileURLWithPath: sourcePath).standardizedFileURL.path + var identity = Data() + for component in [source.rawValue, String(ownerUID), label, canonicalPath] { + let bytes = Data(component.utf8) + let byteCount = UInt64(bytes.count) + for shift in stride(from: 56, through: 0, by: -8) { + identity.append(UInt8(truncatingIfNeeded: byteCount >> shift)) + } + identity.append(bytes) + } + rawValue = SHA256.hash(data: identity) + .map { String(format: "%02x", $0) } + .joined() + } + } + + public let id: ID + public let kind: AutomationKind + public let sourceKind: AutomationSourceKind + public let label: String + public let displayName: String + public let providerBundleIdentifier: String? + public let ownerUID: uid_t? + public let ownership: AutomationOwnership + public let executable: String? + public let arguments: [String] + public let commandSignature: String? + public let environment: [String: String] + public let workingDirectory: String? + public let schedule: AutomationSchedule + public let sourceURL: URL? + public let sourceChecksum: String? + public let enabledState: AutomationEnabledState + public let loadState: AutomationLoadState + public let approvalState: AutomationApprovalState + public let state: AutomationState + public let evidence: [AutomationEvidence] + public let capabilities: Set + public let validationFindings: [String] + + public init( + id: ID, + kind: AutomationKind, + sourceKind: AutomationSourceKind, + label: String, + displayName: String, + providerBundleIdentifier: String?, + ownerUID: uid_t?, + ownership: AutomationOwnership, + executable: String?, + arguments: [String], + commandSignature: String? = nil, + environment: [String: String], + workingDirectory: String?, + schedule: AutomationSchedule, + sourceURL: URL?, + sourceChecksum: String?, + enabledState: AutomationEnabledState, + loadState: AutomationLoadState, + approvalState: AutomationApprovalState, + state: AutomationState, + evidence: [AutomationEvidence], + capabilities: Set, + validationFindings: [String] + ) { + self.id = id + self.kind = kind + self.sourceKind = sourceKind + self.label = label + self.displayName = displayName + self.providerBundleIdentifier = providerBundleIdentifier + self.ownerUID = ownerUID + self.ownership = ownership + self.executable = executable + self.arguments = arguments + self.commandSignature = commandSignature + self.environment = environment + self.workingDirectory = workingDirectory + self.schedule = schedule + self.sourceURL = sourceURL + self.sourceChecksum = sourceChecksum + self.enabledState = enabledState + self.loadState = loadState + self.approvalState = approvalState + self.state = state + self.evidence = evidence + self.capabilities = capabilities + self.validationFindings = validationFindings + } +} + +public enum AutomationSourceHealthState: String, Sendable { + case healthy, partial, failed, permissionRequired +} + +public struct AutomationSourceHealth: Equatable, Sendable { + public let kind: AutomationSourceKind + public let state: AutomationSourceHealthState + public let message: String? + public let refreshedAt: Date + + public init( + kind: AutomationSourceKind, + state: AutomationSourceHealthState, + message: String?, + refreshedAt: Date + ) { + self.kind = kind + self.state = state + self.message = message + self.refreshedAt = refreshedAt + } +} + +public struct AutomationSourceSnapshot: Equatable, Sendable { + public let records: [AutomationRecord] + public let health: AutomationSourceHealth + + public init(records: [AutomationRecord], health: AutomationSourceHealth) { + self.records = records + self.health = health + } + + public static func healthy( + kind: AutomationSourceKind, + records: [AutomationRecord], + refreshedAt: Date = Date() + ) -> Self { + Self( + records: records, + health: AutomationSourceHealth( + kind: kind, + state: .healthy, + message: nil, + refreshedAt: refreshedAt + ) + ) + } + + public static func failed( + kind: AutomationSourceKind, + message: String, + refreshedAt: Date = Date() + ) -> Self { + Self( + records: [], + health: AutomationSourceHealth( + kind: kind, + state: .failed, + message: message, + refreshedAt: refreshedAt + ) + ) + } +} + +public enum AutomationParseError: Error, Equatable, Sendable { + case unreadablePropertyList + case missingLabel + case missingProgram + case invalidField(String) +} diff --git a/Sources/DevScopeCore/AutomationPresentation.swift b/Sources/DevScopeCore/AutomationPresentation.swift new file mode 100644 index 0000000..3df8349 --- /dev/null +++ b/Sources/DevScopeCore/AutomationPresentation.swift @@ -0,0 +1,1004 @@ +import Foundation + +public struct AutomationPresentationContext: Equatable, Sendable { + public static let defaultLongRunningThreshold: TimeInterval = 14_400 + + public let inventory: AutomationInventorySnapshot + public let longRunningThreshold: TimeInterval + + public init( + inventory: AutomationInventorySnapshot, + longRunningThreshold: TimeInterval + ) { + self.inventory = inventory + if longRunningThreshold.isFinite { + self.longRunningThreshold = max(0, longRunningThreshold) + } else { + self.longRunningThreshold = Self.defaultLongRunningThreshold + } + } + + public func build( + processes: [DevProcess], + now: Date, + calendar: Calendar = .current, + isProcessSnapshotComplete: Bool = true + ) -> AutomationPresentationSnapshot { + AutomationPresentationSnapshot.build( + inventory: inventory, + processes: processes, + longRunningThreshold: longRunningThreshold, + now: now, + calendar: calendar, + isProcessSnapshotComplete: isProcessSnapshotComplete + ) + } +} + +public struct AutomationPresentationSnapshot: Equatable, Sendable { + public let inventory: AutomationInventorySnapshot + public let linksByProcessID: [Int32: AutomationProcessLink] + public let allLinksByProcessID: [Int32: [AutomationProcessLink]] + public let longRunningProcessIDs: Set + public let longRunningProcessIdentities: Set + public let processIdentitiesByID: [Int32: ProcessIdentity] + public let isProcessSnapshotComplete: Bool + + public init( + inventory: AutomationInventorySnapshot, + linksByProcessID: [Int32: AutomationProcessLink], + allLinksByProcessID: [Int32: [AutomationProcessLink]]? = nil, + longRunningProcessIDs: Set, + longRunningProcessIdentities: Set, + processIdentitiesByID: [Int32: ProcessIdentity], + isProcessSnapshotComplete: Bool = true + ) { + self.inventory = inventory + self.linksByProcessID = linksByProcessID + self.allLinksByProcessID = allLinksByProcessID ?? linksByProcessID.mapValues { [$0] } + self.longRunningProcessIDs = longRunningProcessIDs + self.longRunningProcessIdentities = longRunningProcessIdentities + self.processIdentitiesByID = processIdentitiesByID + self.isProcessSnapshotComplete = isProcessSnapshotComplete + } + + public static func build( + inventory: AutomationInventorySnapshot, + processes: [DevProcess], + longRunningThreshold: TimeInterval, + now: Date, + calendar: Calendar = .current, + isProcessSnapshotComplete: Bool = true + ) -> Self { + let processes = ProcessSnapshotNormalization.newestUnambiguous(processes) + let links = AutomationProcessCorrelator.links( + records: inventory.records, + processes: processes, + now: now, + calendar: calendar + ) + let allLinksByProcessID = Dictionary(grouping: links, by: \.processIdentity.pid) + let linksByProcessID = allLinksByProcessID.compactMapValues(\.first) + let stronglyRunningRecordIDs = Set( + links.filter { $0.strength == .strong }.map(\.recordID) + ) + let projectedInventory = AutomationInventorySnapshot( + generation: inventory.generation, + records: inventory.records.map { record in + stronglyRunningRecordIDs.contains(record.id) + ? record.withRuntimeState(.running) + : record + }, + health: inventory.health, + refreshedAt: inventory.refreshedAt + ) + let longRunningIdentities = Set(processes.compactMap { process in + LongRunningAssessment.isLongRunning(process.resourceUsage, threshold: longRunningThreshold) + ? ProcessIdentity(process: process) : nil + }) + let longRunning = Set(longRunningIdentities.map(\.pid)) + let identities = Dictionary(uniqueKeysWithValues: + processes.map { ($0.pid, ProcessIdentity(process: $0)) } + ) + return Self( + inventory: projectedInventory, + linksByProcessID: linksByProcessID, + allLinksByProcessID: allLinksByProcessID, + longRunningProcessIDs: longRunning, + longRunningProcessIdentities: longRunningIdentities, + processIdentitiesByID: identities, + isProcessSnapshotComplete: isProcessSnapshotComplete + ) + } + +} + +private extension AutomationRecord { + func withRuntimeState(_ state: AutomationState) -> AutomationRecord { + AutomationRecord( + id: id, + kind: kind, + sourceKind: sourceKind, + label: label, + displayName: displayName, + providerBundleIdentifier: providerBundleIdentifier, + ownerUID: ownerUID, + ownership: ownership, + executable: executable, + arguments: arguments, + commandSignature: commandSignature, + environment: environment, + workingDirectory: workingDirectory, + schedule: schedule, + sourceURL: sourceURL, + sourceChecksum: sourceChecksum, + enabledState: enabledState, + loadState: loadState, + approvalState: approvalState, + state: state, + evidence: evidence, + capabilities: capabilities, + validationFindings: validationFindings + ) + } +} + +public enum AutomationProcessBadge: Equatable, Sendable { + case automated + case longRunning(String) +} + +public enum AutomationSourceFilter: String, CaseIterable, Hashable, Sendable { + case all, launchd, loginItems, scheduled +} + +public enum AutomationOwnershipFilter: String, CaseIterable, Hashable, Sendable { + case all, user, thirdParty, appleSystem + + public func matches(_ ownership: AutomationOwnership) -> Bool { + switch self { + case .all: true + case .user: ownership == .user + case .thirdParty: ownership == .thirdPartySystem || ownership == .managed + case .appleSystem: ownership == .appleSystem + } + } +} + +public enum AutomationActivityTypeFilter: String, CaseIterable, Hashable, Sendable { + case all, automated, longRunning, both + + public func matches(isAutomated: Bool, isLongRunning: Bool) -> Bool { + switch self { + case .all: + true + case .automated: + isAutomated + case .longRunning: + isLongRunning + case .both: + isAutomated && isLongRunning + } + } +} + +public struct AutomationInventoryCountPresentation: Equatable, Sendable { + public let primaryText: String + public let contextText: String? + public let accessibilityLabel: String +} + +public enum AutomationPresentation { + public static func inventoryCount( + visibleCount: Int, + eligibleCount: Int, + totalCount: Int + ) -> AutomationInventoryCountPresentation { + let totalCount = max(0, totalCount) + let eligibleCount = min(max(0, eligibleCount), totalCount) + let visibleCount = min(max(0, visibleCount), eligibleCount) + let automationNoun = eligibleCount == 1 ? "automation" : "automations" + let primaryText = visibleCount == eligibleCount + ? "\(eligibleCount) \(automationNoun)" + : "\(visibleCount) of \(eligibleCount) \(automationNoun)" + let hiddenCount = totalCount - eligibleCount + let contextText = hiddenCount > 0 + ? "\(hiddenCount) Apple system service\(hiddenCount == 1 ? "" : "s") hidden" + : nil + let accessibilityLabel = if let contextText { + "\(primaryText). \(contextText). " + + "In Settings, open Automations and enable Include Apple System Services to review them." + } else { + primaryText + } + return AutomationInventoryCountPresentation( + primaryText: primaryText, + contextText: contextText, + accessibilityLabel: accessibilityLabel + ) + } + + public static func uniquelyStrongLinkedProcessIdentities( + for recordID: AutomationRecord.ID, + linksByProcessID: [Int32: [AutomationProcessLink]] + ) -> Set { + Set(linksByProcessID.values.compactMap { links in + let strongLinks = links.filter { $0.strength == .strong } + let strongRecordIDs = Set(strongLinks.map(\.recordID)) + let identities = Set(strongLinks.map(\.processIdentity)) + guard strongRecordIDs == [recordID], identities.count == 1 else { return nil } + return identities.first + }) + } + + public static func resolvedSelection( + current: AutomationRecord.ID?, + visibleIDs: [AutomationRecord.ID] + ) -> AutomationRecord.ID? { + guard let current, visibleIDs.contains(current) else { return visibleIDs.first } + return current + } + + public static func filtered( + _ records: [AutomationRecord], + source: AutomationSourceFilter, + state: AutomationState?, + ownership: AutomationOwnershipFilter, + searchText: String, + includeAppleSystemServices: Bool + ) -> [AutomationRecord] { + let query = searchText.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() + return records.filter { record in + guard includeAppleSystemServices || record.ownership != .appleSystem, + state == nil || record.state == state, + ownership.matches(record.ownership), + sourceMatches(record.sourceKind, filter: source) else { return false } + guard !query.isEmpty else { return true } + return [ + record.label, record.displayName, record.executable ?? "", + record.arguments.joined(separator: " "), record.sourceURL?.path ?? "", + record.providerBundleIdentifier ?? "", record.schedule.summary, + ].contains { $0.lowercased().contains(query) } + }.sorted(by: recordPrecedes) + } + + public static func badges( + isAutomated: Bool, + isLongRunning: Bool, + elapsed: String + ) -> [AutomationProcessBadge] { + var result: [AutomationProcessBadge] = [] + if isAutomated { result.append(.automated) } + if isLongRunning { result.append(.longRunning(compactElapsed(elapsed))) } + return result + } + + private static func sourceMatches( + _ kind: AutomationSourceKind, + filter: AutomationSourceFilter + ) -> Bool { + switch filter { + case .all: true + case .launchd: kind == .launchAgent || kind == .launchDaemon + case .loginItems: kind == .serviceManagement || kind == .legacyLoginItem + case .scheduled: kind == .crontab + } + } + + private static func recordPrecedes(_ lhs: AutomationRecord, _ rhs: AutomationRecord) -> Bool { + if lhs.label != rhs.label { + return lhs.label.utf8.lexicographicallyPrecedes(rhs.label.utf8) + } + return lhs.id.rawValue.utf8.lexicographicallyPrecedes(rhs.id.rawValue.utf8) + } + + private static func compactElapsed(_ elapsed: String) -> String { + let seconds = ProcessPresentation.elapsedSeconds(elapsed) + guard seconds >= 0 else { return elapsed } + if seconds >= 86_400 { return "\(seconds / 86_400)d" } + if seconds >= 3_600 { return "\(seconds / 3_600)h" } + if seconds >= 60 { return "\(seconds / 60)m" } + return "\(seconds)s" + } +} + +public enum AutomationManagementAction: Equatable, Hashable, Sendable { + case startNow + case stopCurrentRun + case enable + case disable + case disableAndStop + case edit + case duplicate + case importRecord + case exportRedacted + case exportUnredacted + case remove + case restore(AutomationBackup.ID) + + public var title: String { + switch self { + case .startNow: "Start Now" + case .stopCurrentRun: "Stop Current Run" + case .enable: "Enable" + case .disable: "Disable" + case .disableAndStop: "Disable and Stop" + case .edit: "Edit" + case .duplicate: "Duplicate" + case .importRecord: "Import" + case .exportRedacted: "Export Redacted" + case .exportUnredacted: "Export Unredacted" + case .remove: "Remove" + case .restore: "Restore" + } + } + + public var isDestructive: Bool { + switch self { + case .stopCurrentRun, .disableAndStop, .remove: + true + default: + false + } + } + + public var placement: AutomationManagementActionPlacement { + switch self { + case .startNow, .stopCurrentRun, .enable, .disable, .disableAndStop: + .pinned + case .restore: + .history + case .edit, .duplicate, .importRecord, .exportRedacted, .exportUnredacted, .remove: + .overflow + } + } + + public var emphasis: AutomationManagementActionEmphasis { + switch self { + case .startNow, .enable: + .positive + case .disable, .exportUnredacted: + .caution + case .stopCurrentRun, .disableAndStop, .remove: + .destructive + case .edit, .duplicate, .importRecord, .exportRedacted, .restore: + .neutral + } + } + + public var systemImage: String { + switch self { + case .startNow: "play.fill" + case .stopCurrentRun: "stop.fill" + case .enable: "checkmark.circle" + case .disable: "pause.circle" + case .disableAndStop: "stop.circle.fill" + case .edit: "square.and.pencil" + case .duplicate: "plus.square.on.square" + case .importRecord: "square.and.arrow.down" + case .exportRedacted: "square.and.arrow.up" + case .exportUnredacted: "lock.open.fill" + case .remove: "trash" + case .restore: "clock.arrow.circlepath" + } + } +} + +public enum AutomationManagementActionPlacement: Equatable, Sendable { + case pinned + case overflow + case history +} + +public enum AutomationManagementActionEmphasis: Equatable, Sendable { + case neutral + case positive + case caution + case destructive +} + +public struct AutomationConfirmationPolicy: Equatable, Sendable { + public let title: String + public let consequence: String + public let requiredLabel: String? + public let displayedCommand: String? + + public init( + title: String, + consequence: String, + requiredLabel: String? = nil, + displayedCommand: String? = nil + ) { + self.title = title + self.consequence = consequence + self.requiredLabel = requiredLabel + self.displayedCommand = displayedCommand + } + + public func isSatisfiedByLabel(_ candidate: String) -> Bool { + guard let requiredLabel else { return true } + return candidate == requiredLabel + } +} + +public struct AutomationOperationResultPresentation: Equatable, Sendable { + public let title: String + public let detail: String + public let appliedEvidence: [String] + public let verificationEvidence: [String] + public let rollbackEvidence: String + public let backupEvidence: String? + public let mutationEvidence: [String] + public let recoveryGuidance: String? + public let isFailure: Bool + + public init( + title: String, + detail: String, + appliedEvidence: [String], + verificationEvidence: [String], + rollbackEvidence: String, + backupEvidence: String?, + mutationEvidence: [String], + recoveryGuidance: String?, + isFailure: Bool + ) { + self.title = title + self.detail = detail + self.appliedEvidence = appliedEvidence + self.verificationEvidence = verificationEvidence + self.rollbackEvidence = rollbackEvidence + self.backupEvidence = backupEvidence + self.mutationEvidence = mutationEvidence + self.recoveryGuidance = recoveryGuidance + self.isFailure = isFailure + } +} + +public enum AutomationManagementPresentation { + public static func actions( + decision: AutomationCapabilityDecision, + backups: [AutomationBackup], + record: AutomationRecord + ) -> [AutomationManagementAction] { + let capabilities = decision.capabilities + var result: [AutomationManagementAction] = [] + if capabilities.contains(.startNow) { result.append(.startNow) } + if capabilities.contains(.stopCurrentRun) { result.append(.stopCurrentRun) } + if capabilities.contains(.enable) { result.append(.enable) } + if capabilities.contains(.disable) { result.append(.disable) } + if capabilities.contains(.disableAndStop) { result.append(.disableAndStop) } + if capabilities.contains(.edit) { result.append(.edit) } + if capabilities.contains(.duplicate) { result.append(.duplicate) } + if capabilities.contains(.importRecord) { result.append(.importRecord) } + if capabilities.contains(.exportRecord) { + result.append(.exportRedacted) + if record.ownership == .user, + record.kind != .launchDaemon, + record.kind != .backgroundItem, + record.sourceKind != .launchDaemon, + record.sourceKind != .serviceManagement { + result.append(.exportUnredacted) + } + } + if capabilities.contains(.remove) { result.append(.remove) } + if capabilities.contains(.restore) { + result.append(contentsOf: backups + .filter { $0.recordID == record.id } + .sorted { $0.createdAt > $1.createdAt } + .map { .restore($0.id) }) + } + return result + } + + public static func helpText( + for action: AutomationManagementAction, + record: AutomationRecord + ) -> String { + switch action { + case .startNow: + "Request this automation to run now" + case .stopCurrentRun where isLaunchAgent(record): + "Unload the exact launchd service after confirmation" + case .stopCurrentRun: + "Stop strongly linked current processes after confirmation" + case .enable: + "Allow future launches after confirmation" + case .disable: + "Prevent future launches after confirmation" + case .disableAndStop where isLaunchAgent(record): + "Prevent future launches and unload the exact launchd service" + case .disableAndStop: + "Prevent future launches and stop strongly linked current processes" + case .edit: + "Edit this user-owned automation through validated fields" + case .duplicate: + "Create a disabled copy with a distinct label" + case .importRecord: + "Validate and preview an automation file before importing" + case .exportRedacted: + "Export a secret-redacted inspection artifact" + case .exportUnredacted: + "Explicitly request an unredacted export when policy allows" + case .remove: + "Remove through a recoverable transaction after exact-label confirmation" + case .restore: + "Restore this concrete verified backup" + } + } + + public static func confirmation( + for action: AutomationManagementAction, + record: AutomationRecord, + backups: [AutomationBackup] = [] + ) -> AutomationConfirmationPolicy? { + switch action { + case .startNow where record.kind == .cron: + return AutomationConfirmationPolicy( + title: "Run this scheduled command now?", + consequence: "Runs this cron command to completion outside its normal schedule.", + displayedCommand: record.commandSignature.map(ProcessPresentation.redactedCommand) + ) + case .startNow: + return AutomationConfirmationPolicy( + title: "Start this automation now?", + consequence: "Requests an immediate launch. This does not change whether future launches are enabled." + ) + case .stopCurrentRun: + if record.kind == .launchAgent, record.sourceKind == .launchAgent { + return AutomationConfirmationPolicy( + title: "Stop the current run?", + consequence: "Unloads the exact launchd service. " + + "This does not disable future launches; Start Now or Enable can load it again." + ) + } + return AutomationConfirmationPolicy( + title: "Stop the current run?", + consequence: "Stops only strongly linked, birth-validated processes. " + + "A scheduled or supervising automation may launch again." + ) + case .enable: + return AutomationConfirmationPolicy( + title: "Enable future launches?", + consequence: "Allows future launches. This does not start a run now." + ) + case .disable: + return AutomationConfirmationPolicy( + title: "Disable future launches?", + consequence: "Prevents future launches. This does not stop a process that is already running." + ) + case .disableAndStop where isLaunchAgent(record): + return AutomationConfirmationPolicy( + title: "Disable and stop this automation?", + consequence: "Prevents future launches and unloads the exact launchd service." + ) + case .disableAndStop: + return AutomationConfirmationPolicy( + title: "Disable and stop this automation?", + consequence: "Prevents future launches and stops only strongly linked current processes." + ) + case .remove: + return AutomationConfirmationPolicy( + title: "Remove this automation?", + consequence: "Unloads it where applicable, removes its source through a recoverable transaction, and writes a restoration manifest.", + requiredLabel: record.label + ) + case .exportUnredacted: + return AutomationConfirmationPolicy( + title: "Export unredacted source?", + consequence: "The exported file may contain commands, paths, environment values, credentials, or other secrets. DevScope will not log its contents." + ) + case .restore(let backupID): + guard let backup = backups.first(where: { $0.id == backupID && $0.recordID == record.id }) else { + return nil + } + return AutomationConfirmationPolicy( + title: "Restore this verified backup?", + consequence: "Replaces the current definition with the verified backup from \(backup.createdAt.formatted(date: .abbreviated, time: .shortened)) after conflict and integrity checks." + ) + default: + return nil + } + } + + private static func isLaunchAgent(_ record: AutomationRecord) -> Bool { + record.kind == .launchAgent && record.sourceKind == .launchAgent + } + + public static func confirmedOperation( + for action: AutomationManagementAction, + record: AutomationRecord + ) -> AutomationOperation? { + switch action { + case .startNow where record.kind == .cron: + guard let exactCommand = record.commandSignature else { return nil } + return .confirmedRunToCompletion(AutomationRunToCompletionConfirmation( + recordID: record.id, + sourceChecksum: record.sourceChecksum, + exactCommand: exactCommand + )) + case .startNow: return AutomationOperation.startNow + case .stopCurrentRun: return AutomationOperation.stopCurrentRun + case .enable: return AutomationOperation.enable + case .disable: return AutomationOperation.disable + case .disableAndStop: return AutomationOperation.disableAndStop + case .exportRedacted: return AutomationOperation.exportRecord(redacted: true) + case .exportUnredacted: return AutomationOperation.exportRecord(redacted: false) + case .remove: return AutomationOperation.remove + case .restore(let backupID): return AutomationOperation.restore(backupID) + case .edit, .duplicate, .importRecord: return nil + } + } + + public static func result( + _ result: AutomationOperationResult + ) -> AutomationOperationResultPresentation { + let title: String + let detail: String + let isFailure: Bool + switch result.status { + case .succeeded: + title = "Completed" + detail = "The operation completed and DevScope refreshed the automation inventory." + isFailure = false + case .rejected(let reason): + title = "Not performed" + detail = ProcessPresentation.redactedCommand(reason) + isFailure = true + case .failed(let reason): + title = "Operation failed" + detail = ProcessPresentation.redactedCommand(reason) + isFailure = true + case .partialFailure(let reason): + title = "Partially completed" + detail = ProcessPresentation.redactedCommand(reason) + isFailure = true + } + + var recovery: [String] = [] + if let manualRecovery = result.manualRecovery { + recovery.append(ProcessPresentation.redactedCommand(manualRecovery)) + } + if case .failed(let reason) = result.rollback { + recovery.append("Rollback failed: \(ProcessPresentation.redactedCommand(reason))") + } + var recoveryPaths: Set = [] + for evidence in [result.fileMutationEvidence, result.rollbackFileMutationEvidence].compactMap({ $0 }) { + for handle in evidence.recoveryHandles { + let path = handle.fileURL.standardizedFileURL.path + if recoveryPaths.insert(path).inserted { + recovery.append("Recovery location: \(path)") + } + } + } + let rollbackEvidence: String + switch result.rollback { + case .notNeeded: + rollbackEvidence = "Rollback was not required." + case .restored(let backupID): + rollbackEvidence = "Rollback restored verified backup \(backupID.rawValue.uuidString.prefix(8))." + case .failed(let reason): + rollbackEvidence = "Rollback failed: \(ProcessPresentation.redactedCommand(reason))" + } + let backupEvidence = result.backup.map { + "Recovery backup verified · \($0.checksum.prefix(12)) · \($0.createdAt.formatted(date: .abbreviated, time: .shortened))" + } + var mutationEvidence: [String] = [] + if let evidence = result.fileMutationEvidence { + mutationEvidence.append(mutationSummary(evidence, subject: "Source")) + } + if let evidence = result.rollbackFileMutationEvidence { + mutationEvidence.append(mutationSummary(evidence, subject: "Rollback")) + } + return AutomationOperationResultPresentation( + title: title, + detail: detail, + appliedEvidence: result.appliedSteps.map(ProcessPresentation.redactedCommand), + verificationEvidence: result.verificationEvidence.map(ProcessPresentation.redactedCommand), + rollbackEvidence: rollbackEvidence, + backupEvidence: backupEvidence, + mutationEvidence: mutationEvidence, + recoveryGuidance: recovery.isEmpty ? nil : recovery.joined(separator: "\n"), + isFailure: isFailure + ) + } + + private static func mutationSummary( + _ evidence: AutomationFilePartialMutation, + subject: String + ) -> String { + let mutation: String + switch evidence.kind { + case .replace: mutation = "replacement" + case .remove: mutation = "removal" + case .trash: mutation = "trash move" + } + let state = evidence.commitState == .committed ? "committed" : "outcome is unknown" + return "\(subject) \(mutation) \(state); \(evidence.observedFiles.count) observed files and \(evidence.recoveryHandles.count) recovery handles were retained." + } +} + +public enum AutomationEditorPresentation { + public static func environmentText(for environment: [String: String]) -> String { + environment.keys.sorted().compactMap { key in + environment[key].map { "\(key)=\($0)" } + }.joined(separator: "\n") + } + + public static func environment(from text: String) -> [String: String]? { + var environment: [String: String] = [:] + for rawLine in text.split(whereSeparator: \.isNewline) { + let line = String(rawLine) + guard let separator = line.firstIndex(of: "=") else { return nil } + let key = String(line[.. String { + schedule.triggers.map { trigger in + switch trigger { + case .atLogin: "run-at-load" + case .runAtLoad: "run-at-load" + case .interval(let seconds): "interval \(seconds)" + case .calendar(let summary): "calendar \(summary)" + case .keepAlive: "keep-alive" + case .cron(let expression): "cron \(expression)" + case .demand: "on-demand" + } + }.joined(separator: "\n") + } + + public static func schedule(from text: String) -> AutomationSchedule? { + let lines = text.split(whereSeparator: \.isNewline).map { + String($0).trimmingCharacters(in: .whitespaces) + }.filter { !$0.isEmpty } + guard !lines.isEmpty else { return nil } + var triggers: [AutomationSchedule.Trigger] = [] + for line in lines { + switch line { + case "at-login": + triggers.append(.runAtLoad) + case "run-at-load": + triggers.append(.runAtLoad) + case "keep-alive": + triggers.append(.keepAlive) + case "on-demand": + triggers.append(.demand) + default: + if line.hasPrefix("interval "), + let seconds = Int(line.dropFirst("interval ".count)), + seconds > 0 { + triggers.append(.interval(seconds: seconds)) + } else if line.hasPrefix("calendar ") { + let summary = String(line.dropFirst("calendar ".count)) + guard !summary.isEmpty else { return nil } + triggers.append(.calendar(summary)) + } else if line.hasPrefix("cron ") { + let expression = String(line.dropFirst("cron ".count)) + guard !expression.isEmpty else { return nil } + triggers.append(.cron(expression)) + } else { + return nil + } + } + } + guard Set(triggers).count == triggers.count else { return nil } + if triggers.contains(.demand) { + guard triggers == [.demand] else { return nil } + return AutomationSchedule(triggers: triggers, summary: "On demand") + } + if triggers.contains(where: { + if case .cron = $0 { return true } + return false + }) { + guard triggers.count == 1, + case .cron(let expression) = triggers[0] else { return nil } + return AutomationSchedule(triggers: triggers, summary: expression) + } + guard triggers.filter({ + if case .interval = $0 { return true } + return false + }).count <= 1 else { return nil } + + if triggers.contains(.keepAlive), !triggers.contains(.runAtLoad) { + triggers.append(.runAtLoad) + } + var canonical: [AutomationSchedule.Trigger] = [] + if triggers.contains(.runAtLoad) { canonical.append(.runAtLoad) } + if triggers.contains(.keepAlive) { canonical.append(.keepAlive) } + canonical.append(contentsOf: triggers.filter { + if case .interval = $0 { return true } + return false + }) + canonical.append(contentsOf: triggers.filter { + if case .calendar = $0 { return true } + return false + }) + guard canonical.count == triggers.count else { return nil } + let summaries = canonical.map { triggerSummary($0) } + return AutomationSchedule(triggers: canonical, summary: summaries.joined(separator: ", ")) + } + + private static func triggerSummary(_ trigger: AutomationSchedule.Trigger) -> String { + switch trigger { + case .atLogin, .runAtLoad: "At load" + case .keepAlive: "Keep alive" + case .interval(let seconds): "Every \(seconds) seconds" + case .calendar(let summary): summary + case .cron(let expression): expression + case .demand: "On demand" + } + } + + public static func validationMessage( + record: AutomationRecord, + purposeIsDuplicate: Bool, + label: String, + executable: String, + arguments: [String], + environment: [String: String], + schedule: AutomationSchedule, + usesRawRepresentation: Bool, + rawData: Data?, + duplicateDestination: URL?, + workingDirectory: String? = nil + ) -> String? { + guard !label.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { + return "A label is required." + } + guard !executable.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { + return "An executable is required." + } + guard executable.hasPrefix("/"), !executable.contains("\0") else { + return "The executable must be an absolute path without null characters." + } + if usesRawRepresentation { + guard let rawData, !rawData.isEmpty else { + return "Paste a raw representation before applying." + } + if record.kind == .launchAgent || record.kind == .launchDaemon { + guard let plist = try? PropertyListSerialization.propertyList(from: rawData, format: nil) as? [String: Any] else { + return "The raw property list is not valid." + } + guard plist["Label"] as? String == label else { + return "The raw property-list label does not match the reviewed label." + } + let rawExecutable: String? + let rawArguments: [String] + if let program = plist["Program"] as? String { + rawExecutable = program + rawArguments = plist["ProgramArguments"] as? [String] ?? [] + } else if let programArguments = plist["ProgramArguments"] as? [String], + let first = programArguments.first { + rawExecutable = first + rawArguments = Array(programArguments.dropFirst()) + } else { + rawExecutable = nil + rawArguments = [] + } + guard rawExecutable == executable, rawArguments == arguments else { + return "The raw property-list command does not match the reviewed executable and arguments." + } + guard let parsed = try? LaunchdPlistParser.parse( + data: rawData, + sourceURL: record.sourceURL ?? URL(fileURLWithPath: "/tmp/devscope-editor.plist"), + ownerUID: record.ownerUID ?? 0, + ownership: record.ownership + ), + (plist["EnvironmentVariables"] as? [String: String] ?? [:]) == environment, + parsed.schedule.triggers == schedule.triggers, + plist["WorkingDirectory"] as? String == workingDirectory + else { + return "The raw property-list environment, schedule, or working directory does not match the reviewed fields." + } + } else if record.kind == .cron { + let document = CronParser.parse(String(decoding: rawData, as: UTF8.self)) + guard document.invalidLines.isEmpty else { + return "The raw crontab contains invalid lines." + } + let reviewedCommand = ([executable] + arguments).joined(separator: " ") + let matchingReviewedEntries = document.entries.filter { + $0.command == reviewedCommand + && $0.schedule.triggers == schedule.triggers + && $0.environment == environment + } + guard !matchingReviewedEntries.isEmpty else { + return "The raw crontab does not contain the reviewed command, schedule, and environment." + } + if purposeIsDuplicate { + let originalCount = document.entries.filter { + $0.command == record.commandSignature + && $0.schedule.triggers == record.schedule.triggers + && $0.environment == record.environment + }.count + let requiredCount = record.commandSignature == reviewedCommand + && record.schedule.triggers == schedule.triggers + && record.environment == environment ? 2 : 1 + guard originalCount >= 1, matchingReviewedEntries.count >= requiredCount else { + return "The duplicate crontab must retain the selected entry and add one reviewed entry." + } + } + } + } + if purposeIsDuplicate && record.kind == .cron && !usesRawRepresentation { + return "Cron duplication requires an advanced raw crontab containing the existing entries and one reviewed new entry." + } + if record.kind == .cron && !usesRawRepresentation { + return "Cron edits require an advanced raw crontab containing the complete current-user document." + } + if !usesRawRepresentation, + schedule.triggers.contains(where: { + if case .calendar = $0 { return true } + if case .cron = $0 { return true } + return false + }) { + return "Calendar and cron schedules require an advanced raw source representation." + } + if purposeIsDuplicate && duplicateDestination == nil { + return "Choose a label that resolves to a distinct approved destination." + } + return nil + } +} + +public struct AutomationImportPresentation: Equatable, Sendable { + public let expectedKind: AutomationKind + public let destination: URL + public let targetLabel: String + public let targetOwnership: String + public let consequence: String + public let summary: String + public let validationMessage: String? + + public init( + data: Data, + expectedRecord: AutomationRecord, + destination: URL + ) { + expectedKind = expectedRecord.kind + self.destination = destination + targetLabel = expectedRecord.label + switch expectedRecord.ownership { + case .user: targetOwnership = "User owned" + case .thirdPartySystem: targetOwnership = "Third party" + case .appleSystem: targetOwnership = "Apple system" + case .managed: targetOwnership = "Managed" + } + consequence = "This import will replace \(expectedRecord.label) at its checksum-verified destination after ownership and kind are revalidated." + switch expectedRecord.kind { + case .launchAgent, .launchDaemon: + if let plist = try? PropertyListSerialization.propertyList(from: data, format: nil) as? [String: Any], + let label = plist["Label"] as? String, + !label.isEmpty { + summary = "\(expectedRecord.kind.rawValue): \(label)" + validationMessage = nil + } else { + summary = expectedRecord.kind.rawValue + validationMessage = "The selected file is not a labeled launchd property list." + } + case .cron: + let document = CronParser.parse(String(decoding: data, as: UTF8.self)) + summary = "Current-user crontab · \(document.entries.count) entries" + if !document.invalidLines.isEmpty { + validationMessage = "The selected crontab contains invalid lines." + } else if !document.entries.contains(where: { $0.command == expectedRecord.commandSignature }) { + validationMessage = "The selected crontab does not contain the reviewed automation command." + } else { + validationMessage = nil + } + case .loginItem, .backgroundItem: + summary = expectedRecord.kind.rawValue + validationMessage = "This automation kind does not support file import." + } + } + + public var canApply: Bool { validationMessage == nil } +} diff --git a/Sources/DevScopeCore/AutomationProcessCorrelator.swift b/Sources/DevScopeCore/AutomationProcessCorrelator.swift new file mode 100644 index 0000000..69b3de9 --- /dev/null +++ b/Sources/DevScopeCore/AutomationProcessCorrelator.swift @@ -0,0 +1,388 @@ +import Foundation + +public struct AutomationProcessLink: Equatable, Sendable { + public let recordID: AutomationRecord.ID + public let processIdentity: ProcessIdentity + public let strength: AutomationEvidence.Strength + public let evidence: [AutomationEvidence] + + public init( + recordID: AutomationRecord.ID, + processIdentity: ProcessIdentity, + strength: AutomationEvidence.Strength, + evidence: [AutomationEvidence] + ) { + self.recordID = recordID + self.processIdentity = processIdentity + self.strength = strength + self.evidence = evidence + } +} + +struct AutomationCorrelationWork: Equatable { + var indexedProcesses = 0 + var ancestrySteps = 0 + var cronRecordLookups = 0 + var cronCandidateEvaluations = 0 + var nonCronRecordLookups = 0 + var nonCronCandidateEvaluations = 0 +} + +public enum AutomationProcessCorrelator { + public static func links( + records: [AutomationRecord], + processes: [DevProcess], + now: Date, + calendar: Calendar = .current + ) -> [AutomationProcessLink] { + var work = AutomationCorrelationWork() + return links(records: records, processes: processes, now: now, calendar: calendar, work: &work) + } + + static func links( + records: [AutomationRecord], + processes: [DevProcess], + now: Date, + calendar: Calendar = .current, + work: inout AutomationCorrelationWork + ) -> [AutomationProcessLink] { + let bornProcesses = ProcessSnapshotNormalization.newestUnambiguous(processes) + .filter { $0.birthToken != nil } + work.indexedProcesses += bornProcesses.count + let nonCronProcesses = nonCronIndex(processes: bornProcesses) + let ambiguousNilLabelProvenance = ambiguousNilLabelProvenance(in: records) + let processesByPID = Dictionary(uniqueKeysWithValues: bornProcesses.map { ($0.pid, $0) }) + let cronLineage = cronLineageByPID(processesByPID: processesByPID, work: &work) + let cronCarriers = Dictionary(grouping: bornProcesses.compactMap { process -> (CronKey, DevProcess)? in + guard let argv = process.argumentVector, argv.count == 3, argv[1] == "-c", + canonicalPath(argv[0]) == canonicalPath(process.executable) else { return nil } + return (CronKey(shell: argv[0], signature: argv[2]), process) + }, by: \.0).mapValues { $0.map(\.1) } + var result: [AutomationProcessLink] = [] + + for record in records { + var matchedByPID: [Int32: (DevProcess, String)] = [:] + + if record.kind == .cron { + guard let signature = nonempty(record.commandSignature), + record.enabledState == .enabled else { continue } + let shell = nonempty(record.environment["SHELL"]) ?? "/bin/sh" + work.cronRecordLookups += 1 + for process in cronCarriers[CronKey(shell: shell, signature: signature)] ?? [] { + work.cronCandidateEvaluations += 1 + guard process.argumentVector == [shell, "-c", signature], + cronLineage[process.parentPID] == true, + startMatchesSchedule(process, schedule: record.schedule, now: now, calendar: calendar) + else { continue } + matchedByPID[process.pid] = (process, "Exact cron carrier argv, schedule minute, and ancestry") + } + } else { + let allowsNilLaunchLabel = nilLabelProvenanceKey(for: record).map { + !ambiguousNilLabelProvenance.contains($0) + } ?? true + if let executable = canonicalPath(record.executable) { + work.nonCronRecordLookups += 1 + appendNonCronMatches( + identity: .executable(executable), record: record, + index: nonCronProcesses, allowsNilLaunchLabel: allowsNilLaunchLabel, + work: &work, matches: &matchedByPID + ) + } + if let bundleIdentifier = nonempty(record.providerBundleIdentifier) { + work.nonCronRecordLookups += 1 + appendNonCronMatches( + identity: .bundle(bundleIdentifier), record: record, + index: nonCronProcesses, allowsNilLaunchLabel: allowsNilLaunchLabel, + work: &work, matches: &matchedByPID + ) + } + } + + for (_, match) in matchedByPID.sorted(by: { $0.key < $1.key }) { + result.append(AutomationProcessLink( + recordID: record.id, + processIdentity: ProcessIdentity(process: match.0), + strength: .strong, + evidence: [AutomationEvidence( + strength: .strong, + source: "process snapshot", + detail: match.1 + )] + )) + } + } + + return result.sorted(by: linkPrecedes) + } + + private enum NonCronIdentity: Hashable { + case executable(String) + case bundle(String) + + var evidenceName: String { + switch self { + case .executable: "executable" + case .bundle: "bundle identity" + } + } + } + + private struct NonCronKey: Hashable { + let identity: NonCronIdentity + let arguments: [String] + let launchLabel: String? + } + + private struct NilLabelProvenanceKey: Hashable { + let executable: String + let arguments: [String] + } + + private static func ambiguousNilLabelProvenance( + in records: [AutomationRecord] + ) -> Set { + var labelsByKey: [NilLabelProvenanceKey: Set] = [:] + for record in records where record.kind != .cron { + guard let key = nilLabelProvenanceKey(for: record) else { continue } + labelsByKey[key, default: []].insert(record.label) + } + return Set(labelsByKey.compactMap { key, labels in labels.count > 1 ? key : nil }) + } + + private static func nilLabelProvenanceKey( + for record: AutomationRecord + ) -> NilLabelProvenanceKey? { + guard let executable = canonicalPath(record.executable) else { return nil } + return NilLabelProvenanceKey(executable: executable, arguments: record.arguments) + } + + private static func nonCronIndex(processes: [DevProcess]) -> [NonCronKey: [DevProcess]] { + var result: [NonCronKey: [DevProcess]] = [:] + for process in processes { + guard let argumentVector = process.argumentVector, + let executable = canonicalPath(process.executable), + argumentVector.first.flatMap(canonicalPath) == executable else { continue } + let arguments = Array(argumentVector.dropFirst()) + var identities: [NonCronIdentity] = [.executable(executable)] + if let bundleIdentifier = nonempty(process.bundleIdentifier) { + identities.append(.bundle(bundleIdentifier)) + } + for identity in identities { + let key = NonCronKey( + identity: identity, + arguments: arguments, + launchLabel: process.launchLabel + ) + result[key, default: []].append(process) + } + } + return result + } + + private static func appendNonCronMatches( + identity: NonCronIdentity, + record: AutomationRecord, + index: [NonCronKey: [DevProcess]], + allowsNilLaunchLabel: Bool, + work: inout AutomationCorrelationWork, + matches: inout [Int32: (DevProcess, String)] + ) { + let launchLabels: [String?] = allowsNilLaunchLabel ? [record.label, nil] : [record.label] + for launchLabel in launchLabels { + let key = NonCronKey( + identity: identity, + arguments: record.arguments, + launchLabel: launchLabel + ) + for process in index[key] ?? [] { + work.nonCronCandidateEvaluations += 1 + guard hasCompatibleLaunchProvenance(process, record: record) else { continue } + matches[process.pid] = ( + process, nonCronEvidenceDetail(identity: identity.evidenceName, process: process) + ) + } + } + } + + private struct CronKey: Hashable { + let shell: String + let signature: String + } + + private static func cronLineageByPID( + processesByPID: [Int32: DevProcess], + work: inout AutomationCorrelationWork + ) -> [Int32: Bool] { + var result: [Int32: Bool] = [:] + for startPID in processesByPID.keys.sorted() where result[startPID] == nil { + var path: [Int32] = [] + var pathSet = Set() + var cursor = startPID + var resolved = false + while let process = processesByPID[cursor] { + work.ancestrySteps += 1 + if let cached = result[cursor] { + resolved = cached + break + } + guard pathSet.insert(cursor).inserted else { + resolved = false + break + } + path.append(cursor) + if canonicalPath(process.executable) == "/usr/sbin/cron" { + resolved = true + break + } + cursor = process.parentPID + } + for processID in path.reversed() { result[processID] = resolved } + } + return result + } + + private static func linkPrecedes(_ lhs: AutomationProcessLink, _ rhs: AutomationProcessLink) -> Bool { + if lhs.processIdentity.pid != rhs.processIdentity.pid { + return lhs.processIdentity.pid < rhs.processIdentity.pid + } + let lhsBirth = lhs.processIdentity.birthToken.map { ($0.seconds, $0.microseconds) } ?? (0, 0) + let rhsBirth = rhs.processIdentity.birthToken.map { ($0.seconds, $0.microseconds) } ?? (0, 0) + if lhsBirth != rhsBirth { return lhsBirth < rhsBirth } + return lhs.recordID.rawValue.utf8.lexicographicallyPrecedes(rhs.recordID.rawValue.utf8) + } + + private static func hasCompatibleLaunchProvenance( + _ process: DevProcess, + record: AutomationRecord + ) -> Bool { + if let launchLabel = process.launchLabel { + return launchLabel == record.label + } + // Direct PID 1 parentage is conservative launchd-compatibility evidence only. + // It is not a claim that the process was launched under record.label. + return process.parentPID == 1 + } + + private static func nonCronEvidenceDetail(identity: String, process: DevProcess) -> String { + if process.launchLabel != nil { + return "Exact \(identity), arguments, and launch label" + } + return "Exact \(identity) and arguments with direct PID 1 compatibility" + } + + private static func startMatchesSchedule( + _ process: DevProcess, + schedule: AutomationSchedule, + now: Date, + calendar: Calendar + ) -> Bool { + guard let elapsed = process.resourceUsage.map({ ProcessPresentation.elapsedSeconds($0.elapsedTime) }), + elapsed >= 0 else { return false } + let start = now.addingTimeInterval(-TimeInterval(elapsed)) + return schedule.triggers.contains { trigger in + guard case let .cron(expression) = trigger else { return false } + return cronExpression(expression, matches: start, calendar: calendar) + } + } + + private static func cronExpression( + _ expression: String, + matches date: Date, + calendar: Calendar + ) -> Bool { + let expanded: String + switch expression { + case "@hourly": expanded = "0 * * * *" + case "@daily": expanded = "0 0 * * *" + case "@weekly": expanded = "0 0 * * 0" + case "@monthly": expanded = "0 0 1 * *" + case "@yearly": expanded = "0 0 1 1 *" + case "@reboot": return false + default: expanded = expression + } + let fields = expanded.split(whereSeparator: \.isWhitespace).map(String.init) + guard fields.count == 5 else { return false } + let components = calendar.dateComponents([.minute, .hour, .day, .month, .weekday], from: date) + guard let minute = components.minute, let hour = components.hour, let day = components.day, + let month = components.month, let weekday = components.weekday else { return false } + let cronWeekday = weekday - 1 + guard let minuteField = parsedField(fields[0], range: 0...59, names: [:]), + let hourField = parsedField(fields[1], range: 0...23, names: [:]), + let dayField = parsedField(fields[2], range: 1...31, names: [:]), + let monthField = parsedField(fields[3], range: 1...12, names: monthNames), + let weekdayField = parsedField(fields[4], range: 0...7, names: weekdayNames) + else { return false } + let dayMatch = dayField.values.contains(day) + let weekdayMatch = weekdayField.values.contains(cronWeekday) + || (cronWeekday == 0 && weekdayField.values.contains(7)) + let dayConstraint = dayField.wildcardOrigin || weekdayField.wildcardOrigin + ? dayMatch && weekdayMatch + : dayMatch || weekdayMatch + return minuteField.values.contains(minute) + && hourField.values.contains(hour) + && monthField.values.contains(month) + && dayConstraint + } + + private struct ParsedCronField { + let values: Set + let wildcardOrigin: Bool + } + + private static func parsedField( + _ expression: String, + range: ClosedRange, + names: [String: Int] + ) -> ParsedCronField? { + let items = expression.lowercased().split(separator: ",", omittingEmptySubsequences: false) + guard !items.isEmpty else { return nil } + var values = Set() + var wildcardOrigin = false + for item in items { + let stepParts = item.split(separator: "/", omittingEmptySubsequences: false) + guard stepParts.count == 1 || stepParts.count == 2 else { return nil } + let step = stepParts.count == 2 ? Int(stepParts[1]) : 1 + guard let step, step > 0 else { return nil } + let base = String(stepParts[0]) + let bounds: ClosedRange + if base == "*" { + bounds = range + wildcardOrigin = true + } else { + let parts = base.split(separator: "-", omittingEmptySubsequences: false) + // macOS crontab(5) permits steps only after `*` or an explicit range. + guard stepParts.count == 1 || parts.count == 2 else { return nil } + let lower = valueOf(parts.first.map(String.init) ?? "", names: names) + let upper = parts.count == 2 ? valueOf(String(parts[1]), names: names) : lower + guard parts.count <= 2, let lower, let upper, + range.contains(lower), range.contains(upper), lower <= upper else { return nil } + bounds = lower...upper + } + for value in bounds where (value - bounds.lowerBound).isMultiple(of: step) { + values.insert(value) + } + } + return ParsedCronField(values: values, wildcardOrigin: wildcardOrigin) + } + + private static func valueOf(_ value: String, names: [String: Int]) -> Int? { + names[value.lowercased()] ?? Int(value) + } + + private static let monthNames = Dictionary(uniqueKeysWithValues: + zip(["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"], 1...12) + ) + private static let weekdayNames = [ + "sun": 0, "mon": 1, "tue": 2, "wed": 3, "thu": 4, "fri": 5, "sat": 6, + ] + + private static func canonicalPath(_ value: String?) -> String? { + guard let value = nonempty(value), value.hasPrefix("/") else { return nil } + return URL(fileURLWithPath: value).resolvingSymlinksInPath().standardizedFileURL.path + } + + private static func nonempty(_ value: String?) -> String? { + guard let value, !value.isEmpty else { return nil } + return value + } +} diff --git a/Sources/DevScopeCore/AutomationSource.swift b/Sources/DevScopeCore/AutomationSource.swift new file mode 100644 index 0000000..ed921b1 --- /dev/null +++ b/Sources/DevScopeCore/AutomationSource.swift @@ -0,0 +1,231 @@ +import Darwin +import CryptoKit +import Foundation + +public protocol AutomationSource: Sendable { + var kind: AutomationSourceKind { get } + func snapshot() async -> AutomationSourceSnapshot +} + +public struct AutomationCommand: Equatable, Sendable { + public let executable: String + public let arguments: [String] + public let environment: [String: String] + + public init( + executable: String, + arguments: [String], + environment: [String: String] = [:] + ) { + self.executable = executable + self.arguments = arguments + self.environment = environment + } +} + +public struct AutomationCommandResult: Equatable, Sendable { + public let status: Int32 + public let standardOutput: Data + public let standardError: Data + + public init(status: Int32, standardOutput: Data, standardError: Data) { + self.status = status + self.standardOutput = standardOutput + self.standardError = standardError + } +} + +public protocol AutomationCommandRunning: Sendable { + func run(_ command: AutomationCommand) async throws -> AutomationCommandResult +} + +public struct AutomationFileMetadata: Equatable, Sendable { + public let canonicalURL: URL + public let ownerUID: uid_t + public let isSymbolicLink: Bool + public let modificationDate: Date + public let resourceIdentifier: String? + public let permissions: Int? + + public init( + canonicalURL: URL, + ownerUID: uid_t, + isSymbolicLink: Bool, + modificationDate: Date, + resourceIdentifier: String? = nil, + permissions: Int? = nil + ) { + self.canonicalURL = canonicalURL + self.ownerUID = ownerUID + self.isSymbolicLink = isSymbolicLink + self.modificationDate = modificationDate + self.resourceIdentifier = resourceIdentifier + self.permissions = permissions + } +} + +public protocol AutomationFileSystem: Sendable { + func itemExists(at url: URL) -> Bool + func plistURLs(in directory: URL) throws -> [URL] + func read(_ url: URL) throws -> Data + func metadata(for url: URL) throws -> AutomationFileMetadata + func createDirectory(_ url: URL, permissions: Int) throws + func writeStagedFile( + nextTo url: URL, + data: Data, + permissions: Int, + authorization: AutomationDirectoryAuthorization + ) throws -> AutomationStagedFile + func replaceItem( + at destination: AutomationFileAuthorization, + with staged: AutomationStagedFile + ) throws -> AutomationFileMutationOutcome + func fileMatchesBinding( + _ file: AutomationFileAuthorization, + binding: AutomationStagedFileBinding + ) throws -> Bool + func moveToTrash(_ source: AutomationFileAuthorization) throws -> AutomationFileMutationOutcome + func removeItem(_ source: AutomationFileAuthorization) throws -> AutomationFileMutationOutcome +} + +public struct AutomationDirectoryAuthorization: Equatable, Sendable { + public let directoryURL: URL + public let resourceIdentifier: String + + public init?(directoryURL: URL, resourceIdentifier: String?) { + guard let resourceIdentifier, !resourceIdentifier.isEmpty else { return nil } + self.directoryURL = directoryURL.standardizedFileURL + self.resourceIdentifier = resourceIdentifier + } +} + +public enum AutomationFileExpectation: Equatable, Sendable { + case absent + case existing(resourceIdentifier: String) +} + +public struct AutomationFileAuthorization: Equatable, Sendable { + public let fileURL: URL + public let directory: AutomationDirectoryAuthorization + public let expectation: AutomationFileExpectation + + public init?( + fileURL: URL, + directory: AutomationDirectoryAuthorization, + expectation: AutomationFileExpectation + ) { + let fileURL = fileURL.standardizedFileURL + guard fileURL.deletingLastPathComponent().standardizedFileURL.path + == directory.directoryURL.standardizedFileURL.path + else { + return nil + } + if case .existing(let resourceIdentifier) = expectation, + resourceIdentifier.isEmpty { return nil } + self.fileURL = fileURL + self.directory = directory + self.expectation = expectation + } +} + +public struct AutomationStagedFile: Equatable, Sendable { + public let url: URL + public let authorization: AutomationFileAuthorization + public let binding: AutomationStagedFileBinding + + public init( + url: URL, + authorization: AutomationFileAuthorization, + binding: AutomationStagedFileBinding + ) { + self.url = url + self.authorization = authorization + self.binding = binding + } +} + +public struct AutomationStagedFileBinding: Equatable, Sendable { + public let data: Data + public let checksum: String + public let resourceIdentifier: String + public let ownerUID: uid_t + public let permissions: Int + public let linkCount: UInt64 + + public init( + data: Data, + resourceIdentifier: String, + ownerUID: uid_t, + permissions: Int, + linkCount: UInt64 + ) { + self.data = data + checksum = SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined() + self.resourceIdentifier = resourceIdentifier + self.ownerUID = ownerUID + self.permissions = permissions + self.linkCount = linkCount + } +} + +public struct AutomationFileMutationReceipt: Equatable, Sendable { + public let primaryFile: AutomationFileAuthorization? + public let resultURL: URL? + public let bindingVerified: Bool + + public init( + primaryFile: AutomationFileAuthorization?, + resultURL: URL?, + bindingVerified: Bool + ) { + self.primaryFile = primaryFile + self.resultURL = resultURL + self.bindingVerified = bindingVerified + } +} + +public enum AutomationFileMutationOutcome: Equatable, Sendable { + case committed(AutomationFileMutationReceipt) + case unchanged +} + +public enum AutomationFileMutationCommitState: Equatable, Sendable { + case committed + case unknown +} + +public enum AutomationFileMutationKind: Equatable, Sendable { + case replace + case remove + case trash +} + +public struct AutomationFilePartialMutation: Error, Equatable, Sendable { + public let kind: AutomationFileMutationKind + public let commitState: AutomationFileMutationCommitState + public let observedFiles: [AutomationFileAuthorization] + public let recoveryHandle: AutomationFileAuthorization? + public let recoveryHandles: [AutomationFileAuthorization] + public let resultURL: URL? + + public init( + kind: AutomationFileMutationKind, + commitState: AutomationFileMutationCommitState, + observedFiles: [AutomationFileAuthorization], + recoveryHandle: AutomationFileAuthorization?, + recoveryHandles: [AutomationFileAuthorization] = [], + resultURL: URL? + ) { + self.kind = kind + self.commitState = commitState + self.observedFiles = observedFiles + var handles: [AutomationFileAuthorization] = [] + for handle in [recoveryHandle].compactMap({ $0 }) + recoveryHandles + where !handles.contains(handle) { + handles.append(handle) + } + self.recoveryHandle = handles.first + self.recoveryHandles = handles + self.resultURL = resultURL + } +} diff --git a/Sources/DevScopeCore/BoundedSystemCommandRunner.swift b/Sources/DevScopeCore/BoundedSystemCommandRunner.swift new file mode 100644 index 0000000..88c8c78 --- /dev/null +++ b/Sources/DevScopeCore/BoundedSystemCommandRunner.swift @@ -0,0 +1,313 @@ +import Darwin +import Foundation + +struct BoundedSystemCommandResult: Equatable, Sendable { + let status: Int32 + let standardOutput: Data + let standardError: Data + var processIdentifier: Int32? = nil +} + +enum BoundedSystemCommandError: Error, Equatable { + case invalidCommand + case launchFailed + case outputLimitExceeded + case executionTimedOut +} + +protocol SystemCommandRunning: Sendable { + func run(executableURL: URL, arguments: [String]) throws -> BoundedSystemCommandResult +} + +struct BoundedSystemCommandRunner: SystemCommandRunning { + private static let defaultMaximumCapturedBytes = 8 * 1_024 * 1_024 + + private let maximumCapturedBytes: Int + private let executionTimeout: TimeInterval + private let terminationGraceInterval: TimeInterval + + init( + maximumCapturedBytes: Int = Self.defaultMaximumCapturedBytes, + executionTimeout: TimeInterval = 10, + terminationGraceInterval: TimeInterval = 0.5 + ) { + self.maximumCapturedBytes = max(0, maximumCapturedBytes) + self.executionTimeout = max(0, executionTimeout) + self.terminationGraceInterval = max(0, terminationGraceInterval) + } + + func run(executableURL: URL, arguments: [String]) throws -> BoundedSystemCommandResult { + let executable = executableURL.path + guard executable.hasPrefix("/"), + !executable.contains("\0"), + arguments.allSatisfy({ !$0.contains("\0") }) else { + throw BoundedSystemCommandError.invalidCommand + } + + let spawned = try Self.spawn(executable: executable, arguments: arguments) + let group = DispatchGroup() + let output = LockedBox?>(nil) + let error = LockedBox?>(nil) + let status = LockedBox?>(nil) + let outputLimitExceeded = LockedBox(false) + + group.enter() + DispatchQueue.global(qos: .userInitiated).async { + output.value = Result { + try Self.drain( + spawned.standardOutput, + maximumCapturedBytes: maximumCapturedBytes, + onLimitExceeded: { outputLimitExceeded.value = true } + ) + } + group.leave() + } + group.enter() + DispatchQueue.global(qos: .userInitiated).async { + error.value = Result { + try Self.drain( + spawned.standardError, + maximumCapturedBytes: maximumCapturedBytes, + onLimitExceeded: { outputLimitExceeded.value = true } + ) + } + group.leave() + } + group.enter() + DispatchQueue.global(qos: .userInitiated).async { + status.value = Result { try Self.reap(spawned.pid) } + group.leave() + } + + var remainingWait = executionTimeout + var completed = false + while !completed, !outputLimitExceeded.value { + if group.wait(timeout: .now()) == .success { + completed = true + break + } + guard remainingWait > 0 else { break } + let interval = min(remainingWait, 0.01) + completed = group.wait(timeout: .now() + interval) == .success + remainingWait -= interval + } + guard completed else { + Self.terminateAndDrain( + spawned, + group: group, + terminationGraceInterval: terminationGraceInterval + ) + throw outputLimitExceeded.value + ? BoundedSystemCommandError.outputLimitExceeded + : BoundedSystemCommandError.executionTimedOut + } + + let capturedOutput = try output.value?.get() ?? { throw BoundedSystemCommandError.launchFailed }() + let capturedError = try error.value?.get() ?? { throw BoundedSystemCommandError.launchFailed }() + let waitStatus = try status.value?.get() ?? { throw BoundedSystemCommandError.launchFailed }() + guard !capturedOutput.exceededLimit, !capturedError.exceededLimit else { + throw BoundedSystemCommandError.outputLimitExceeded + } + return BoundedSystemCommandResult( + status: Self.commandStatus(fromWaitStatus: waitStatus), + standardOutput: capturedOutput.data, + standardError: capturedError.data, + processIdentifier: spawned.pid + ) + } + + private struct SpawnedCommand: Sendable { + let pid: pid_t + let standardOutput: FileHandle + let standardError: FileHandle + } + + private struct CapturedStream: Sendable { + let data: Data + let exceededLimit: Bool + } + + private static func spawn(executable: String, arguments: [String]) throws -> SpawnedCommand { + var output = [Int32](repeating: -1, count: 2) + var error = [Int32](repeating: -1, count: 2) + guard output.withUnsafeMutableBufferPointer({ Darwin.pipe($0.baseAddress!) }) == 0 else { + throw BoundedSystemCommandError.launchFailed + } + guard error.withUnsafeMutableBufferPointer({ Darwin.pipe($0.baseAddress!) }) == 0 else { + close(output[0]); close(output[1]) + throw BoundedSystemCommandError.launchFailed + } + for descriptor in output + error { + let existingFlags = fcntl(descriptor, F_GETFD) + guard existingFlags >= 0, + fcntl(descriptor, F_SETFD, existingFlags | FD_CLOEXEC) == 0 else { + for descriptor in output + error { close(descriptor) } + throw BoundedSystemCommandError.launchFailed + } + } + + var actions: posix_spawn_file_actions_t? = nil + var attributes: posix_spawnattr_t? = nil + guard posix_spawn_file_actions_init(&actions) == 0 else { + for descriptor in output + error { close(descriptor) } + throw BoundedSystemCommandError.launchFailed + } + defer { posix_spawn_file_actions_destroy(&actions) } + guard posix_spawnattr_init(&attributes) == 0 else { + for descriptor in output + error { close(descriptor) } + throw BoundedSystemCommandError.launchFailed + } + defer { posix_spawnattr_destroy(&attributes) } + + var result = posix_spawn_file_actions_addopen( + &actions, STDIN_FILENO, "/dev/null", O_RDONLY, 0 + ) + if result == 0 { result = posix_spawn_file_actions_adddup2(&actions, output[1], STDOUT_FILENO) } + if result == 0 { result = posix_spawn_file_actions_adddup2(&actions, error[1], STDERR_FILENO) } + if result == 0 { result = posix_spawn_file_actions_addclose(&actions, output[0]) } + if result == 0 { result = posix_spawn_file_actions_addclose(&actions, error[0]) } + if result == 0 { result = posix_spawn_file_actions_addclose(&actions, output[1]) } + if result == 0 { result = posix_spawn_file_actions_addclose(&actions, error[1]) } + + var emptyMask = sigset_t() + var defaultSignals = sigset_t() + sigemptyset(&emptyMask) + sigemptyset(&defaultSignals) + for signal in [SIGINT, SIGHUP, SIGQUIT, SIGTERM, SIGPIPE] { + sigaddset(&defaultSignals, signal) + } + if result == 0 { result = posix_spawnattr_setsigmask(&attributes, &emptyMask) } + if result == 0 { result = posix_spawnattr_setsigdefault(&attributes, &defaultSignals) } + if result == 0 { result = posix_spawnattr_setpgroup(&attributes, 0) } + let flags = Int16( + POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF + | POSIX_SPAWN_CLOEXEC_DEFAULT + ) + if result == 0 { result = posix_spawnattr_setflags(&attributes, flags) } + guard result == 0 else { + for descriptor in output + error { close(descriptor) } + throw BoundedSystemCommandError.launchFailed + } + + let argv = [executable] + arguments + let environment = [ + "HOME=\(FileManager.default.homeDirectoryForCurrentUser.path)", + "LC_ALL=C", + "LOGNAME=\(NSUserName())", + "PATH=/usr/bin:/bin:/usr/sbin:/sbin", + "USER=\(NSUserName())", + ] + var pid: pid_t = 0 + let spawnResult = withMutableCStringArray(argv) { argvPointer in + withMutableCStringArray(environment) { environmentPointer in + posix_spawn( + &pid, + executable, + &actions, + &attributes, + argvPointer, + environmentPointer + ) + } + } + close(output[1]) + close(error[1]) + guard spawnResult == 0, pid > 1 else { + close(output[0]) + close(error[0]) + throw BoundedSystemCommandError.launchFailed + } + return SpawnedCommand( + pid: pid, + standardOutput: FileHandle(fileDescriptor: output[0], closeOnDealloc: true), + standardError: FileHandle(fileDescriptor: error[0], closeOnDealloc: true) + ) + } + + private static func withMutableCStringArray( + _ strings: [String], + _ body: (UnsafeMutablePointer?>) -> Result + ) -> Result { + let pointers = strings.map { strdup($0)! } + defer { + for pointer in pointers { free(pointer) } + } + var terminated = pointers.map(Optional.some) + terminated.append(nil) + return terminated.withUnsafeMutableBufferPointer { buffer in + body(buffer.baseAddress!) + } + } + + private static func drain( + _ handle: FileHandle, + maximumCapturedBytes: Int, + onLimitExceeded: @Sendable () -> Void + ) throws -> CapturedStream { + defer { try? handle.close() } + var captured = Data() + var exceededLimit = false + while let chunk = try handle.read(upToCount: 64 * 1_024), !chunk.isEmpty { + let available = maximumCapturedBytes - captured.count + if available > 0 { + captured.append(chunk.prefix(available)) + } + if chunk.count > available, !exceededLimit { + exceededLimit = true + onLimitExceeded() + } + } + return CapturedStream(data: captured, exceededLimit: exceededLimit) + } + + private static func reap(_ pid: pid_t) throws -> Int32 { + var status: Int32 = 0 + while true { + let result = waitpid(pid, &status, 0) + if result == pid { return status } + if result == -1, errno == EINTR { continue } + throw BoundedSystemCommandError.launchFailed + } + } + + private static func terminate(processGroupID: pid_t, signal: Int32) { + guard processGroupID > 1 else { return } + _ = Darwin.kill(-processGroupID, signal) + } + + private static func terminateAndDrain( + _ command: SpawnedCommand, + group: DispatchGroup, + terminationGraceInterval: TimeInterval + ) { + terminate(processGroupID: command.pid, signal: SIGTERM) + if group.wait(timeout: .now() + terminationGraceInterval) == .timedOut { + terminate(processGroupID: command.pid, signal: SIGKILL) + _ = Darwin.kill(command.pid, SIGKILL) + } + if group.wait(timeout: .now() + 1) == .timedOut { + try? command.standardOutput.close() + try? command.standardError.close() + _ = group.wait(timeout: .now() + 1) + } + } + + private static func commandStatus(fromWaitStatus status: Int32) -> Int32 { + let signal = status & 0x7f + return signal == 0 ? (status >> 8) & 0xff : 128 + signal + } +} + +private final class LockedBox: @unchecked Sendable { + private let lock = NSLock() + private var storedValue: Value + + init(_ value: Value) { + storedValue = value + } + + var value: Value { + get { lock.withLock { storedValue } } + set { lock.withLock { storedValue = newValue } } + } +} diff --git a/Sources/DevScopeCore/CronAutomationSource.swift b/Sources/DevScopeCore/CronAutomationSource.swift new file mode 100644 index 0000000..30b164a --- /dev/null +++ b/Sources/DevScopeCore/CronAutomationSource.swift @@ -0,0 +1,461 @@ +import CryptoKit +import Darwin +import Foundation + +public struct CronDocument: Equatable, Sendable { + public let originalText: String + public let originalLines: [String] + public let environmentAssignments: [CronEnvironmentAssignment] + public let environment: [String: String] + public let entries: [CronEntry] + public let invalidLines: [CronInvalidLine] +} + +public enum CronDocumentChecksum { + public static func normalizedData(_ data: Data) -> Data? { + guard let text = String(data: data, encoding: .utf8) else { return nil } + var normalized = text + .replacingOccurrences(of: "\r\n", with: "\n") + .replacingOccurrences(of: "\r", with: "\n") + while normalized.hasSuffix("\n") { + normalized.removeLast() + } + normalized.append("\n") + return Data(normalized.utf8) + } + + public static func checksum(_ data: Data) -> String? { + normalizedData(data).map { + SHA256.hash(data: $0).map { String(format: "%02x", $0) }.joined() + } + } +} + +public struct CronEnvironmentAssignment: Equatable, Sendable { + public let lineNumber: Int + public let name: String + public let value: String +} + +public struct CronInvalidLine: Equatable, Sendable { + public let lineNumber: Int + public let content: String + public let reason: String +} + +public struct CronEntry: Identifiable, Equatable, Sendable { + public let id: String + public let lineNumber: Int + public let scheduleExpression: String + public let command: String + public let isEnabled: Bool + public let environment: [String: String] + public let schedule: AutomationSchedule +} + +public enum CronParser { + private static let disabledPrefix = "# devscope-disabled: " + + public static func parse(_ text: String) -> CronDocument { + let lines = text.components(separatedBy: "\n") + var environment: [String: String] = [:] + var environmentAssignments: [CronEnvironmentAssignment] = [] + var entries: [CronEntry] = [] + var invalidLines: [CronInvalidLine] = [] + var scheduleOccurrences: [String: Int] = [:] + + for (offset, originalLine) in lines.enumerated() { + let lineNumber = offset + 1 + let trimmed = originalLine.trimmingCharacters(in: .whitespacesAndNewlines) + guard !trimmed.isEmpty else { continue } + + let isDisabled = trimmed.hasPrefix(disabledPrefix) + if trimmed.hasPrefix("#"), !isDisabled { continue } + + if let assignment = environmentAssignment(in: trimmed) { + environment[assignment.name] = assignment.value + environmentAssignments.append(CronEnvironmentAssignment( + lineNumber: lineNumber, + name: assignment.name, + value: assignment.value + )) + continue + } + + let isEnabled = !isDisabled + let candidate = isEnabled ? trimmed : String(trimmed.dropFirst(disabledPrefix.count)) + guard let parsed = parseEntry(candidate) else { + invalidLines.append(CronInvalidLine( + lineNumber: lineNumber, + content: originalLine, + reason: "Invalid crontab line" + )) + continue + } + + let occurrence = scheduleOccurrences[parsed.expression, default: 0] + scheduleOccurrences[parsed.expression] = occurrence + 1 + entries.append(CronEntry( + id: stableEntryID(scheduleExpression: parsed.expression, occurrence: occurrence), + lineNumber: lineNumber, + scheduleExpression: parsed.expression, + command: parsed.command, + isEnabled: isEnabled, + environment: environment, + schedule: parsed.schedule + )) + } + + return CronDocument( + originalText: text, + originalLines: lines, + environmentAssignments: environmentAssignments, + environment: environment, + entries: entries, + invalidLines: invalidLines + ) + } + + public static func recordID(for entry: CronEntry, ownerUID: uid_t) -> AutomationRecord.ID { + AutomationRecord.ID( + source: .crontab, + ownerUID: ownerUID, + label: "cron-entry", + sourcePath: "/.devscope/current-user-crontab/\(entry.id)" + ) + } + + private static func environmentAssignment(in line: String) -> (name: String, value: String)? { + guard let equals = line.firstIndex(of: "=") else { return nil } + let rawName = line[..= 2, rawName.last == quote else { return nil } + name = String(rawName.dropFirst().dropLast()) + } else { + guard !rawName.contains(where: \.isWhitespace), + !rawName.contains("\""), + !rawName.contains("'") + else { return nil } + name = String(rawName) + } + guard !name.isEmpty else { return nil } + + var value = line[line.index(after: equals)...] + .trimmingCharacters(in: .whitespacesAndNewlines) + if let quote = value.first, quote == "\"" || quote == "'" { + guard value.count >= 2, value.last == quote else { return nil } + value = String(value.dropFirst().dropLast()) + } + return (name, String(value)) + } + + private static func parseEntry( + _ line: String + ) -> (expression: String, command: String, schedule: AutomationSchedule)? { + let firstFields = line.split(maxSplits: 1, whereSeparator: \.isWhitespace) + guard !firstFields.isEmpty else { return nil } + + if firstFields[0].hasPrefix("@") { + guard firstFields.count == 2 else { return nil } + let expression = String(firstFields[0]) + let summary: String + switch expression { + case "@reboot": summary = "At startup" + case "@hourly": summary = "Hourly" + case "@daily": summary = "Daily" + case "@weekly": summary = "Weekly" + case "@monthly": summary = "Monthly" + case "@yearly": summary = "Yearly" + default: return nil + } + return ( + expression, + String(firstFields[1]), + AutomationSchedule(triggers: [.cron(expression)], summary: summary) + ) + } + + let fields = line.split(maxSplits: 5, whereSeparator: \.isWhitespace) + guard fields.count == 6 else { return nil } + let specifications = [ + CronFieldSpecification(minimum: 0, maximum: 59), + CronFieldSpecification(minimum: 0, maximum: 23), + CronFieldSpecification(minimum: 1, maximum: 31), + CronFieldSpecification( + minimum: 1, + maximum: 12, + names: ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"] + ), + CronFieldSpecification( + minimum: 0, + maximum: 7, + names: ["sun", "mon", "tue", "wed", "thu", "fri", "sat"] + ), + ] + guard zip(fields.prefix(5), specifications).allSatisfy({ field, specification in + isValid(field: String(field), specification: specification) + }) else { return nil } + + let expression = fields.prefix(5).joined(separator: " ") + let summary = expression == "0 9 * * 1" ? "Mondays at 09:00" : "Cron: \(expression)" + return ( + expression, + String(fields[5]), + AutomationSchedule(triggers: [.cron(expression)], summary: summary) + ) + } + + private struct CronFieldSpecification { + let minimum: Int + let maximum: Int + var names: Set = [] + } + + private static func isValid( + field: String, + specification: CronFieldSpecification + ) -> Bool { + if specification.names.contains(field.lowercased()) { + return true + } + + let items = field.split(separator: ",", omittingEmptySubsequences: false) + guard !items.isEmpty, + items.count == 1 || !items.contains(where: { $0.contains("*") }) + else { return false } + return items.allSatisfy { item in + let stepParts = item.split(separator: "/", omittingEmptySubsequences: false) + guard stepParts.count == 1 || stepParts.count == 2 else { return false } + let base = String(stepParts[0]) + + if stepParts.count == 2 { + guard isASCIIDigits(stepParts[1]), + let step = Int(stepParts[1]), step > 0, + base == "*" || base.contains("-") + else { return false } + } + + if base == "*" { return true } + let rangeParts = base.split(separator: "-", omittingEmptySubsequences: false) + if rangeParts.count == 2 { + guard isASCIIDigits(rangeParts[0]), + isASCIIDigits(rangeParts[1]), + let lower = Int(rangeParts[0]), + let upper = Int(rangeParts[1]), + specification.minimum...specification.maximum ~= lower, + specification.minimum...specification.maximum ~= upper + else { return false } + return lower <= upper + } + guard rangeParts.count == 1, + isASCIIDigits(base), + let value = Int(base) + else { return false } + return specification.minimum...specification.maximum ~= value + } + } + + private static func isASCIIDigits(_ value: S) -> Bool { + !value.isEmpty && value.allSatisfy { character in + character >= "0" && character <= "9" + } + } + + private static func stableEntryID(scheduleExpression: String, occurrence: Int) -> String { + var identity = Data("devscope-crontab-entry\0".utf8) + identity.append(Data(scheduleExpression.utf8)) + identity.append(0) + identity.append(Data(String(occurrence).utf8)) + return SHA256.hash(data: identity) + .map { String(format: "%02x", $0) } + .joined() + } +} + +public struct CronAutomationSource: AutomationSource { + public let kind: AutomationSourceKind = .crontab + + private let commandRunner: any AutomationCommandRunning + private let currentUID: uid_t + private let currentUsername: String + + public init( + commandRunner: any AutomationCommandRunning, + currentUID: uid_t, + currentUsername: String + ) { + self.commandRunner = commandRunner + self.currentUID = currentUID + self.currentUsername = currentUsername + } + + public func snapshot() async -> AutomationSourceSnapshot { + let result: AutomationCommandResult + do { + result = try await commandRunner.run(AutomationCommand( + executable: "/usr/bin/crontab", + arguments: ["-l"], + environment: ["LC_ALL": "C"] + )) + } catch { + return .failed( + kind: kind, + message: "Unable to list the current-user crontab." + ) + } + + if result.status == 1, + result.standardOutput.isEmpty, + Self.isNoCrontabDiagnostic( + result.standardError, + currentUsername: currentUsername + ) + { + return .healthy(kind: kind, records: []) + } + guard result.status == 0 else { + return .failed( + kind: kind, + message: "Unable to list the current-user crontab (exit status \(result.status))." + ) + } + guard let text = String(data: result.standardOutput, encoding: .utf8) else { + return .failed( + kind: kind, + message: "The current-user crontab is not valid UTF-8." + ) + } + + let document = CronParser.parse(text) + guard let documentChecksum = CronDocumentChecksum.checksum(result.standardOutput) else { + return .failed( + kind: kind, + message: "The current-user crontab is not valid UTF-8." + ) + } + let entryRecords = document.entries.enumerated().map { index, entry in + Self.record( + for: entry, + index: index, + ownerUID: currentUID, + documentChecksum: documentChecksum + ) + } + let invalidRecords = document.invalidLines.map { + Self.invalidRecord(for: $0, ownerUID: currentUID, documentChecksum: documentChecksum) + } + let records = entryRecords + invalidRecords + guard !invalidRecords.isEmpty else { + return .healthy(kind: kind, records: records) + } + + let count = invalidRecords.count + return AutomationSourceSnapshot( + records: records, + health: AutomationSourceHealth( + kind: kind, + state: .partial, + message: "\(count) invalid crontab line\(count == 1 ? "." : "s.")", + refreshedAt: Date() + ) + ) + } + + private static func record( + for entry: CronEntry, + index: Int, + ownerUID: uid_t, + documentChecksum: String + ) -> AutomationRecord { + let label = "Cron entry \(index + 1)" + return AutomationRecord( + id: CronParser.recordID(for: entry, ownerUID: ownerUID), + kind: .cron, + sourceKind: .crontab, + label: label, + displayName: label, + providerBundleIdentifier: nil, + ownerUID: ownerUID, + ownership: .user, + executable: nil, + arguments: [], + commandSignature: entry.command, + environment: entry.environment, + workingDirectory: nil, + schedule: entry.schedule, + sourceURL: nil, + sourceChecksum: documentChecksum, + enabledState: entry.isEnabled ? .enabled : .disabled, + loadState: .unknown, + approvalState: .notApplicable, + state: entry.isEnabled ? .idle : .disabled, + evidence: [AutomationEvidence( + strength: .strong, + source: "current-user crontab", + detail: "Listed by the current-user crontab source" + )], + capabilities: [], + validationFindings: [] + ) + } + + private static func invalidRecord( + for invalidLine: CronInvalidLine, + ownerUID: uid_t, + documentChecksum: String + ) -> AutomationRecord { + let label = "Invalid crontab line \(invalidLine.lineNumber)" + return AutomationRecord( + id: AutomationRecord.ID( + source: .crontab, + ownerUID: ownerUID, + label: "invalid-cron-entry", + sourcePath: "/.devscope/current-user-crontab/invalid/\(invalidLine.lineNumber)" + ), + kind: .cron, + sourceKind: .crontab, + label: label, + displayName: label, + providerBundleIdentifier: nil, + ownerUID: ownerUID, + ownership: .user, + executable: nil, + arguments: [], + environment: [:], + workingDirectory: nil, + schedule: AutomationSchedule( + triggers: [.cron("invalid")], + summary: "Invalid cron schedule" + ), + sourceURL: nil, + sourceChecksum: documentChecksum, + enabledState: .unknown, + loadState: .unknown, + approvalState: .notApplicable, + state: .invalid, + evidence: [AutomationEvidence( + strength: .strong, + source: "current-user crontab", + detail: "The listed crontab contains an invalid line" + )], + capabilities: [], + validationFindings: ["Invalid crontab line"] + ) + } + + private static func isNoCrontabDiagnostic( + _ data: Data, + currentUsername: String + ) -> Bool { + guard !currentUsername.isEmpty, + !currentUsername.contains(where: \.isNewline), + let diagnostic = String(data: data, encoding: .utf8) + else { return false } + + let expected = "crontab: no crontab for \(currentUsername)" + return diagnostic == expected + || diagnostic == expected + "\n" + || diagnostic == expected + "\r\n" + } +} diff --git a/Sources/DevScopeCore/DevProcess.swift b/Sources/DevScopeCore/DevProcess.swift new file mode 100644 index 0000000..aebe982 --- /dev/null +++ b/Sources/DevScopeCore/DevProcess.swift @@ -0,0 +1,221 @@ +import Foundation + +public struct ProcessBirthToken: Equatable, Hashable, Sendable { + public let seconds: UInt64 + public let microseconds: UInt64 + + public init(seconds: UInt64, microseconds: UInt64) { + self.seconds = seconds + self.microseconds = microseconds + } +} + +public struct DevProcess: Identifiable, Equatable, Hashable, Sendable { + public let pid: Int32 + public let parentPID: Int32 + public let executable: String + public let command: String + public let argumentVector: [String]? + public let currentDirectory: String? + public let resourceUsage: DevProcessResourceUsage? + public let birthToken: ProcessBirthToken? + public let bundleIdentifier: String? + /// Exact service-manager metadata when supplied by a trusted scanner boundary. + /// Nil does not imply a label and callers must never infer one from names or paths. + public let launchLabel: String? + + public var id: Int32 { pid } + + public init( + pid: Int32, + parentPID: Int32, + executable: String, + command: String, + argumentVector: [String]? = nil, + currentDirectory: String? = nil, + resourceUsage: DevProcessResourceUsage? = nil, + birthToken: ProcessBirthToken? = nil, + bundleIdentifier: String? = nil, + launchLabel: String? = nil + ) { + self.pid = pid + self.parentPID = parentPID + self.executable = executable + self.command = command + self.argumentVector = argumentVector + self.currentDirectory = currentDirectory + self.resourceUsage = resourceUsage + self.birthToken = birthToken + self.bundleIdentifier = bundleIdentifier + self.launchLabel = launchLabel + } + + public var executableName: String { + URL(fileURLWithPath: executable).lastPathComponent + } +} + +enum ProcessSnapshotNormalization { + static func newestUnambiguous(_ processes: [DevProcess]) -> [DevProcess] { + let grouped = Dictionary(grouping: processes, by: \.pid) + return grouped.keys.sorted().compactMap { processID in + guard let rows = grouped[processID] else { return nil } + let newestBirth = rows.compactMap(\.birthToken).max(by: birthPrecedes) + let candidates = rows.filter { $0.birthToken == newestBirth } + guard let first = candidates.first, + candidates.dropFirst().allSatisfy({ $0 == first }) else { return nil } + return first + } + } + + private static func birthPrecedes(_ lhs: ProcessBirthToken, _ rhs: ProcessBirthToken) -> Bool { + (lhs.seconds, lhs.microseconds) < (rhs.seconds, rhs.microseconds) + } +} + +public struct DevProcessResourceUsage: Equatable, Hashable, Sendable { + public let cpuPercent: Double + public let residentMemoryBytes: Int64 + public let elapsedTime: String + + public init(cpuPercent: Double, residentMemoryBytes: Int64, elapsedTime: String) { + self.cpuPercent = cpuPercent + self.residentMemoryBytes = residentMemoryBytes + self.elapsedTime = elapsedTime + } +} + +public struct DevProcessMetricSample: Identifiable, Equatable, Sendable { + public let id: UUID + public let timestamp: Date + public let cpuPercent: Double + public let residentMemoryBytes: Int64 + public let gpuPercent: Double? + + public init( + id: UUID = UUID(), + timestamp: Date, + cpuPercent: Double, + residentMemoryBytes: Int64, + gpuPercent: Double? = nil + ) { + self.id = id + self.timestamp = timestamp + self.cpuPercent = cpuPercent + self.residentMemoryBytes = residentMemoryBytes + self.gpuPercent = gpuPercent + } +} + +public struct DevProcessTag: Identifiable, Equatable, Hashable, Sendable { + public let id: String + public let title: String + public let symbolName: String + + public init(id: String, title: String, symbolName: String) { + self.id = id + self.title = title + self.symbolName = symbolName + } + + public static let notebook = DevProcessTag(id: "notebook", title: "Notebook", symbolName: "book") + public static let training = DevProcessTag(id: "training", title: "Training", symbolName: "chart.line.uptrend.xyaxis") + public static let llm = DevProcessTag(id: "llm", title: "LLM", symbolName: "brain.head.profile") + public static let inference = DevProcessTag(id: "inference", title: "Inference", symbolName: "brain") + public static let llmServer = DevProcessTag(id: "llm-server", title: "LLM Server", symbolName: "text.bubble") + public static let vectorDB = DevProcessTag(id: "vector-db", title: "Vector DB", symbolName: "square.stack.3d.up") + public static let api = DevProcessTag(id: "api", title: "API", symbolName: "network") + public static let mcp = DevProcessTag(id: "mcp", title: "MCP", symbolName: "point.3.connected.trianglepath.dotted") + public static let experiment = DevProcessTag(id: "experiment", title: "Experiment", symbolName: "flask") + public static let dataApp = DevProcessTag(id: "data-app", title: "Data App", symbolName: "chart.bar") +} + +public enum DevRuntimeKind: String, CaseIterable, Identifiable, Sendable { + case javascript = "JavaScript" + case python = "Python" + case swift = "Swift" + case rust = "Rust" + case go = "Go" + case flutter = "Flutter" + case java = "Java" + case database = "Database" + case container = "Container" + case webServer = "Web Server" + case ai = "AI" + case mcp = "MCP" + case browser = "Browser" + case macApp = "App" + case backgroundAgent = "Background Agent" + case systemService = "System Service" + case shell = "Shell" + case other = "Other" + + public var id: String { rawValue } + + public var symbolName: String { + switch self { + case .javascript: + "curlybraces" + case .python: + "terminal" + case .swift: + "swift" + case .rust: + "hammer" + case .go: + "bolt.horizontal" + case .flutter: + "iphone.and.arrow.forward" + case .java: + "cup.and.saucer" + case .database: + "cylinder" + case .container: + "shippingbox" + case .webServer: + "network" + case .ai: + "brain" + case .mcp: + "point.3.connected.trianglepath.dotted" + case .browser: + "safari" + case .macApp: + "macwindow" + case .backgroundAgent: + "gearshape.2" + case .systemService: + "server.rack" + case .shell: + "terminal.fill" + case .other: + "questionmark.circle" + } + } +} + +public struct DevProcessClassification: Equatable, Sendable { + public let kind: DevRuntimeKind + public let displayName: String + public let projectHint: String? + public let tags: [DevProcessTag] + + public init(kind: DevRuntimeKind, displayName: String, projectHint: String?, tags: [DevProcessTag] = []) { + self.kind = kind + self.displayName = displayName + self.projectHint = projectHint + self.tags = tags + } +} + +public struct ClassifiedDevProcess: Identifiable, Equatable, Sendable { + public let process: DevProcess + public let classification: DevProcessClassification + + public var id: Int32 { process.pid } + + public init(process: DevProcess, classification: DevProcessClassification) { + self.process = process + self.classification = classification + } +} diff --git a/Sources/DevScopeCore/FullDiskAccessGuidance.swift b/Sources/DevScopeCore/FullDiskAccessGuidance.swift new file mode 100644 index 0000000..325a4a6 --- /dev/null +++ b/Sources/DevScopeCore/FullDiskAccessGuidance.swift @@ -0,0 +1,67 @@ +import Foundation + +public enum FullDiskAccessSettingsRoute { + public static let deepLink = + "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles" + + @discardableResult + public static func open( + deepLink: String = deepLink, + using openURL: (URL) -> Bool, + fallback: () -> Void + ) -> Bool { + guard let url = URL(string: deepLink), openURL(url) else { + fallback() + return false + } + return true + } +} + +public struct FullDiskAccessGuidance: Equatable, Sendable { + public let appPath: String + public let isSandboxed: Bool + + public init(appPath: String, isSandboxed: Bool) { + self.appPath = appPath + self.isSandboxed = isSandboxed + } + + public var isInstalledInApplications: Bool { + appPath.hasPrefix("/Applications/") + } + + public var title: String { + isSandboxed + ? "This sandbox build cannot use Full Disk Access" + : "Add DevScope to Full Disk Access" + } + + public var detail: String { + if isSandboxed { + if !isInstalledInApplications { + return + "This validation/development build is running at \(appPath) and uses App Sandbox. Install the full DevScope build in Applications; Full Disk Access cannot override App Sandbox for process inspection." + } + return + "Install the full DevScope build in Applications. Full Disk Access cannot override App Sandbox for process inspection." + } + if !isInstalledInApplications { + return + "Install DevScope in Applications first so macOS keeps permission attached to the app you launch. Current app: \(appPath)" + } + return "macOS requires you to add and enable the exact DevScope app manually." + } + + public var steps: [String] { + guard !isSandboxed else { return [] } + return [ + "Open Full Disk Access.", + "Click + and authenticate.", + "Select \(appPath).", + "Enable the DevScope toggle.", + "Quit and reopen DevScope.", + "Return here and choose Check Access.", + ] + } +} diff --git a/Sources/DevScopeCore/GPUMetricProvider.swift b/Sources/DevScopeCore/GPUMetricProvider.swift new file mode 100644 index 0000000..30df1dd --- /dev/null +++ b/Sources/DevScopeCore/GPUMetricProvider.swift @@ -0,0 +1,85 @@ +import Foundation + +public struct DevGPUMetric: Equatable, Sendable { + public let utilizationPercent: Double + public let modelName: String? + + public init(utilizationPercent: Double, modelName: String? = nil) { + self.utilizationPercent = utilizationPercent + self.modelName = modelName + } +} + +public protocol GPUMetricProviding: Sendable { + func snapshot() throws -> DevGPUMetric? +} + +public struct SystemGPUMetricProvider: GPUMetricProviding { + private let executableURL: URL + private let commandRunner: any SystemCommandRunning + + public init(executableURL: URL = URL(fileURLWithPath: "/usr/sbin/ioreg")) { + self.executableURL = executableURL + commandRunner = BoundedSystemCommandRunner() + } + + init( + executableURL: URL, + commandRunner: any SystemCommandRunning + ) { + self.executableURL = executableURL + self.commandRunner = commandRunner + } + + public func snapshot() throws -> DevGPUMetric? { + let result = try commandRunner.run( + executableURL: executableURL, + arguments: ["-r", "-c", "AGXAccelerator", "-d", "1"] + ) + + guard result.status == 0 else { + return nil + } + + return Self.parse(String(decoding: result.standardOutput, as: UTF8.self)) + } + + public static func parse(_ output: String) -> DevGPUMetric? { + guard let utilization = number(after: #""Device Utilization %"\s*=\s*"#, in: output) else { + return nil + } + + return DevGPUMetric( + utilizationPercent: min(max(utilization, 0), 100), + modelName: quotedValue(after: #""model"\s*=\s*"#, in: output) + ) + } + + private static func number(after prefixPattern: String, in output: String) -> Double? { + guard let regex = try? NSRegularExpression(pattern: prefixPattern + #"([0-9]+(?:\.[0-9]+)?)"#) else { + return nil + } + let range = NSRange(output.startIndex.. 1, + let matchRange = Range(match.range(at: 1), in: output) else { + return nil + } + + return Double(output[matchRange]) + } + + private static func quotedValue(after prefixPattern: String, in output: String) -> String? { + guard let regex = try? NSRegularExpression(pattern: prefixPattern + #""([^"]+)""#) else { + return nil + } + let range = NSRange(output.startIndex.. 1, + let matchRange = Range(match.range(at: 1), in: output) else { + return nil + } + + return String(output[matchRange]) + } +} diff --git a/Sources/DevScopeCore/InterfacePresentation.swift b/Sources/DevScopeCore/InterfacePresentation.swift new file mode 100644 index 0000000..2a933f1 --- /dev/null +++ b/Sources/DevScopeCore/InterfacePresentation.swift @@ -0,0 +1,74 @@ +import Foundation + +public enum DevScopeWorkspaceMode: String, CaseIterable, Sendable { + case processes + case automations +} + +public enum AutomationPresentationSettings { + public static let defaultLongRunningThreshold: TimeInterval = 14_400 + public static let minimumLongRunningThreshold: TimeInterval = 3_600 + public static let maximumLongRunningThreshold: TimeInterval = 604_800 + public static let longRunningThresholdStep: TimeInterval = 3_600 + + public static let longRunningThresholdSecondsKey = "longRunningThresholdSeconds" + public static let includeAppleSystemServicesKey = "includeAppleSystemServices" + public static let selectedWorkspaceModeKey = "selectedWorkspaceMode" + public static let notifyLongRunningAutomationKey = "notifyLongRunningAutomation" + public static let notifyUnexpectedAutomationExitKey = "notifyUnexpectedAutomationExit" + public static let notifyRepeatedAutomationFailureKey = "notifyRepeatedAutomationFailure" + + public static func normalizedThreshold(_ value: TimeInterval) -> TimeInterval { + guard value.isFinite else { return defaultLongRunningThreshold } + let clamped = min(max(value, minimumLongRunningThreshold), maximumLongRunningThreshold) + return (clamped / longRunningThresholdStep).rounded() * longRunningThresholdStep + } +} + +public enum InterfaceSelectionDirection: Sendable { + case previous + case next +} + +public enum InterfacePresentation { + public static let defaultFocusLimit = 6 + + public static func visibleFocusWorkflows( + _ workflows: [DevWorkflow], + selectedID: String, + showsAll: Bool + ) -> [DevWorkflow] { + guard !showsAll else { + return workflows + } + + var visible = Array(workflows.prefix(defaultFocusLimit)) + if let selected = workflows.first(where: { $0.id == selectedID }), + !visible.contains(where: { $0.id == selected.id }) { + visible.append(selected) + } + return visible + } + + public static func movedSelection( + in visibleIDs: [Int32], + current: Int32?, + direction: InterfaceSelectionDirection + ) -> Int32? { + guard !visibleIDs.isEmpty else { + return nil + } + + guard let current, let currentIndex = visibleIDs.firstIndex(of: current) else { + return direction == .next ? visibleIDs.first : visibleIDs.last + } + + let destination = switch direction { + case .previous: + max(visibleIDs.startIndex, currentIndex - 1) + case .next: + min(visibleIDs.index(before: visibleIDs.endIndex), currentIndex + 1) + } + return visibleIDs[destination] + } +} diff --git a/Sources/DevScopeCore/LaunchctlServiceTargetClassifier.swift b/Sources/DevScopeCore/LaunchctlServiceTargetClassifier.swift new file mode 100644 index 0000000..a139c64 --- /dev/null +++ b/Sources/DevScopeCore/LaunchctlServiceTargetClassifier.swift @@ -0,0 +1,25 @@ +import Foundation + +public enum LaunchctlServiceTargetState: Equatable, Sendable { + case loaded + case absent + case unknown +} + +public enum LaunchctlServiceTargetClassifier { + public static func classify( + _ result: AutomationCommandResult, + label: String, + guiUID: uid_t + ) -> LaunchctlServiceTargetState { + if result.status == 0 { return .loaded } + guard result.status == 113 else { return .unknown } + + let expectedDiagnostic = + "Bad request.\nCould not find service \"\(label)\" in domain for user gui: \(guiUID)\n" + guard String(data: result.standardError, encoding: .utf8) == expectedDiagnostic else { + return .unknown + } + return .absent + } +} diff --git a/Sources/DevScopeCore/LaunchdAutomationSource.swift b/Sources/DevScopeCore/LaunchdAutomationSource.swift new file mode 100644 index 0000000..5532c02 --- /dev/null +++ b/Sources/DevScopeCore/LaunchdAutomationSource.swift @@ -0,0 +1,787 @@ +import Darwin +import CoreFoundation +import CryptoKit +import Foundation + +public struct LaunchdScanRoot: Equatable, Sendable { + public let url: URL + public let kind: AutomationKind + public let ownership: AutomationOwnership + public let isMutable: Bool + + public init( + url: URL, + kind: AutomationKind, + ownership: AutomationOwnership, + isMutable: Bool + ) { + self.url = url + self.kind = kind + self.ownership = ownership + self.isMutable = isMutable + } +} + +public struct LaunchdRuntimeState: Equatable, Sendable { + public let enabledState: AutomationEnabledState + public let loadState: AutomationLoadState + + public init( + enabledState: AutomationEnabledState, + loadState: AutomationLoadState + ) { + self.enabledState = enabledState + self.loadState = loadState + } +} + +public protocol LaunchdRuntimeStateProviding: Sendable { + func states( + for labels: [String], + guiUID: uid_t + ) async -> [String: LaunchdRuntimeState] +} + +typealias LaunchdRecordParser = @Sendable ( + Data, + URL, + uid_t, + AutomationOwnership, + AutomationKind, + Bool +) throws -> AutomationRecord + +public actor LaunchdAutomationSource: AutomationSource { + private struct CacheIdentity: Hashable, Sendable { + let canonicalURL: URL + let resourceIdentifier: String? + let modificationDate: Date + let checksum: String + let ownerUID: uid_t + let ownership: AutomationOwnership + let kind: AutomationKind + let isMutable: Bool + } + + private struct CacheEntry: Sendable { + let identity: CacheIdentity + let record: AutomationRecord + } + + public nonisolated let kind: AutomationSourceKind = .launchAgent + + private let fileSystem: any AutomationFileSystem + private let currentUID: uid_t + private let roots: [LaunchdScanRoot] + private let runtimeStateProvider: (any LaunchdRuntimeStateProviding)? + private let recordParser: LaunchdRecordParser + private var cache: [URL: CacheEntry] = [:] + + public init( + fileSystem: any AutomationFileSystem, + currentUID: uid_t, + roots: [LaunchdScanRoot], + runtimeStateProvider: (any LaunchdRuntimeStateProviding)? = nil + ) { + self.fileSystem = fileSystem + self.currentUID = currentUID + self.roots = roots + self.runtimeStateProvider = runtimeStateProvider + recordParser = { data, sourceURL, ownerUID, ownership, kind, isMutable in + try LaunchdPlistParser.parse( + data: data, + canonicalSourceURL: sourceURL, + ownerUID: ownerUID, + ownership: ownership, + kind: kind, + isMutable: isMutable + ) + } + } + + init( + fileSystem: any AutomationFileSystem, + currentUID: uid_t, + roots: [LaunchdScanRoot], + runtimeStateProvider: (any LaunchdRuntimeStateProviding)? = nil, + recordParser: @escaping LaunchdRecordParser + ) { + self.fileSystem = fileSystem + self.currentUID = currentUID + self.roots = roots + self.runtimeStateProvider = runtimeStateProvider + self.recordParser = recordParser + } + + public static func defaultRoots(homeDirectory: URL) -> [LaunchdScanRoot] { + [ + LaunchdScanRoot( + url: homeDirectory.appending(path: "Library/LaunchAgents"), + kind: .launchAgent, + ownership: .user, + isMutable: true + ), + LaunchdScanRoot( + url: URL(fileURLWithPath: "/Library/LaunchAgents"), + kind: .launchAgent, + ownership: .thirdPartySystem, + isMutable: false + ), + LaunchdScanRoot( + url: URL(fileURLWithPath: "/Library/LaunchDaemons"), + kind: .launchDaemon, + ownership: .thirdPartySystem, + isMutable: false + ), + LaunchdScanRoot( + url: URL(fileURLWithPath: "/System/Library/LaunchAgents"), + kind: .launchAgent, + ownership: .appleSystem, + isMutable: false + ), + LaunchdScanRoot( + url: URL(fileURLWithPath: "/System/Library/LaunchDaemons"), + kind: .launchDaemon, + ownership: .appleSystem, + isMutable: false + ), + ] + } + + public func snapshot() async -> AutomationSourceSnapshot { + var records: [AutomationRecord] = [] + var errors: [String] = [] + var seenCanonicalURLs: Set = [] + + for root in roots { + let urls: [URL] + do { + urls = try fileSystem.plistURLs(in: root.url) + } catch { + errors.append("\(root.url.path): \(error)") + continue + } + + for url in urls { + let metadata: AutomationFileMetadata + do { + metadata = try fileSystem.metadata(for: url) + } catch { + let finding = "Unable to inspect launchd definition: \(error)" + errors.append("\(url.path): \(finding)") + records.append(Self.invalidRecord( + root: root, + sourceURL: url.standardizedFileURL, + ownerUID: root.ownership == .user ? currentUID : 0, + checksum: nil, + finding: finding + )) + continue + } + + let canonicalURL = Self.canonical(metadata.canonicalURL) + let ownerUID = metadata.ownerUID + seenCanonicalURLs.insert(canonicalURL) + let data: Data + do { + data = try fileSystem.read(url) + } catch { + cache.removeValue(forKey: canonicalURL) + let finding = "Unable to read launchd definition: \(error)" + errors.append("\(url.path): \(finding)") + records.append(Self.invalidRecord( + root: root, + sourceURL: canonicalURL, + ownerUID: ownerUID, + checksum: nil, + finding: finding + )) + continue + } + + let isMutable = Self.isSafelyMutable( + root: root, + metadata: metadata, + canonicalURL: canonicalURL, + currentUID: currentUID + ) + let checksum = Self.checksum(data) + let identity = CacheIdentity( + canonicalURL: canonicalURL, + resourceIdentifier: metadata.resourceIdentifier, + modificationDate: metadata.modificationDate, + checksum: checksum, + ownerUID: ownerUID, + ownership: root.ownership, + kind: root.kind, + isMutable: isMutable + ) + if let entry = cache[canonicalURL], entry.identity == identity { + records.append(entry.record) + continue + } + + do { + let record = try recordParser( + data, + canonicalURL, + ownerUID, + root.ownership, + root.kind, + isMutable + ) + records.append(record) + cache[canonicalURL] = CacheEntry(identity: identity, record: record) + } catch { + cache.removeValue(forKey: canonicalURL) + let finding = Self.finding(for: error) + errors.append("\(url.path): \(finding)") + records.append(Self.invalidRecord( + root: root, + sourceURL: canonicalURL, + ownerUID: ownerUID, + checksum: checksum, + finding: finding + )) + } + } + } + + cache = cache.filter { seenCanonicalURLs.contains($0.key) } + + if let runtimeStateProvider { + let labels = records.compactMap { record -> String? in + guard record.kind == .launchAgent, + record.sourceKind == .launchAgent, + record.ownership == .user, + record.ownerUID == currentUID, + !record.label.isEmpty, + !record.label.contains("/"), + !record.label.contains("\0") + else { return nil } + return record.label + }.sorted() + let runtimeStates = await runtimeStateProvider.states( + for: labels, + guiUID: currentUID + ) + records = records.map { record in + guard let runtimeState = runtimeStates[record.label], + record.kind == .launchAgent, + record.ownership == .user, + record.ownerUID == currentUID + else { return record } + return Self.applying(runtimeState, to: record) + } + } + + if errors.isEmpty { + return .healthy(kind: kind, records: records) + } + return AutomationSourceSnapshot( + records: records, + health: AutomationSourceHealth( + kind: kind, + state: records.isEmpty ? .failed : .partial, + message: Self.healthMessage(for: errors), + refreshedAt: Date() + ) + ) + } + + private static func applying( + _ runtimeState: LaunchdRuntimeState, + to record: AutomationRecord + ) -> AutomationRecord { + let state: AutomationState + switch runtimeState.enabledState { + case .enabled: + state = .idle + case .disabled: + state = .disabled + case .unknown: + state = .unresolved + } + return AutomationRecord( + id: record.id, + kind: record.kind, + sourceKind: record.sourceKind, + label: record.label, + displayName: record.displayName, + providerBundleIdentifier: record.providerBundleIdentifier, + ownerUID: record.ownerUID, + ownership: record.ownership, + executable: record.executable, + arguments: record.arguments, + commandSignature: record.commandSignature, + environment: record.environment, + workingDirectory: record.workingDirectory, + schedule: record.schedule, + sourceURL: record.sourceURL, + sourceChecksum: record.sourceChecksum, + enabledState: runtimeState.enabledState, + loadState: runtimeState.loadState, + approvalState: record.approvalState, + state: state, + evidence: record.evidence, + capabilities: record.capabilities, + validationFindings: record.validationFindings + ) + } + + static func healthMessage(for errors: [String]) -> String { + precondition(!errors.isEmpty) + let examples = errors.prefix(2).map { error in + let compact = error.split(whereSeparator: \.isWhitespace).joined(separator: " ") + guard compact.count > 120 else { return compact } + return String(compact.prefix(119)) + "…" + } + let issueLabel = errors.count == 1 ? "issue" : "issues" + let exampleLabel = examples.count == 1 ? "Example" : "Examples" + return "Launchd inspection found \(errors.count) \(issueLabel). " + + "\(exampleLabel): \(examples.joined(separator: "; ")). " + + "Select an Invalid record for per-definition details." + } + + private static func invalidRecord( + root: LaunchdScanRoot, + sourceURL: URL, + ownerUID: uid_t, + checksum: String?, + finding: String + ) -> AutomationRecord { + let sourceURL = sourceURL.standardizedFileURL + let label = sourceURL.deletingPathExtension().lastPathComponent + let sourceKind: AutomationSourceKind = root.kind == .launchDaemon + ? .launchDaemon : .launchAgent + return AutomationRecord( + id: AutomationRecord.ID( + source: sourceKind, + ownerUID: ownerUID, + label: label, + sourcePath: sourceURL.path + ), + kind: root.kind, + sourceKind: sourceKind, + label: label, + displayName: label, + providerBundleIdentifier: nil, + ownerUID: ownerUID, + ownership: root.ownership, + executable: nil, + arguments: [], + environment: [:], + workingDirectory: nil, + schedule: AutomationSchedule(triggers: [.demand], summary: "Invalid definition"), + sourceURL: sourceURL, + sourceChecksum: checksum, + enabledState: .unknown, + loadState: .unknown, + approvalState: .notApplicable, + state: .invalid, + evidence: [AutomationEvidence( + strength: .weak, + source: "launchd property list", + detail: finding + )], + capabilities: [], + validationFindings: [finding] + ) + } + + private static func finding(for error: Error) -> String { + switch error as? AutomationParseError { + case .unreadablePropertyList: + return "Unreadable property list" + case .missingLabel: + return "Missing required Label" + case .missingProgram: + return "Missing Program, BundleProgram, or ProgramArguments" + case .invalidField(let field): + return "Invalid \(field)" + case nil: + return "Unable to parse launchd definition: \(error)" + } + } + + private static func checksum(_ data: Data) -> String { + SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined() + } + + private static func isSafelyMutable( + root: LaunchdScanRoot, + metadata: AutomationFileMetadata, + canonicalURL: URL, + currentUID: uid_t + ) -> Bool { + guard + root.isMutable, + root.kind == .launchAgent, + root.ownership == .user, + metadata.ownerUID == currentUID, + !metadata.isSymbolicLink + else { + return false + } + + let rootComponents = canonical(root.url).pathComponents + let fileComponents = canonicalURL.pathComponents + return fileComponents.count > rootComponents.count + && fileComponents.prefix(rootComponents.count).elementsEqual(rootComponents) + } + + private static func canonical(_ url: URL) -> URL { + URL(fileURLWithPath: url.standardizedFileURL.path) + } +} + +public enum LaunchdPlistParser { + public static func parse( + data: Data, + sourceURL: URL, + ownerUID: uid_t, + ownership: AutomationOwnership + ) throws -> AutomationRecord { + let canonicalSourceURL = sourceURL.standardizedFileURL + let kind: AutomationKind = canonicalSourceURL.deletingLastPathComponent().lastPathComponent + == "LaunchDaemons" ? .launchDaemon : .launchAgent + return try parse( + data: data, + canonicalSourceURL: canonicalSourceURL, + ownerUID: ownerUID, + ownership: ownership, + kind: kind, + isMutable: false + ) + } + + static func parse( + data: Data, + canonicalSourceURL: URL, + ownerUID: uid_t, + ownership: AutomationOwnership, + kind: AutomationKind, + isMutable: Bool + ) throws -> AutomationRecord { + guard + let plist = try? PropertyListSerialization.propertyList( + from: data, + options: [], + format: nil + ) as? [String: Any] + else { + throw AutomationParseError.unreadablePropertyList + } + guard let labelValue = plist["Label"] else { + throw AutomationParseError.missingLabel + } + guard let label = labelValue as? String, !label.isEmpty else { + throw AutomationParseError.invalidField("Label") + } + + let program = try optionalNonemptyString("Program", in: plist) + let bundleProgram = try optionalNonemptyString("BundleProgram", in: plist) + let programArguments = try optionalStringArray("ProgramArguments", in: plist) + let environment = try optionalStringDictionary("EnvironmentVariables", in: plist) ?? [:] + let workingDirectory = try optionalNonemptyString("WorkingDirectory", in: plist) + let executable: String + let arguments: [String] + if let program { + guard program.hasPrefix("/") else { + throw AutomationParseError.invalidField("Program") + } + executable = program + arguments = programArguments ?? [] + } else if let bundleProgram { + guard !bundleProgram.hasPrefix("/") else { + throw AutomationParseError.invalidField("BundleProgram") + } + executable = bundleProgram + arguments = programArguments ?? [] + } else { + guard + let programArguments, + let firstArgument = programArguments.first, + !firstArgument.isEmpty + else { + throw AutomationParseError.missingProgram + } + executable = firstArgument + arguments = Array(programArguments.dropFirst()) + } + + let schedule = try parseSchedule(plist) + let sourceKind: AutomationSourceKind = kind == .launchDaemon ? .launchDaemon : .launchAgent + let checksum = SHA256.hash(data: data) + .map { String(format: "%02x", $0) } + .joined() + let capabilities: Set = isMutable + ? [.edit, .duplicate, .exportRecord, .remove] + : [.exportRecord] + + return AutomationRecord( + id: AutomationRecord.ID( + source: sourceKind, + ownerUID: ownerUID, + label: label, + sourcePath: canonicalSourceURL.path + ), + kind: kind, + sourceKind: sourceKind, + label: label, + displayName: label, + providerBundleIdentifier: nil, + ownerUID: ownerUID, + ownership: ownership, + executable: executable, + arguments: arguments, + environment: environment, + workingDirectory: workingDirectory, + schedule: schedule, + sourceURL: canonicalSourceURL, + sourceChecksum: checksum, + enabledState: .unknown, + loadState: .unknown, + approvalState: .notApplicable, + state: .unresolved, + evidence: [AutomationEvidence( + strength: .strong, + source: "launchd property list", + detail: "Parsed \(canonicalSourceURL.lastPathComponent)" + )], + capabilities: capabilities, + validationFindings: [] + ) + } + + private static func parseSchedule(_ plist: [String: Any]) throws -> AutomationSchedule { + var triggers: [AutomationSchedule.Trigger] = [] + var summaries: [String] = [] + + if let value = plist["RunAtLoad"] { + guard let runAtLoad = strictBoolean(value) else { + throw AutomationParseError.invalidField("RunAtLoad") + } + if runAtLoad { + triggers.append(.runAtLoad) + summaries.append("At load") + } + } + + if let value = plist["KeepAlive"] { + if let keepAlive = strictBoolean(value) { + if keepAlive { + if !triggers.contains(.runAtLoad) { + triggers.append(.runAtLoad) + summaries.append("At load") + } + triggers.append(.keepAlive) + summaries.append("kept alive") + } + } else if let conditions = value as? [String: Any] { + try validateKeepAliveConditions(conditions) + if !triggers.contains(.runAtLoad) { + triggers.append(.runAtLoad) + summaries.append("At load") + } + triggers.append(.keepAlive) + summaries.append("kept alive conditionally") + } else { + throw AutomationParseError.invalidField("KeepAlive") + } + } + + if let value = plist["StartInterval"] { + guard let seconds = strictInteger(value), seconds > 0 else { + throw AutomationParseError.invalidField("StartInterval") + } + triggers.append(.interval(seconds: seconds)) + summaries.append(intervalSummary(seconds)) + } + + if let value = plist["StartCalendarInterval"] { + let descriptions = try parseCalendarIntervals(value) + triggers.append(contentsOf: descriptions.map(AutomationSchedule.Trigger.calendar)) + summaries.append(descriptions.joined(separator: "; ")) + } + + if triggers.isEmpty { + return AutomationSchedule(triggers: [.demand], summary: "On demand") + } + return AutomationSchedule(triggers: triggers, summary: summaries.joined(separator: ", ")) + } + + private static func parseCalendarIntervals(_ value: Any) throws -> [String] { + if let fields = value as? [String: Any] { + return [try calendarSummary(fields)] + } + guard let intervals = value as? [[String: Any]], !intervals.isEmpty else { + throw AutomationParseError.invalidField("StartCalendarInterval") + } + return try intervals.map(calendarSummary) + } + + private static func validateKeepAliveConditions(_ conditions: [String: Any]) throws { + let booleanKeys: Set = ["SuccessfulExit", "NetworkState", "Crashed"] + let dictionaryKeys: Set = ["PathState", "OtherJobEnabled"] + guard Set(conditions.keys).isSubset(of: booleanKeys.union(dictionaryKeys)) else { + throw AutomationParseError.invalidField("KeepAlive") + } + + for (key, value) in conditions { + if booleanKeys.contains(key) { + guard strictBoolean(value) != nil else { + throw AutomationParseError.invalidField("KeepAlive") + } + } else { + guard + let dictionary = value as? [String: Any], + dictionary.values.allSatisfy({ strictBoolean($0) != nil }) + else { + throw AutomationParseError.invalidField("KeepAlive") + } + } + } + } + + private static func calendarSummary(_ fields: [String: Any]) throws -> String { + let supportedKeys: Set = ["Minute", "Hour", "Day", "Weekday", "Month"] + guard Set(fields.keys).isSubset(of: supportedKeys) else { + throw AutomationParseError.invalidField("StartCalendarInterval") + } + + let minute = try calendarInteger("Minute", in: fields, range: 0...59) + let hour = try calendarInteger("Hour", in: fields, range: 0...23) + let day = try calendarInteger("Day", in: fields, range: 1...31) + let weekday = try calendarInteger("Weekday", in: fields, range: 0...7) + let month = try calendarInteger("Month", in: fields, range: 1...12) + + let weekdayNames = [ + "Sundays", "Mondays", "Tuesdays", "Wednesdays", + "Thursdays", "Fridays", "Saturdays", "Sundays", + ] + let monthNames = [ + "", "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December", + ] + let weekdayDescription = weekday.map { weekdayNames[$0] } + let dateDescription: String + if let month { + let monthName = monthNames[month] + switch (day, weekdayDescription) { + case (.some(let day), .some(let weekdayDescription)): + dateDescription = "\(monthName) (day \(day) or \(weekdayDescription))" + case (.some(let day), .none): + dateDescription = "\(monthName) \(day)" + case (.none, .some(let weekdayDescription)): + dateDescription = "\(weekdayDescription) in \(monthName)" + case (.none, .none): + dateDescription = "Every \(monthName)" + } + } else { + switch (day, weekdayDescription) { + case (.some(let day), .some(let weekdayDescription)): + dateDescription = "\(weekdayDescription) or Day \(day) of every month" + case (.some(let day), .none): + dateDescription = "Day \(day) of every month" + case (.none, .some(let weekdayDescription)): + dateDescription = weekdayDescription + case (.none, .none): + dateDescription = "Every day" + } + } + + if let hour, let minute { + return "\(dateDescription) at \(String(format: "%02d:%02d", hour, minute))" + } + if let hour { + return "\(dateDescription) during hour \(String(format: "%02d", hour))" + } + if let minute { + return "\(dateDescription) at minute \(String(format: "%02d", minute)) of every hour" + } + return "\(dateDescription) every minute" + } + + private static func calendarInteger( + _ key: String, + in fields: [String: Any], + range: ClosedRange + ) throws -> Int? { + guard let value = fields[key] else { return nil } + guard let integer = strictInteger(value), range.contains(integer) else { + throw AutomationParseError.invalidField("StartCalendarInterval.\(key)") + } + return integer + } + + private static func optionalNonemptyString( + _ key: String, + in plist: [String: Any] + ) throws -> String? { + guard let value = plist[key] else { return nil } + guard let string = value as? String, !string.isEmpty else { + throw AutomationParseError.invalidField(key) + } + return string + } + + private static func optionalStringArray( + _ key: String, + in plist: [String: Any] + ) throws -> [String]? { + guard let value = plist[key] else { return nil } + guard let values = value as? [Any], values.allSatisfy({ $0 is String }) else { + throw AutomationParseError.invalidField(key) + } + return values.compactMap { $0 as? String } + } + + private static func optionalStringDictionary( + _ key: String, + in plist: [String: Any] + ) throws -> [String: String]? { + guard let value = plist[key] else { return nil } + guard let dictionary = value as? [String: Any], + dictionary.keys.allSatisfy({ !$0.isEmpty }), + dictionary.values.allSatisfy({ $0 is String }) else { + throw AutomationParseError.invalidField(key) + } + return dictionary.reduce(into: [String: String]()) { result, entry in + result[entry.key] = entry.value as? String + } + } + + private static func strictBoolean(_ value: Any) -> Bool? { + guard + let number = value as? NSNumber, + CFGetTypeID(number) == CFBooleanGetTypeID() + else { + return nil + } + return number.boolValue + } + + private static func strictInteger(_ value: Any) -> Int? { + guard + let number = value as? NSNumber, + CFGetTypeID(number) != CFBooleanGetTypeID(), + let integer = value as? Int + else { + return nil + } + return integer + } + + private static func intervalSummary(_ seconds: Int) -> String { + if seconds.isMultiple(of: 3_600) { + let hours = seconds / 3_600 + return "Every \(hours) \(hours == 1 ? "hour" : "hours")" + } + if seconds.isMultiple(of: 60) { + let minutes = seconds / 60 + return "Every \(minutes) \(minutes == 1 ? "minute" : "minutes")" + } + return "Every \(seconds) \(seconds == 1 ? "second" : "seconds")" + } +} diff --git a/Sources/DevScopeCore/LiveActivityLayoutPolicy.swift b/Sources/DevScopeCore/LiveActivityLayoutPolicy.swift new file mode 100644 index 0000000..5239c2c --- /dev/null +++ b/Sources/DevScopeCore/LiveActivityLayoutPolicy.swift @@ -0,0 +1,69 @@ +import Foundation + +public enum LiveActivityLayoutMode: Equatable, Sendable { + case wide + case stacked + case compact +} + +public enum LiveActivityVerticalMode: Equatable, Sendable { + case normal + case condensed +} + +public enum LiveActivityLayoutPolicy { + public static let defaultHeight = 190.0 + public static let minimumHeight = 120.0 + public static let maximumHeight = 360.0 + public static let minimumProcessHeight = 360.0 + private static let splitAllowance = 8.0 + private static let persistenceTolerance = 0.5 + private static let compactWidthThreshold = 800.0 + private static let condensedHeightThreshold = 160.0 + private static let stackedContentHeight = 225.0 + + public static func resolvedHeight(preferredHeight: Double, workspaceHeight: Double) -> Double { + let preferred = preferredHeight.isFinite ? preferredHeight : defaultHeight + let clampedPreference = min(max(preferred, minimumHeight), maximumHeight) + let available = max(minimumHeight, workspaceHeight - minimumProcessHeight - splitAllowance) + return min(clampedPreference, available) + } + + public static func shouldPersist(measuredHeight: Double, workspaceHeight: Double) -> Bool { + measuredHeight >= minimumHeight + && measuredHeight <= maximumHeight + && workspaceHeight > minimumProcessHeight + measuredHeight + splitAllowance + } + + public static func updatedPreferredHeight( + currentPreferredHeight: Double, + measuredHeight: Double, + workspaceHeight: Double + ) -> Double { + guard shouldPersist(measuredHeight: measuredHeight, workspaceHeight: workspaceHeight) else { + return currentPreferredHeight + } + guard + !currentPreferredHeight.isFinite + || abs(measuredHeight - currentPreferredHeight) >= persistenceTolerance + else { + return currentPreferredHeight + } + return measuredHeight + } + + public static func mode(availableWidth: Double) -> LiveActivityLayoutMode { + if availableWidth >= 1_000 { return .wide } + if availableWidth >= compactWidthThreshold { return .stacked } + return .compact + } + + public static func verticalMode( + availableHeight: Double, + layoutMode: LiveActivityLayoutMode + ) -> LiveActivityVerticalMode { + if availableHeight < condensedHeightThreshold { return .condensed } + if layoutMode == .stacked, availableHeight < stackedContentHeight { return .condensed } + return .normal + } +} diff --git a/Sources/DevScopeCore/LoginBackgroundAutomationSource.swift b/Sources/DevScopeCore/LoginBackgroundAutomationSource.swift new file mode 100644 index 0000000..6a12e61 --- /dev/null +++ b/Sources/DevScopeCore/LoginBackgroundAutomationSource.swift @@ -0,0 +1,532 @@ +import CryptoKit +import Darwin +import Foundation + +public struct LegacyLoginItemDescriptor: Equatable, Sendable { + public let name: String + public let path: String + public let isHidden: Bool + + public init(name: String, path: String, isHidden: Bool) { + self.name = name + self.path = path + self.isHidden = isHidden + } +} + +struct LegacyLoginItemRecoveryDescriptor: Codable, Equatable, Sendable { + let version: Int + let recordID: String + let ownerUID: uid_t + let name: String + let path: String + let isHidden: Bool +} + +public enum LegacyLoginItemRecoveryDocumentError: Error, Equatable, Sendable { + case invalidBinding +} + +public enum LegacyLoginItemRecoveryDocument { + public static func checksum(ofEncodedDocument data: Data) -> String { + SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined() + } + + public static func encode( + selectedRecord: AutomationRecord, + descriptor: LegacyLoginItemDescriptor, + currentUID: uid_t + ) throws -> Data { + guard descriptor.path.hasPrefix("/"), !descriptor.path.contains("\0") else { + throw LegacyLoginItemRecoveryDocumentError.invalidBinding + } + let canonicalPath = URL(fileURLWithPath: descriptor.path).standardizedFileURL.path + let expectedID = AutomationRecord.ID( + source: .legacyLoginItem, + ownerUID: currentUID, + label: descriptor.name, + sourcePath: canonicalPath + ) + guard selectedRecord.kind == .loginItem, + selectedRecord.sourceKind == .legacyLoginItem, + selectedRecord.ownership == .user, + selectedRecord.ownerUID == currentUID, + selectedRecord.id == expectedID, + selectedRecord.label == descriptor.name, + let executable = selectedRecord.executable, + URL(fileURLWithPath: executable).standardizedFileURL.path + == canonicalPath + else { throw LegacyLoginItemRecoveryDocumentError.invalidBinding } + let recovery = LegacyLoginItemRecoveryDescriptor( + version: 1, + recordID: selectedRecord.id.rawValue, + ownerUID: currentUID, + name: descriptor.name, + path: canonicalPath, + isHidden: descriptor.isHidden + ) + do { + let encoder = JSONEncoder() + encoder.outputFormatting = [.sortedKeys] + return try encoder.encode(recovery) + } catch { + throw LegacyLoginItemRecoveryDocumentError.invalidBinding + } + } +} + +public enum LegacyLoginItemAdapterError: Error, Equatable, Sendable { + case permissionDenied + case unavailable +} + +public protocol LegacyLoginItemListing: Sendable { + func currentUserLoginItems() async throws -> [LegacyLoginItemDescriptor] +} + +public struct OSACommandLegacyLoginItemAdapter: LegacyLoginItemListing { + private struct Item: Decodable { + let name: String + let path: String + let hidden: Bool + } + + private static let fixedProgram = """ + const systemEvents = Application('System Events'); + JSON.stringify(systemEvents.loginItems().map(item => ({ + name: item.name(), + path: item.path(), + hidden: item.hidden() + }))); + """ + + private let runner: any AutomationCommandRunning + + public init(runner: any AutomationCommandRunning) { + self.runner = runner + } + + public func currentUserLoginItems() async throws -> [LegacyLoginItemDescriptor] { + let result: AutomationCommandResult + do { + result = try await runner.run(AutomationCommand( + executable: "/usr/bin/osascript", + arguments: ["-l", "JavaScript", "-e", Self.fixedProgram] + )) + } catch { + throw LegacyLoginItemAdapterError.unavailable + } + + guard result.status == 0 else { + throw LegacyLoginItemAdapterError.permissionDenied + } + guard let items = try? JSONDecoder().decode([Item].self, from: result.standardOutput) else { + throw LegacyLoginItemAdapterError.unavailable + } + return items.map { + LegacyLoginItemDescriptor(name: $0.name, path: $0.path, isHidden: $0.hidden) + } + } +} + +public struct LegacyLoginItemAutomationSource: AutomationSource { + public let kind: AutomationSourceKind = .legacyLoginItem + + private let adapter: any LegacyLoginItemListing + private let currentUID: uid_t + private let now: @Sendable () -> Date + + public init( + adapter: any LegacyLoginItemListing, + currentUID: uid_t, + now: @escaping @Sendable () -> Date = Date.init + ) { + self.adapter = adapter + self.currentUID = currentUID + self.now = now + } + + public func snapshot() async -> AutomationSourceSnapshot { + let items: [LegacyLoginItemDescriptor] + do { + items = try await adapter.currentUserLoginItems() + } catch LegacyLoginItemAdapterError.permissionDenied { + return AutomationSourceSnapshot( + records: [], + health: AutomationSourceHealth( + kind: kind, + state: .permissionRequired, + message: "Allow DevScope to inspect current-user login items in System Settings.", + refreshedAt: now() + ) + ) + } catch { + return .failed( + kind: kind, + message: "Current-user login items are unavailable.", + refreshedAt: now() + ) + } + + var invalidCount = 0 + let records = items.compactMap { item -> AutomationRecord? in + guard !item.name.isEmpty, item.path.hasPrefix("/") else { + invalidCount += 1 + return nil + } + let provisionalRecord = Self.record( + for: item, + currentUID: currentUID, + sourceChecksum: nil + ) + guard let recoveryData = try? LegacyLoginItemRecoveryDocument.encode( + selectedRecord: provisionalRecord, + descriptor: item, + currentUID: currentUID + ) else { + invalidCount += 1 + return nil + } + return Self.record( + for: item, + currentUID: currentUID, + sourceChecksum: LegacyLoginItemRecoveryDocument.checksum( + ofEncodedDocument: recoveryData + ) + ) + } + let refreshDate = now() + return AutomationSourceSnapshot( + records: records, + health: AutomationSourceHealth( + kind: kind, + state: invalidCount == 0 ? .healthy : (records.isEmpty ? .failed : .partial), + message: invalidCount == 0 ? nil : "Some login-item records were not usable.", + refreshedAt: refreshDate + ) + ) + } + + private static func record( + for item: LegacyLoginItemDescriptor, + currentUID: uid_t, + sourceChecksum: String? + ) -> AutomationRecord { + let sourceURL = URL(fileURLWithPath: item.path).standardizedFileURL + return AutomationRecord( + id: AutomationRecord.ID( + source: .legacyLoginItem, + ownerUID: currentUID, + label: item.name, + sourcePath: sourceURL.path + ), + kind: .loginItem, + sourceKind: .legacyLoginItem, + label: item.name, + displayName: item.name, + providerBundleIdentifier: nil, + ownerUID: currentUID, + ownership: .user, + executable: sourceURL.path, + arguments: [], + environment: [:], + workingDirectory: nil, + schedule: AutomationSchedule(triggers: [.atLogin], summary: "At login"), + sourceURL: sourceURL, + sourceChecksum: sourceChecksum, + enabledState: .enabled, + loadState: .unknown, + approvalState: .notApplicable, + state: .unresolved, + evidence: [AutomationEvidence( + strength: .strong, + source: "Current-user legacy login items", + detail: item.isHidden ? "Configured hidden at login" : "Configured at login" + )], + capabilities: [.exportRecord], + validationFindings: [] + ) + } +} + +public enum BackgroundTaskDiagnosticPolicy: Equatable, Sendable { + case available + case administratorApprovalRequired + + public static var currentSystem: Self { + forOperatingSystemMajorVersion( + ProcessInfo.processInfo.operatingSystemVersion.majorVersion + ) + } + + public static func forOperatingSystemMajorVersion(_ majorVersion: Int) -> Self { + majorVersion >= 27 ? .administratorApprovalRequired : .available + } +} + +public actor BackgroundTaskAutomationSource: AutomationSource { + public nonisolated let kind: AutomationSourceKind = .serviceManagement + + private struct ParsedDiagnostic: Sendable { + let records: [AutomationRecord] + let boundaryCount: Int + let unusableCount: Int + let rawOutputHash: String + } + + private struct BoundedFields { + var values: [String: String] = [:] + var hasConflict = false + } + + private static let recognizedKeys: Set = [ + "Name", + "Identifier", + "URL", + "Bundle Identifier", + "Executable Path", + "Disposition", + "Parent Identifier", + "Team Identifier", + "Developer Name", + "Type", + ] + + private let runner: any AutomationCommandRunning + private let diagnosticPolicy: BackgroundTaskDiagnosticPolicy + private let now: @Sendable () -> Date + private var rawOutputHash: String? + + public init( + runner: any AutomationCommandRunning, + diagnosticPolicy: BackgroundTaskDiagnosticPolicy = .currentSystem, + now: @escaping @Sendable () -> Date = Date.init + ) { + self.runner = runner + self.diagnosticPolicy = diagnosticPolicy + self.now = now + } + + public func currentRawOutputHash() -> String? { + rawOutputHash + } + + public func snapshot() async -> AutomationSourceSnapshot { + guard diagnosticPolicy == .available else { + return AutomationSourceSnapshot( + records: [], + health: AutomationSourceHealth( + kind: kind, + state: .permissionRequired, + message: "macOS 27 or later requires administrator approval for this Background Items " + + "diagnostic. Review Background Items in System Settings; DevScope did not run the diagnostic.", + refreshedAt: now() + ) + ) + } + + let result: AutomationCommandResult + do { + result = try await runner.run(AutomationCommand( + executable: "/usr/bin/sfltool", + arguments: ["dumpbtm"] + )) + } catch { + return failedSnapshot(message: "Background Task Management diagnostic is unavailable.") + } + + rawOutputHash = Self.hash(result.standardOutput) + guard result.status == 0 else { + return failedSnapshot(message: "Background Task Management diagnostic failed.") + } + guard let parsed = Self.parse(result.standardOutput) else { + return failedSnapshot(message: "Background Task Management output could not be interpreted.") + } + rawOutputHash = parsed.rawOutputHash + + let refreshDate = now() + let healthState: AutomationSourceHealthState + let message: String? + if parsed.boundaryCount == 0 { + healthState = .failed + message = "Background Task Management record boundaries were not recognized." + } else if parsed.unusableCount > 0 { + healthState = parsed.records.isEmpty ? .failed : .partial + message = "Some Background Task Management records were unresolved." + } else { + healthState = .healthy + message = nil + } + return AutomationSourceSnapshot( + records: parsed.records, + health: AutomationSourceHealth( + kind: kind, + state: healthState, + message: message, + refreshedAt: refreshDate + ) + ) + } + + private func failedSnapshot(message: String) -> AutomationSourceSnapshot { + .failed(kind: kind, message: message, refreshedAt: now()) + } + + private static func parse(_ data: Data) -> ParsedDiagnostic? { + guard let text = String(data: data, encoding: .utf8) else { return nil } + var boundedRecords: [BoundedFields] = [] + var currentRecord: BoundedFields? + var driftCount = 0 + + for line in text.components(separatedBy: .newlines) { + let trimmed = line.trimmingCharacters(in: .whitespaces) + if isRecordBoundary(trimmed) { + if let currentRecord { + boundedRecords.append(currentRecord) + } + currentRecord = BoundedFields() + continue + } + if isBoundaryLike(trimmed) { + if let currentRecord { + boundedRecords.append(currentRecord) + } + currentRecord = nil + driftCount += 1 + continue + } + guard currentRecord != nil, + let delimiter = trimmed.range(of: ": ") + else { continue } + let key = String(trimmed[.. AutomationRecord? in + guard !bounded.hasConflict else { return nil } + return record(from: bounded.values, fingerprint: fingerprint) + } + return ParsedDiagnostic( + records: records, + boundaryCount: boundedRecords.count, + unusableCount: boundedRecords.count - records.count + driftCount, + rawOutputHash: fingerprint + ) + } + + private static func isRecordBoundary(_ line: String) -> Bool { + guard line.first == "#", line.last == ":" else { return false } + let digits = line.dropFirst().dropLast() + return !digits.isEmpty && digits.utf8.allSatisfy { byte in + byte >= Character("0").asciiValue! && byte <= Character("9").asciiValue! + } + } + + private static func isBoundaryLike(_ line: String) -> Bool { + line.first == "#" && line.last == ":" + } + + private static func record( + from fields: [String: String], + fingerprint: String + ) -> AutomationRecord? { + let bundleIdentifier = fields["Bundle Identifier"] + let identifier = fields["Identifier"] + let executable = normalizedFilePath(fields["Executable Path"]) + let sourceURL = normalizedFileURL(fields["URL"]) + let hasStrongBundleIdentity = bundleIdentifier?.isEmpty == false + let hasStrongLabeledPathIdentity = identifier?.isEmpty == false + && (executable != nil || sourceURL != nil) + guard hasStrongBundleIdentity || hasStrongLabeledPathIdentity else { return nil } + + let label = identifier ?? bundleIdentifier! + let displayName = fields["Name"] ?? label + let identityPath = sourceURL?.path ?? executable ?? "bundle:\(bundleIdentifier!)" + var evidence: [AutomationEvidence] = [] + if let bundleIdentifier { + evidence.append(AutomationEvidence( + strength: .strong, + source: "Background Task Management diagnostic", + detail: "Exact bundle identifier: \(bundleIdentifier)" + )) + } + if let identifier { + evidence.append(AutomationEvidence( + strength: executable == nil && sourceURL == nil ? .weak : .strong, + source: "Background Task Management diagnostic", + detail: "Exact item identifier: \(identifier)" + )) + } + for key in [ + "Disposition", "Parent Identifier", "Team Identifier", "Developer Name", "Type", + ] { + if let value = fields[key] { + evidence.append(AutomationEvidence( + strength: .weak, + source: "Background Task Management diagnostic \(key)", + detail: value + )) + } + } + + return AutomationRecord( + id: AutomationRecord.ID( + source: .serviceManagement, + ownerUID: 0, + label: label, + sourcePath: identityPath + ), + kind: .backgroundItem, + sourceKind: .serviceManagement, + label: label, + displayName: displayName, + providerBundleIdentifier: bundleIdentifier, + ownerUID: nil, + ownership: .thirdPartySystem, + executable: executable, + arguments: [], + environment: [:], + workingDirectory: nil, + schedule: AutomationSchedule(triggers: [.demand], summary: "System-managed background item"), + sourceURL: sourceURL, + sourceChecksum: fingerprint, + enabledState: .unknown, + loadState: .unknown, + approvalState: .unknown, + state: .unresolved, + evidence: evidence, + capabilities: [.exportRecord], + validationFindings: [] + ) + } + + private static func normalizedFileURL(_ value: String?) -> URL? { + guard let value else { return nil } + if value.hasPrefix("/") { + return URL(fileURLWithPath: value).standardizedFileURL + } + guard let url = URL(string: value), url.isFileURL else { return nil } + return url.standardizedFileURL + } + + private static func normalizedFilePath(_ value: String?) -> String? { + normalizedFileURL(value)?.path + } + + private static func hash(_ data: Data) -> String { + SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined() + } +} diff --git a/Sources/DevScopeCore/LongRunningAssessment.swift b/Sources/DevScopeCore/LongRunningAssessment.swift new file mode 100644 index 0000000..5a3fa32 --- /dev/null +++ b/Sources/DevScopeCore/LongRunningAssessment.swift @@ -0,0 +1,15 @@ +import Foundation + +public enum LongRunningAssessment { + public static func isLongRunning( + _ usage: DevProcessResourceUsage?, + threshold: TimeInterval = 14_400 + ) -> Bool { + guard let usage else { + return false + } + + let seconds = ProcessPresentation.elapsedSeconds(usage.elapsedTime) + return seconds >= 0 && Double(seconds) >= threshold + } +} diff --git a/Sources/DevScopeCore/PersistentCopyStore.swift b/Sources/DevScopeCore/PersistentCopyStore.swift new file mode 100644 index 0000000..5a47b56 --- /dev/null +++ b/Sources/DevScopeCore/PersistentCopyStore.swift @@ -0,0 +1,133 @@ +import Foundation + +public enum PersistentCopyStoreError: Error, Equatable, Sendable { + case payloadTooLarge +} + +public struct PersistedCopyPayload: Codable, Equatable, Sendable { + public let label: String + public let text: String + public let timestamp: Date + public let isTruncated: Bool + + public init(label: String, text: String, timestamp: Date = Date(), isTruncated: Bool = false) { + self.label = label + self.text = text + self.timestamp = timestamp + self.isTruncated = isTruncated + } +} + +public struct PersistentCopyStore: Sendable { + public let directory: URL + public let maxStoredBytes: Int + private let maxReadablePayloadBytes: Int + + private var payloadURL: URL { + directory.appendingPathComponent("last-copy.json", isDirectory: false) + } + + public init(directory: URL? = nil, maxStoredBytes: Int = 2_000_000) { + if let directory { + self.directory = directory + } else { + let baseDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first ?? + FileManager.default.temporaryDirectory + self.directory = baseDirectory + .appendingPathComponent("DevScope", isDirectory: true) + .appendingPathComponent("Clipboard", isDirectory: true) + } + self.maxStoredBytes = max(0, maxStoredBytes) + let (escapedTextLimit, escapedTextOverflow) = self.maxStoredBytes.multipliedReportingOverflow(by: 6) + let encodedTextLimit = escapedTextOverflow ? Int.max - 1 : escapedTextLimit + let (readLimit, readLimitOverflow) = encodedTextLimit.addingReportingOverflow(64 * 1_024) + self.maxReadablePayloadBytes = readLimitOverflow ? Int.max - 1 : readLimit + } + + @discardableResult + public func save(text: String, label: String, timestamp: Date = Date()) throws -> PersistedCopyPayload { + try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) + try FileManager.default.setAttributes([.posixPermissions: 0o700], ofItemAtPath: directory.path) + + let bounded = boundedText(text) + let payload = PersistedCopyPayload( + label: label, + text: bounded.text, + timestamp: timestamp, + isTruncated: bounded.isTruncated + ) + let data = try JSONEncoder.devScopeCopyStore.encode(payload) + guard data.count <= maxReadablePayloadBytes else { + throw PersistentCopyStoreError.payloadTooLarge + } + try data.write(to: payloadURL, options: [.atomic]) + try FileManager.default.setAttributes([.posixPermissions: 0o600], ofItemAtPath: payloadURL.path) + return payload + } + + public func load() throws -> PersistedCopyPayload? { + guard FileManager.default.fileExists(atPath: payloadURL.path) else { + return nil + } + + let handle = try FileHandle(forReadingFrom: payloadURL) + defer { try? handle.close() } + let data = try handle.read(upToCount: maxReadablePayloadBytes + 1) ?? Data() + guard data.count <= maxReadablePayloadBytes else { + throw PersistentCopyStoreError.payloadTooLarge + } + return try JSONDecoder.devScopeCopyStore.decode(PersistedCopyPayload.self, from: data) + } + + public func clear() throws { + guard FileManager.default.fileExists(atPath: payloadURL.path) else { + return + } + + try FileManager.default.removeItem(at: payloadURL) + } + + private func boundedText(_ text: String) -> (text: String, isTruncated: Bool) { + let data = Data(text.utf8) + guard data.count > maxStoredBytes else { + return (text, false) + } + + let suffix = "\n[truncated for DevScope recovery cache]" + let suffixData = Data(suffix.utf8).prefix(maxStoredBytes) + let prefixLimit = max(0, maxStoredBytes - suffixData.count) + var boundedData = validUTF8Prefix(of: data, maxBytes: prefixLimit) + boundedData.append(suffixData) + + return (String(decoding: boundedData, as: UTF8.self), true) + } + + private func validUTF8Prefix(of data: Data, maxBytes: Int) -> Data { + var end = min(data.count, maxBytes) + while end > 0 { + let candidate = Data(data.prefix(end)) + if String(data: candidate, encoding: .utf8) != nil { + return candidate + } + end -= 1 + } + return Data() + } +} + +private extension JSONEncoder { + static var devScopeCopyStore: JSONEncoder { + let encoder = JSONEncoder() + encoder.dateEncodingStrategy = .iso8601 + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] + return encoder + } +} + +private extension JSONDecoder { + static var devScopeCopyStore: JSONDecoder { + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .iso8601 + return decoder + } +} diff --git a/Sources/DevScopeCore/ProcessAccessAssessment.swift b/Sources/DevScopeCore/ProcessAccessAssessment.swift new file mode 100644 index 0000000..9cb7e5d --- /dev/null +++ b/Sources/DevScopeCore/ProcessAccessAssessment.swift @@ -0,0 +1,142 @@ +import Foundation + +public enum ProcessAccessRequirementKind: String, Sendable { + case processMetadata + case workingDirectories +} + +public enum ProcessAccessRequirementState: String, Sendable { + case ready + case needed + case blocked +} + +public enum ProcessAccessAction: String, Sendable { + case privacySecurity + case fullDiskAccess +} + +public struct ProcessAccessRequirement: Equatable, Sendable { + public let kind: ProcessAccessRequirementKind + public let state: ProcessAccessRequirementState + public let title: String + public let detail: String + public let action: ProcessAccessAction? + + public init( + kind: ProcessAccessRequirementKind, + state: ProcessAccessRequirementState, + title: String, + detail: String, + action: ProcessAccessAction? + ) { + self.kind = kind + self.state = state + self.title = title + self.detail = detail + self.action = action + } +} + +public struct ProcessAccessAssessment: Equatable, Sendable { + public let isSandboxed: Bool + public let processCount: Int + public let currentDirectoryCount: Int + public let scanErrorDescription: String? + public let requirements: [ProcessAccessRequirement] + + public var hasNeededAction: Bool { + requirements.contains { $0.action != nil && $0.state == .needed } + } + + public var hasBlockedRequirement: Bool { + requirements.contains { $0.state == .blocked } + } + + public init( + isSandboxed: Bool, + processCount: Int, + currentDirectoryCount: Int, + scanErrorDescription: String?, + requirements: [ProcessAccessRequirement] + ) { + self.isSandboxed = isSandboxed + self.processCount = processCount + self.currentDirectoryCount = currentDirectoryCount + self.scanErrorDescription = scanErrorDescription + self.requirements = requirements + } + + public static func assess( + isSandboxed: Bool, + processes: [DevProcess]?, + errorDescription: String? + ) -> ProcessAccessAssessment { + let processCount = processes?.count ?? 0 + let currentDirectoryCount = processes?.filter { $0.currentDirectory != nil }.count ?? 0 + var requirements: [ProcessAccessRequirement] = [] + + if isSandboxed { + requirements.append( + ProcessAccessRequirement( + kind: .processMetadata, + state: .blocked, + title: "Process Metadata", + detail: "Blocked by App Sandbox. There is no macOS privacy permission that grants unrestricted process inspection to this build.", + action: nil + ) + ) + return ProcessAccessAssessment( + isSandboxed: isSandboxed, + processCount: processCount, + currentDirectoryCount: currentDirectoryCount, + scanErrorDescription: errorDescription, + requirements: requirements + ) + } + + if processCount > 0 { + requirements.append( + ProcessAccessRequirement( + kind: .processMetadata, + state: .ready, + title: "Process Metadata", + detail: "\(processCount) local processes are visible to DevScope.", + action: nil + ) + ) + } else { + requirements.append( + ProcessAccessRequirement( + kind: .processMetadata, + state: .blocked, + title: "Process Metadata", + detail: errorDescription ?? "macOS is not returning process metadata to DevScope. This is not a known user-grantable Privacy & Security permission.", + action: nil + ) + ) + } + + if processCount > 0 { + requirements.append( + ProcessAccessRequirement( + kind: .workingDirectories, + state: currentDirectoryCount > 0 ? .ready : .needed, + title: "Working Directories", + detail: currentDirectoryCount > 0 + ? "\(currentDirectoryCount) process folders are visible for project grouping." + : "Process rows are visible, but folder context is hidden. Grant Full Disk Access only if you need project folders and richer grouping.", + action: currentDirectoryCount > 0 ? nil : .fullDiskAccess + ) + ) + } + + return ProcessAccessAssessment( + isSandboxed: isSandboxed, + processCount: processCount, + currentDirectoryCount: currentDirectoryCount, + scanErrorDescription: errorDescription, + requirements: requirements + ) + } +} diff --git a/Sources/DevScopeCore/ProcessActionPolicy.swift b/Sources/DevScopeCore/ProcessActionPolicy.swift new file mode 100644 index 0000000..e0aee97 --- /dev/null +++ b/Sources/DevScopeCore/ProcessActionPolicy.swift @@ -0,0 +1,40 @@ +import Foundation + +public enum ProcessActionDecision: Equatable, Sendable { + case allowed + case protected(reason: String) + + public var isAllowed: Bool { + if case .allowed = self { return true } + return false + } + + public var reason: String? { + guard case .protected(let reason) = self else { return nil } + return reason + } +} + +public enum ProcessActionPolicy { + private static let protectedExecutables: Set = [ + "kernel_task", "launchd", "loginwindow", "WindowServer", "runningboardd", + "securityd", "tccd", "opendirectoryd", "powerd", + ] + + public static func decision( + for item: ClassifiedDevProcess, + currentProcessID: Int32 + ) -> ProcessActionDecision { + let process = item.process + if process.pid == currentProcessID { + return .protected(reason: "DevScope cannot terminate itself") + } + if process.pid == 0 || process.pid == 1 || process.executableName == "launchd" { + return .protected(reason: "macOS launch infrastructure is protected") + } + if protectedExecutables.contains(process.executableName) { + return .protected(reason: "Critical macOS system infrastructure is protected") + } + return .allowed + } +} diff --git a/Sources/DevScopeCore/ProcessActionPresentation.swift b/Sources/DevScopeCore/ProcessActionPresentation.swift new file mode 100644 index 0000000..36c5682 --- /dev/null +++ b/Sources/DevScopeCore/ProcessActionPresentation.swift @@ -0,0 +1,76 @@ +public enum ProcessActionControl: CaseIterable, Sendable { + case terminate + case terminateTree + case forceOptions +} + +public struct ProcessActionControlPresentation: Equatable, Sendable { + public let isDisabled: Bool + public let help: String + public let accessibilityHint: String +} + +public enum ProcessActionPresentation { + public static func control( + _ control: ProcessActionControl, + isEnded: Bool, + actionDecision: ProcessActionDecision + ) -> ProcessActionControlPresentation { + if isEnded { + return ProcessActionControlPresentation( + isDisabled: true, + help: "Unavailable: This process has ended.", + accessibilityHint: "Unavailable because the selected process has ended." + ) + } + if let reason = actionDecision.reason { + let guidance = "Unavailable: \(reason)" + return ProcessActionControlPresentation( + isDisabled: true, + help: guidance, + accessibilityHint: guidance + ) + } + + switch control { + case .terminate: + return ProcessActionControlPresentation( + isDisabled: !actionDecision.isAllowed, + help: "Requests confirmation before sending SIGTERM.", + accessibilityHint: "Requests confirmation before sending SIGTERM to the selected process." + ) + case .terminateTree: + return ProcessActionControlPresentation( + isDisabled: !actionDecision.isAllowed, + help: "Requests confirmation before sending SIGTERM to the process tree.", + accessibilityHint: "Requests confirmation before sending SIGTERM to the selected process and its descendants." + ) + case .forceOptions: + return ProcessActionControlPresentation( + isDisabled: !actionDecision.isAllowed, + help: "Force kill options", + accessibilityHint: "Opens destructive SIGKILL options for the selected process." + ) + } + } +} + +public enum ProcessTerminationAction: CaseIterable, Sendable { + case single + case tree + case forceSingle + case forceTree + + public func consequence(pid: Int32, descendantCount: Int) -> String { + switch self { + case .single: + "Sends SIGTERM to PID \(pid). The process may save state before exiting." + case .tree: + "Sends SIGTERM to PID \(pid) after \(descendantCount) descendant process\(descendantCount == 1 ? "" : "es")." + case .forceSingle: + "Sends SIGKILL to PID \(pid). The process cannot save state." + case .forceTree: + "Sends SIGKILL to PID \(pid) and \(descendantCount) descendant process\(descendantCount == 1 ? "" : "es"). None can save state." + } + } +} diff --git a/Sources/DevScopeCore/ProcessCacheIdentity.swift b/Sources/DevScopeCore/ProcessCacheIdentity.swift new file mode 100644 index 0000000..5591b86 --- /dev/null +++ b/Sources/DevScopeCore/ProcessCacheIdentity.swift @@ -0,0 +1,26 @@ +public struct ProcessCacheIdentity: Equatable, Hashable, Sendable { + private enum Lifecycle: Equatable, Hashable, Sendable { + case knownBirth(ProcessBirthToken) + case fallback( + parentPID: Int32, + executable: String, + command: String + ) + } + + public let pid: Int32 + private let lifecycle: Lifecycle + + public init(process: DevProcess) { + pid = process.pid + if let birthToken = process.birthToken { + lifecycle = .knownBirth(birthToken) + } else { + lifecycle = .fallback( + parentPID: process.parentPID, + executable: process.executable, + command: process.command + ) + } + } +} diff --git a/Sources/DevScopeCore/ProcessClassificationCache.swift b/Sources/DevScopeCore/ProcessClassificationCache.swift new file mode 100644 index 0000000..c316efc --- /dev/null +++ b/Sources/DevScopeCore/ProcessClassificationCache.swift @@ -0,0 +1,117 @@ +import Foundation + +public struct ProcessIdentityFingerprint: Hashable, Sendable { + public let parentPID: Int32 + public let executable: String + public let command: String + public let currentDirectory: String? + public let birthToken: ProcessBirthToken? + public let workspaceFacts: WorkspaceFacts? + + public init(process: DevProcess, workspaceFacts: WorkspaceFacts? = nil) { + parentPID = process.parentPID + executable = process.executable + command = process.command + currentDirectory = process.currentDirectory + birthToken = process.birthToken + self.workspaceFacts = workspaceFacts + } +} + +public struct ProcessClassificationCache: Sendable { + private struct Entry: Sendable { + let fingerprint: ProcessIdentityFingerprint + let classification: DevProcessClassification + } + + private var entries: [Int32: Entry] = [:] + + public var cachedProcessIDs: Set { + Set(entries.keys) + } + + public init() {} + + public mutating func invalidateAll() { + entries.removeAll(keepingCapacity: true) + } + + public mutating func classified(_ processes: [DevProcess]) -> [ClassifiedDevProcess] { + classified(processes) { process in + ProcessClassifier.classify(process) + } + } + + public mutating func classified( + _ processes: [DevProcess], + workspaceFactsCache: WorkspaceFactsCache + ) -> [ClassifiedDevProcess] { + classified(processes, workspaceFactsCache: workspaceFactsCache) { process, workspaceFacts in + ProcessClassifier.classify(process, workspaceFacts: workspaceFacts) + } + } + + public mutating func classified( + _ processes: [DevProcess], + workspaceFactsCache: WorkspaceFactsCache, + classifier: (DevProcess, WorkspaceFacts) -> DevProcessClassification? + ) -> [ClassifiedDevProcess] { + workspaceFactsCache.withRevalidationCycle(maximumStaleEntries: 8) { + classified( + processes, + context: { process in + workspaceFactsCache.facts(for: process.currentDirectory) + }, + fingerprint: { process, workspaceFacts in + ProcessIdentityFingerprint(process: process, workspaceFacts: workspaceFacts) + }, + classifier: classifier + ) + } + } + + public mutating func classified( + _ processes: [DevProcess], + classifier: (DevProcess) -> DevProcessClassification? + ) -> [ClassifiedDevProcess] { + classified( + processes, + context: { _ in () }, + fingerprint: { process, _ in + ProcessIdentityFingerprint(process: process) + }, + classifier: { process, _ in + classifier(process) + } + ) + } + + private mutating func classified( + _ processes: [DevProcess], + context: (DevProcess) -> Context, + fingerprint: (DevProcess, Context) -> ProcessIdentityFingerprint, + classifier: (DevProcess, Context) -> DevProcessClassification? + ) -> [ClassifiedDevProcess] { + let activeIDs = Set(processes.map(\.pid)) + entries = entries.filter { activeIDs.contains($0.key) } + + return processes.compactMap { process in + let classificationContext = context(process) + let fingerprint = fingerprint(process, classificationContext) + let classification: DevProcessClassification + + if let cached = entries[process.pid], cached.fingerprint == fingerprint { + classification = cached.classification + } else { + entries[process.pid] = nil + guard let fresh = classifier(process, classificationContext) else { + return nil + } + entries[process.pid] = Entry(fingerprint: fingerprint, classification: fresh) + classification = fresh + } + + return ClassifiedDevProcess(process: process, classification: classification) + } + } +} diff --git a/Sources/DevScopeCore/ProcessClassifier.swift b/Sources/DevScopeCore/ProcessClassifier.swift new file mode 100644 index 0000000..136b894 --- /dev/null +++ b/Sources/DevScopeCore/ProcessClassifier.swift @@ -0,0 +1,954 @@ +import Foundation + +public enum ProcessClassifier { + public static func classify( + _ process: DevProcess, + workspaceFacts: WorkspaceFacts? = nil + ) -> DevProcessClassification? { + let command = process.command.trimmingCharacters(in: .whitespacesAndNewlines) + let tokens = tokenize(command) + let lowerTokens = tokens.map { $0.lowercased() } + let executableName = process.executableName.lowercased() + let haystack = ([executableName] + lowerTokens).joined(separator: " ") + + if isJavaScriptProcess(executableName: executableName, tokens: lowerTokens, haystack: haystack) { + let kind = DevRuntimeKind.javascript + return DevProcessClassification( + kind: kind, + displayName: displayName(for: tokens, lowerTokens: lowerTokens, fallback: process.executableName), + projectHint: projectHint(from: command), + tags: tags(for: kind, executableName: executableName, tokens: lowerTokens, haystack: haystack) + ) + } + + if isPythonProcess(executableName: executableName, tokens: lowerTokens, haystack: haystack) { + let kind = DevRuntimeKind.python + return DevProcessClassification( + kind: kind, + displayName: displayName(for: tokens, lowerTokens: lowerTokens, fallback: process.executableName), + projectHint: projectHint(from: command), + tags: tags(for: kind, executableName: executableName, tokens: lowerTokens, haystack: haystack) + ) + } + + if isNativeBuildProcess( + executableName: executableName, + tokens: lowerTokens, + haystack: haystack, + currentDirectory: process.currentDirectory, + workspaceFacts: workspaceFacts + ) { + let kind = nativeBuildKind(executableName: executableName, tokens: lowerTokens) + return DevProcessClassification( + kind: kind, + displayName: displayName(for: tokens, lowerTokens: lowerTokens, fallback: process.executableName), + projectHint: projectHint(from: process.currentDirectory ?? command), + tags: tags(for: kind, executableName: executableName, tokens: lowerTokens, haystack: haystack) + ) + } + + if isFlutterProcess( + executableName: executableName, + tokens: lowerTokens, + haystack: haystack, + currentDirectory: process.currentDirectory, + workspaceFacts: workspaceFacts + ) { + let kind = DevRuntimeKind.flutter + return DevProcessClassification( + kind: kind, + displayName: flutterDisplayName(tokens: tokens, lowerTokens: lowerTokens, fallback: process.executableName), + projectHint: projectHint(from: process.currentDirectory ?? command), + tags: tags(for: kind, executableName: executableName, tokens: lowerTokens, haystack: haystack) + ) + } + + if isDatabaseProcess(executableName: executableName, tokens: lowerTokens, haystack: haystack) { + let kind = DevRuntimeKind.database + return DevProcessClassification( + kind: kind, + displayName: displayName(for: tokens, lowerTokens: lowerTokens, fallback: process.executableName), + projectHint: projectHint(from: process.currentDirectory ?? command), + tags: tags(for: kind, executableName: executableName, tokens: lowerTokens, haystack: haystack) + ) + } + + if isContainerProcess(executableName: executableName, tokens: lowerTokens, haystack: haystack) { + let kind = DevRuntimeKind.container + return DevProcessClassification( + kind: kind, + displayName: displayName(for: tokens, lowerTokens: lowerTokens, fallback: process.executableName), + projectHint: projectHint(from: process.currentDirectory ?? command), + tags: tags(for: kind, executableName: executableName, tokens: lowerTokens, haystack: haystack) + ) + } + + if isWebServerProcess(executableName: executableName, tokens: lowerTokens, haystack: haystack) { + let kind = DevRuntimeKind.webServer + return DevProcessClassification( + kind: kind, + displayName: displayName(for: tokens, lowerTokens: lowerTokens, fallback: process.executableName), + projectHint: projectHint(from: process.currentDirectory ?? command), + tags: tags(for: kind, executableName: executableName, tokens: lowerTokens, haystack: haystack) + ) + } + + if isAIProcess(executableName: executableName, tokens: lowerTokens, haystack: haystack) { + let kind = DevRuntimeKind.ai + return DevProcessClassification( + kind: kind, + displayName: aiDisplayName(executableName: executableName, tokens: lowerTokens, haystack: haystack, fallback: process.executableName), + projectHint: projectHint(from: process.currentDirectory ?? command), + tags: tags(for: kind, executableName: executableName, tokens: lowerTokens, haystack: haystack) + ) + } + + if isMCPProcess(tokens: lowerTokens, haystack: haystack) { + let kind = DevRuntimeKind.mcp + return DevProcessClassification( + kind: kind, + displayName: displayName(for: tokens, lowerTokens: lowerTokens, fallback: process.executableName), + projectHint: projectHint(from: process.currentDirectory ?? command), + tags: tags(for: kind, executableName: executableName, tokens: lowerTokens, haystack: haystack) + ) + } + + if isDevShellProcess(executableName: executableName, currentDirectory: process.currentDirectory) { + let kind = DevRuntimeKind.shell + return DevProcessClassification( + kind: kind, + displayName: shellDisplayName(executableName, tokens: tokens, lowerTokens: lowerTokens), + projectHint: projectHint(from: process.currentDirectory ?? command), + tags: tags(for: kind, executableName: executableName, tokens: lowerTokens, haystack: haystack) + ) + } + + if isDevWorkspace(process.currentDirectory) { + let kind = DevRuntimeKind.other + return DevProcessClassification( + kind: kind, + displayName: displayName(for: tokens, lowerTokens: lowerTokens, fallback: process.executableName), + projectHint: projectHint(from: process.currentDirectory ?? command), + tags: tags(for: kind, executableName: executableName, tokens: lowerTokens, haystack: haystack) + ) + } + + return generalActivityClassification( + process: process, + executableName: executableName, + tokens: tokens, + lowerTokens: lowerTokens, + haystack: haystack + ) + } + + public static func classified(_ processes: [DevProcess]) -> [ClassifiedDevProcess] { + processes.compactMap { process in + guard let classification = classify(process) else { + return nil + } + return ClassifiedDevProcess(process: process, classification: classification) + } + } + + private static func isJavaScriptProcess(executableName: String, tokens: [String], haystack: String) -> Bool { + let executableMatches = ["node", "npm", "pnpm", "yarn", "bun", "deno", "tsx", "ts-node", "next", "vite", "electron"].contains(executableName) + let commandMatches = tokens.contains("next") || tokens.contains("vite") || tokens.contains("tsx") || tokens.contains("ts-node") || haystack.contains("/.bin/next") || haystack.contains("/.bin/vite") + let appMatches = haystack.contains("visual studio code.app/contents/macos/electron") || + haystack.contains("visual studio code.app/contents/macos/code") || + haystack.contains("cursor.app/contents/macos/cursor") || + haystack.contains("zed.app/contents/macos/zed") + return executableMatches || commandMatches || appMatches + } + + private static func isPythonProcess(executableName: String, tokens: [String], haystack: String) -> Bool { + let executableMatches = [ + "python", + "pytest", + "uvicorn", + "gunicorn", + "jupyter", + "ipykernel", + "torchrun", + "accelerate", + "streamlit", + "gradio", + "mlflow", + "tensorboard" + ].contains(executableName) || executableName.hasPrefix("python3") || executableName.contains("python") + let commandMatches = tokens.contains("pytest") || + tokens.contains("uvicorn") || + tokens.contains("gunicorn") || + tokens.contains("jupyter") || + tokens.contains("torchrun") || + tokens.contains("accelerate") || + tokens.contains("streamlit") || + tokens.contains("gradio") || + tokens.contains("mlflow") || + tokens.contains("tensorboard") || + haystack.contains("ipykernel_launcher") || + haystack.contains(".py") + return executableMatches || commandMatches + } + + private static func isNativeBuildProcess( + executableName: String, + tokens: [String], + haystack: String, + currentDirectory: String?, + workspaceFacts: WorkspaceFacts? + ) -> Bool { + let trustedExecutableMatches = ["swift", "swift-frontend", "sourcekit-lsp", "xcodebuild", "cargo", "rustc", "rustup"].contains(executableName) + let workspaceExecutableMatches = ["go", "air"].contains(executableName) && isDevWorkspace(currentDirectory) + let trustedCommandMatches = tokenIndex(matching: "swift", in: tokens) != nil || tokenIndex(matching: "xcodebuild", in: tokens) != nil || tokenIndex(matching: "cargo", in: tokens) != nil || tokenIndex(matching: "rustc", in: tokens) != nil || haystack.contains("cargo watch") + let appMatches = !isFlutterWorkspace(currentDirectory, workspaceFacts: workspaceFacts) && + ( + haystack.contains("xcode.app") || + haystack.contains("android studio.app") + ) + let workspaceCommandMatches = (isDirectToolInvocation("go", in: tokens) || isDirectToolInvocation("air", in: tokens) || ((tokenIndex(matching: "go", in: tokens) != nil || tokenIndex(matching: "air", in: tokens) != nil) && isDevWorkspace(currentDirectory))) + let executableMatches = trustedExecutableMatches || workspaceExecutableMatches + let commandMatches = trustedCommandMatches || workspaceCommandMatches + return executableMatches || commandMatches || appMatches + } + + private static func nativeBuildKind(executableName: String, tokens: [String]) -> DevRuntimeKind { + if executableName == "swift" || executableName == "swift-frontend" || executableName == "sourcekit-lsp" || executableName == "xcodebuild" || executableName == "xcode" || tokenIndex(matching: "swift", in: tokens) != nil || tokenIndex(matching: "xcodebuild", in: tokens) != nil { + return .swift + } + if executableName == "cargo" || executableName == "rustc" || executableName == "rustup" || tokenIndex(matching: "cargo", in: tokens) != nil || tokenIndex(matching: "rustc", in: tokens) != nil { + return .rust + } + if executableName == "go" || executableName == "air" || tokenIndex(matching: "go", in: tokens) != nil || tokenIndex(matching: "air", in: tokens) != nil { + return .go + } + return .other + } + + private static func isFlutterProcess( + executableName: String, + tokens: [String], + haystack: String, + currentDirectory: String?, + workspaceFacts: WorkspaceFacts? + ) -> Bool { + let executableMatches = ["flutter", "dart", "dartvm"].contains(executableName) + let commandMatches = tokenIndex(matching: "flutter", in: tokens) != nil || + tokenIndex(matching: "dart", in: tokens) != nil || + haystack.contains("flutter_tools.snapshot") || + haystack.contains("frontend_server.dart.snapshot") || + haystack.contains("dartdevc") || + haystack.contains(".dart_tool/") || + haystack.contains("flutter") || + haystack.contains("dart") + let workspaceMatches = isFlutterWorkspace(currentDirectory, workspaceFacts: workspaceFacts) + return executableMatches || commandMatches || workspaceMatches + } + + private static func flutterDisplayName(tokens: [String], lowerTokens: [String], fallback: String) -> String { + if let flutterIndex = tokenIndex(matching: "flutter", in: lowerTokens) { + let command = lowerTokens.dropFirst(flutterIndex + 1).first { !$0.hasPrefix("-") } + return command.map { "flutter \($0)" } ?? "flutter" + } + if let dartIndex = tokenIndex(matching: "dart", in: lowerTokens) { + let command = lowerTokens.dropFirst(dartIndex + 1).first { !$0.hasPrefix("-") } + return command.map { "dart \($0)" } ?? "dart" + } + if let appName = macOSAppDisplayName(in: tokens) { + return appName + } + return fallback + } + + private static func isDatabaseProcess(executableName: String, tokens: [String], haystack: String) -> Bool { + ["postgres", "postmaster", "redis-server", "mongod", "mysql", "mysqld", "mariadbd", "clickhouse", "influxd"].contains(executableName) || tokens.contains("postgres") || tokens.contains("redis-server") || haystack.contains("/postgres") || haystack.contains("/redis") + } + + private static func isContainerProcess(executableName: String, tokens: [String], haystack: String) -> Bool { + ["docker", "dockerd", "containerd", "colima", "podman", "kubectl"].contains(executableName) || tokens.contains("docker") || tokens.contains("kubectl") || haystack.contains("/docker") || haystack.contains("docker.app") || haystack.contains("orbstack.app") + } + + private static func isWebServerProcess(executableName: String, tokens: [String], haystack: String) -> Bool { + ["nginx", "caddy", "httpd", "apache2"].contains(executableName) || tokens.contains("nginx") || tokens.contains("caddy") || haystack.contains("apache") + } + + private static func isAIProcess(executableName: String, tokens: [String], haystack: String) -> Bool { + ["ollama", "llama-server", "lmstudio"].contains(executableName) || tokens.contains("ollama") || haystack.contains("llama") || haystack.contains("lm studio.app") || haystack.contains("ollama.app") + } + + private static func isMCPProcess(tokens: [String], haystack: String) -> Bool { + (tokens.contains("mcp") || tokens.contains("--stdio") || tokens.contains("stdio") || haystack.contains("/mcp/")) && + ( + tokenIndex(matching: "basic-memory", in: tokens) != nil || + tokenIndex(matching: "gk", in: tokens) != nil || + tokenIndex(matching: "node", in: tokens) != nil || + tokenIndex(matching: "npm", in: tokens) != nil || + haystack.contains("skycomputeruseclient") || + haystack.contains("computer use.app") || + haystack.contains("/.codex/") || + haystack.contains("/mcp/") + ) + } + + private static func generalActivityClassification( + process: DevProcess, + executableName: String, + tokens: [String], + lowerTokens: [String], + haystack: String + ) -> DevProcessClassification { + let kind: DevRuntimeKind + if isBrowserProcess(executableName: executableName, haystack: haystack) { + kind = .browser + } else if isMacOSAppProcess(tokens: tokens, haystack: haystack) { + kind = .macApp + } else if isBackgroundAgentProcess(executableName: executableName, haystack: haystack) { + kind = .backgroundAgent + } else if isSystemServiceProcess(haystack: haystack) { + kind = .systemService + } else { + kind = .other + } + + return DevProcessClassification( + kind: kind, + displayName: activityDisplayName( + executableName: executableName, + command: process.command, + tokens: tokens, + lowerTokens: lowerTokens, + fallback: process.executableName + ), + projectHint: isDevWorkspace(process.currentDirectory) ? projectHint(from: process.currentDirectory ?? process.command) : nil, + tags: tags(for: kind, executableName: executableName, tokens: lowerTokens, haystack: haystack) + ) + } + + private static func isBrowserProcess(executableName: String, haystack: String) -> Bool { + let browsers = [ + "safari", + "google chrome", + "google chrome helper", + "firefox", + "firefox helper", + "arc", + "arc helper", + "brave browser", + "brave browser helper", + "microsoft edge", + "microsoft edge helper" + ] + + return browsers.contains(executableName) || + haystack.contains("safari.app") || + haystack.contains("google chrome.app") || + haystack.contains("google chrome helper.app") || + haystack.contains("firefox.app") || + haystack.contains("firefox helper.app") || + haystack.contains("arc.app") || + haystack.contains("arc helper.app") || + haystack.contains("brave browser.app") || + haystack.contains("brave browser helper.app") || + haystack.contains("microsoft edge.app") || + haystack.contains("microsoft edge helper.app") + } + + private static func isMacOSAppProcess(tokens: [String], haystack: String) -> Bool { + macOSAppDisplayName(in: tokens) != nil || haystack.contains(".app/contents/macos/") + } + + private static func isBackgroundAgentProcess(executableName: String, haystack: String) -> Bool { + executableName.contains("agent") || + executableName.contains("helper") || + haystack.contains("/launchagents/") || + haystack.contains("/launchdaemons/") || + haystack.contains("daemon.framework") || + haystack.contains("agent.app") || + haystack.contains("helper.app") + } + + private static func isSystemServiceProcess(haystack: String) -> Bool { + haystack.contains("/system/library/") || + haystack.contains("/usr/libexec/") || + haystack.contains("/usr/sbin/") || + haystack.contains("/sbin/") + } + + private static func activityDisplayName( + executableName: String, + command: String, + tokens: [String], + lowerTokens: [String], + fallback: String + ) -> String { + if isBrowserProcess(executableName: executableName, haystack: ([executableName] + lowerTokens).joined(separator: " ")), + let appName = macOSAppDisplayName(inCommand: command, preferLast: true) ?? lastMacOSAppDisplayName(in: tokens) { + return appName + } + if let appName = macOSAppDisplayName(inCommand: command) ?? macOSAppDisplayName(in: tokens) { + return appName + } + + let commandDisplayName = commandExecutableDisplayName(tokens: tokens) + let fallbackDisplayName = URL(fileURLWithPath: fallback).lastPathComponent + + if let commandDisplayName, + shouldPreferCommandDisplayName(commandDisplayName, over: fallbackDisplayName) { + return commandDisplayName + } + + if !fallbackDisplayName.isEmpty && !isGenericExecutableName(fallbackDisplayName) { + return fallbackDisplayName + } + + if let commandDisplayName { + return commandDisplayName + } + + let executableDisplayName = URL(fileURLWithPath: executableName).lastPathComponent + if !executableDisplayName.isEmpty && !isGenericExecutableName(executableDisplayName) { + return executableDisplayName + } + + return fallbackDisplayName.isEmpty ? fallback : fallbackDisplayName + } + + private static func commandExecutableDisplayName(tokens: [String]) -> String? { + for token in tokens.prefix(4) { + let candidate = URL(fileURLWithPath: token).lastPathComponent + guard !candidate.isEmpty, + !candidate.hasPrefix("-"), + !isGenericExecutableName(candidate) else { + continue + } + return candidate + } + + return nil + } + + private static func shouldPreferCommandDisplayName(_ commandDisplayName: String, over fallbackDisplayName: String) -> Bool { + let fallback = fallbackDisplayName.trimmingCharacters(in: .whitespacesAndNewlines) + guard !fallback.isEmpty else { + return true + } + + let command = commandDisplayName.trimmingCharacters(in: .whitespacesAndNewlines) + let lowerFallback = fallback.lowercased() + let lowerCommand = command.lowercased() + + if lowerFallback == lowerCommand { + return false + } + + if isGenericExecutableName(fallback) { + return true + } + + if fallback.count <= 3 && command.count > fallback.count { + return true + } + + return lowerCommand.hasPrefix(lowerFallback) && command.count > fallback.count + } + + private static func isGenericExecutableName(_ name: String) -> Bool { + let normalized = name.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() + return [ + "a", + "bin", + "contents", + "coreservices", + "frameworks", + "helpers", + "library", + "macos", + "privateframeworks", + "resources", + "support", + "versions", + "xpcservices" + ].contains(normalized) + } + + private static func aiDisplayName(executableName: String, tokens: [String], haystack: String, fallback: String) -> String { + if executableName == "ollama" || tokens.contains("ollama") || haystack.contains("ollama.app") || haystack.contains("/ollama") { + if tokens.contains("serve") { + return "Ollama server" + } + return "Ollama" + } + if executableName == "lmstudio" || tokens.contains("lmstudio") || haystack.contains("lm studio") { + return "LM Studio" + } + if executableName == "llama-server" || tokens.contains("llama-server") { + return "Llama server" + } + return fallback + } + + private static func tags( + for kind: DevRuntimeKind, + executableName: String, + tokens: [String], + haystack: String + ) -> [DevProcessTag] { + var result: [DevProcessTag] = [] + + func add(_ tag: DevProcessTag) { + if !result.contains(tag) { + result.append(tag) + } + } + + if tokens.contains("jupyter") || tokens.contains("ipykernel") || haystack.contains("ipykernel") || haystack.contains("notebook") { + add(.notebook) + } + if tokens.contains("torchrun") || tokens.contains("accelerate") || haystack.contains("train") || haystack.contains("trainer") || haystack.contains("finetune") || haystack.contains("fine-tune") { + add(.training) + } + if haystack.contains("llm") || haystack.contains("llama") || haystack.contains("mistral") || haystack.contains("qwen") || haystack.contains("gemma") || haystack.contains("transformers") || haystack.contains("vllm") || haystack.contains("ollama") || haystack.contains("mlx") { + add(.llm) + } + if haystack.contains("vllm") || haystack.contains("llama") || haystack.contains("ollama") || haystack.contains("mlx") || haystack.contains("transformers") { + add(.inference) + } + if haystack.contains("vllm") || haystack.contains("ollama") || haystack.contains("llama-server") || haystack.contains("llama.cpp") { + add(.llmServer) + } + if haystack.contains("qdrant") || haystack.contains("chroma") || haystack.contains("milvus") || haystack.contains("weaviate") || haystack.contains("faiss") { + add(.vectorDB) + } + if tokens.contains("uvicorn") || tokens.contains("gunicorn") || haystack.contains("fastapi") || haystack.contains("flask") { + add(.api) + } + if tokens.contains("mcp") || haystack.contains("/mcp/") { + add(.mcp) + } + if haystack.contains("mlflow") || haystack.contains("wandb") || haystack.contains("tensorboard") || haystack.contains("optuna") { + add(.experiment) + } + if haystack.contains("streamlit") || haystack.contains("gradio") || haystack.contains("dash") { + add(.dataApp) + } + if kind == .ai && result.isEmpty { + add(.inference) + } + + return result + } + + private static func isDevShellProcess(executableName: String, currentDirectory: String?) -> Bool { + let shellNames = ["zsh", "bash", "fish", "sh", "tmux"] + guard shellNames.contains(executableName), let currentDirectory else { + return false + } + + return isDevWorkspace(currentDirectory) + } + + private static func isDevWorkspace(_ currentDirectory: String?) -> Bool { + guard let currentDirectory else { + return false + } + + let home = NSHomeDirectory() + guard currentDirectory.hasPrefix(home) else { + return false + } + + let workspaceMarkers = [ + "\(home)/dev/", + "\(home)/Developer/", + "\(home)/code/", + "\(home)/Code/", + "\(home)/src/", + "\(home)/workspace/", + "\(home)/.codex/" + ] + return currentDirectory == "\(home)/.codex" || workspaceMarkers.contains { currentDirectory.hasPrefix($0) } + } + + private static func isFlutterWorkspace( + _ currentDirectory: String?, + workspaceFacts: WorkspaceFacts? + ) -> Bool { + if let workspaceFacts { + return workspaceFacts.isFlutterWorkspace + } + + guard let currentDirectory else { + return false + } + + let components = URL(fileURLWithPath: currentDirectory).pathComponents.map { $0.lowercased() } + if components.contains("flutter") || components.contains(".dart_tool") { + return isDevWorkspace(currentDirectory) + } + + return FileManager.default.fileExists(atPath: "\(currentDirectory)/pubspec.yaml") || + FileManager.default.fileExists(atPath: "\(currentDirectory)/.dart_tool/package_config.json") + } + + private static func displayName(for tokens: [String], lowerTokens: [String], fallback: String) -> String { + if let mcpName = mcpDisplayName(tokens: tokens, lowerTokens: lowerTokens) { + return mcpName + } + if let nextIndex = tokenIndex(matching: "next", in: lowerTokens), lowerTokens.dropFirst(nextIndex + 1).contains("dev") { + return "next dev" + } + if lowerTokens.contains("vite") { + return "vite" + } + if let uvicornIndex = tokenIndex(matching: "uvicorn", in: lowerTokens) { + return serverDisplayName(prefix: "uvicorn", tokens: tokens, after: uvicornIndex) + } + if let gunicornIndex = tokenIndex(matching: "gunicorn", in: lowerTokens) { + return serverDisplayName(prefix: "gunicorn", tokens: tokens, after: gunicornIndex) + } + if lowerTokens.contains("pytest") { + return "pytest" + } + if lowerTokens.contains("torchrun") { + return toolScriptDisplayName(prefix: "torchrun", tokens: tokens, lowerTokens: lowerTokens) + } + if lowerTokens.contains("accelerate") { + return toolScriptDisplayName(prefix: "accelerate", tokens: tokens, lowerTokens: lowerTokens) + } + if lowerTokens.contains("streamlit") { + return toolScriptDisplayName(prefix: "streamlit", tokens: tokens, lowerTokens: lowerTokens) + } + if lowerTokens.contains("gradio") { + return toolScriptDisplayName(prefix: "gradio", tokens: tokens, lowerTokens: lowerTokens) + } + if lowerTokens.contains("jupyter") { + return "jupyter" + } + if lowerTokens.contains("mlflow") { + return "mlflow" + } + if lowerTokens.contains("tensorboard") { + return "tensorboard" + } + if lowerTokens.first == "npm", lowerTokens.dropFirst().prefix(2) == ["run", "dev"] { + return "npm run dev" + } + if lowerTokens.first == "pnpm", lowerTokens.contains("dev") { + return "pnpm dev" + } + if lowerTokens.first == "yarn", lowerTokens.contains("dev") { + return "yarn dev" + } + if let moduleName = pythonModuleName(in: tokens, lowerTokens: lowerTokens) { + return "python module \(moduleName)" + } + if isPythonCommand(tokens: lowerTokens) { + return "python command" + } + if let scriptName = pythonScriptName(in: tokens) { + return "python \(scriptName)" + } + if let nodeName = nodeScriptName(in: tokens, lowerTokens: lowerTokens) { + return "node \(nodeName)" + } + if let appName = macOSAppDisplayName(in: tokens) { + return appName + } + if lowerTokens.first == "swift" { + return lowerTokens.dropFirst().first.map { "swift \($0)" } ?? "swift" + } + if lowerTokens.first == "cargo" { + return lowerTokens.dropFirst().first.map { "cargo \($0)" } ?? "cargo" + } + if lowerTokens.first == "go" { + return lowerTokens.dropFirst().first.map { "go \($0)" } ?? "go" + } + return fallback + } + + private static func serverDisplayName(prefix: String, tokens: [String], after index: Int) -> String { + let appToken = tokens.dropFirst(index + 1).first { token in + !token.hasPrefix("-") && !token.contains("/") + } + + guard let appToken else { + return prefix + } + + let appName = appToken + .split(separator: ":") + .first + .map(String.init)? + .split(separator: ".") + .first + .map(String.init) + + return appName.map { "\(prefix) \($0)" } ?? prefix + } + + private static func toolScriptDisplayName(prefix: String, tokens: [String], lowerTokens: [String]) -> String { + guard let toolIndex = tokenIndex(matching: prefix, in: lowerTokens) else { + return prefix + } + + let scriptToken = tokens.dropFirst(toolIndex + 1).first { token in + !token.hasPrefix("-") && + token != "run" && + (token.hasSuffix(".py") || token.hasSuffix(".ipynb") || token.contains("/")) + } + + guard let scriptToken else { + return prefix + } + + let scriptName = URL(fileURLWithPath: scriptToken).deletingPathExtension().lastPathComponent + return scriptName.isEmpty ? prefix : "\(prefix) \(scriptName)" + } + + private static func pythonScriptName(in tokens: [String]) -> String? { + tokens.dropFirst().first { token in + token.hasSuffix(".py") + } + .map { token in + URL(fileURLWithPath: token).deletingPathExtension().lastPathComponent + } + } + + private static func nodeScriptName(in tokens: [String], lowerTokens: [String]) -> String? { + guard let first = lowerTokens.first, + first == "node" || first.hasSuffix("/node") else { + return nil + } + + return tokens.dropFirst().first { token in + !token.hasPrefix("-") + } + .map { token in + URL(fileURLWithPath: token).deletingPathExtension().lastPathComponent + } + } + + private static func mcpDisplayName(tokens: [String], lowerTokens: [String]) -> String? { + let hasMCPSignal = lowerTokens.contains("mcp") || + lowerTokens.contains("--stdio") || + lowerTokens.contains("stdio") || + lowerTokens.contains { $0.contains("/mcp/") } + guard hasMCPSignal else { + return nil + } + + if lowerTokens.first == "npm", + let execIndex = lowerTokens.firstIndex(of: "exec"), + execIndex + 1 < tokens.count { + let packageName = packageName(from: tokens[execIndex + 1]) + return mcpToolName(packageName) + } + + if let basicMemoryIndex = tokenIndex(matching: "basic-memory", in: lowerTokens), + lowerTokens.dropFirst(basicMemoryIndex + 1).contains("mcp") { + return "basic-memory MCP" + } + + if let toolToken = tokens.dropFirst().first(where: { token in + let name = URL(fileURLWithPath: token).lastPathComponent.lowercased() + return name == "xcodebuildmcp" || name == "shadcn" || name == "basic-memory" || name == "gk" || name == "skycomputeruseclient" + }) { + return mcpToolName(URL(fileURLWithPath: toolToken).lastPathComponent) + } + + if lowerTokens.contains(where: { token in + token.contains("/mcp/server") || token == "server.cjs" || token == "server.mjs" || token == "server.bundle.mjs" + }) { + return "MCP server" + } + + return nil + } + + private static func packageName(from token: String) -> String { + let rawName = URL(fileURLWithPath: token).lastPathComponent + if rawName.hasPrefix("@") { + let components = rawName.split(separator: "@", omittingEmptySubsequences: false) + guard components.count > 2 else { + return rawName + } + return components.dropLast().joined(separator: "@") + } + + return rawName.split(separator: "@").first.map(String.init) ?? rawName + } + + private static func mcpToolName(_ packageName: String) -> String { + switch packageName.lowercased() { + case "xcodebuildmcp": + return "xcodebuildmcp" + case "shadcn": + return "shadcn MCP" + case "basic-memory": + return "basic-memory MCP" + case "gk": + return "GitKraken MCP" + case "skycomputeruseclient": + return "Computer Use MCP" + default: + return packageName.lowercased().contains("mcp") ? packageName : "\(packageName) MCP" + } + } + + private static func pythonModuleName(in tokens: [String], lowerTokens: [String]) -> String? { + guard let moduleIndex = lowerTokens.firstIndex(of: "-m"), + moduleIndex + 1 < tokens.count else { + return nil + } + + let moduleName = tokens[moduleIndex + 1] + guard moduleName.range(of: #"^[A-Za-z_][A-Za-z0-9_.-]*$"#, options: .regularExpression) != nil else { + return nil + } + + return moduleName + } + + private static func isPythonCommand(tokens: [String]) -> Bool { + guard tokens.contains("-c") else { + return false + } + + return tokens.contains("python") || + tokens.contains("python3") || + tokens.contains(where: { token in + token.hasSuffix("/python") || + token.hasSuffix("/python3") || + token.contains("/python3.") || + token.hasPrefix("python3.") + }) + } + + private static func macOSAppDisplayName(in tokens: [String]) -> String? { + for token in tokens where token.contains(".app/") || token.hasSuffix(".app") { + let components = URL(fileURLWithPath: token).pathComponents + if let appComponent = components.first(where: { $0.hasSuffix(".app") }) { + let appName = String(appComponent.dropLast(4)) + if !appName.isEmpty { + return appName + } + } + } + + return nil + } + + private static func macOSAppDisplayName(inCommand command: String, preferLast: Bool = false) -> String? { + let appNames = command.split(separator: "/").compactMap { component -> String? in + guard component.hasSuffix(".app") else { + return nil + } + + let appName = component.dropLast(4) + return appName.isEmpty ? nil : String(appName) + } + + return preferLast ? appNames.last : appNames.first + } + + private static func lastMacOSAppDisplayName(in tokens: [String]) -> String? { + for token in tokens.reversed() where token.contains(".app/") || token.hasSuffix(".app") { + let components = URL(fileURLWithPath: token).pathComponents + if let appComponent = components.last(where: { $0.hasSuffix(".app") }) { + let appName = String(appComponent.dropLast(4)) + if !appName.isEmpty { + return appName + } + } + } + + return nil + } + + private static func shellDisplayName( + _ executableName: String, + tokens: [String], + lowerTokens: [String] + ) -> String { + if lowerTokens.contains("python") || lowerTokens.contains("python3") || lowerTokens.contains(where: { $0.hasPrefix("python3.") }) { + if isPythonCommand(tokens: lowerTokens) { + return "python command" + } + if let scriptName = pythonScriptName(in: tokens) { + return "python \(scriptName)" + } + return "python" + } + + let baseName = executableName.hasPrefix("-") ? String(executableName.dropFirst()) : executableName + return URL(fileURLWithPath: baseName).lastPathComponent + } + + private static func tokenIndex(matching needle: String, in tokens: [String]) -> Int? { + tokens.firstIndex { token in + token == needle || token.hasSuffix("/\(needle)") || token.hasSuffix("/.bin/\(needle)") + } + } + + private static func isDirectToolInvocation(_ needle: String, in tokens: [String]) -> Bool { + guard let first = tokens.first else { + return false + } + + return first == needle || first.hasSuffix("/\(needle)") || first.hasSuffix("/.bin/\(needle)") + } + + private static func projectHint(from command: String) -> String? { + let home = NSHomeDirectory() + guard let homeRange = command.range(of: home) else { + return nil + } + + let path = String(command[homeRange.lowerBound...]) + let components = URL(fileURLWithPath: path).pathComponents + if let codexPluginName = codexPluginName(from: components) { + return codexPluginName + } + let markers = ["dev", "Developer", "code", "Code", "src", "workspace"] + + for marker in markers { + guard let index = components.firstIndex(of: marker), + index + 1 < components.count else { + continue + } + let remaining = components[(index + 1)...].filter { !$0.isEmpty && $0 != "/" } + guard let first = remaining.first else { + continue + } + if remaining.count >= 2, ["work", "personal", "github", "projects", "flutter"].contains(first.lowercased()) { + return remaining.dropFirst().first + } + return first + } + + return components.last { !$0.isEmpty && $0 != "/" && !$0.hasPrefix(".") } + } + + private static func codexPluginName(from components: [String]) -> String? { + guard let codexIndex = components.firstIndex(of: ".codex") else { + return nil + } + + if let cacheIndex = components[codexIndex...].firstIndex(of: "cache") { + let pluginComponents = components.dropFirst(cacheIndex + 1).filter { !$0.isEmpty && $0 != "/" } + if pluginComponents.count >= 2, + pluginComponents[0].hasPrefix("openai-") { + return String(pluginComponents[1]) + } + return pluginComponents.first.map { String($0) } + } + + return "Codex" + } + + private static func tokenize(_ command: String) -> [String] { + command + .split(whereSeparator: { $0 == " " || $0 == "\t" || $0 == "\n" }) + .map(String.init) + } +} diff --git a/Sources/DevScopeCore/ProcessIntelligence.swift b/Sources/DevScopeCore/ProcessIntelligence.swift new file mode 100644 index 0000000..717732c --- /dev/null +++ b/Sources/DevScopeCore/ProcessIntelligence.swift @@ -0,0 +1,1020 @@ +import Foundation + +public enum DevWorkflowKind: String, CaseIterable, Sendable { + case aiMLLab = "AI / ML Lab" + case localLLMStack = "Local LLM Stack" + case notebookSession = "Notebook Session" + case trainingRun = "Training Run" + case apiService = "API Service" + case dataApp = "Data App" + case vectorDatabase = "Vector Database" + case buildWorkspace = "Build Workspace" + case webWorkspace = "Web Workspace" + case projectWorkspace = "Project Workspace" + case runtimeGroup = "Runtime Group" + + public var symbolName: String { + switch self { + case .aiMLLab: + "brain" + case .localLLMStack: + "text.bubble" + case .notebookSession: + "book" + case .trainingRun: + "chart.line.uptrend.xyaxis" + case .apiService: + "network" + case .dataApp: + "chart.bar" + case .vectorDatabase: + "square.stack.3d.up" + case .buildWorkspace: + "hammer" + case .webWorkspace: + "curlybraces" + case .projectWorkspace: + "folder" + case .runtimeGroup: + "square.grid.2x2" + } + } +} + +public enum DevWorkflowRisk: String, Comparable, Sendable { + case normal = "Normal" + case busy = "Busy" + case heavy = "Heavy" + + public static func < (lhs: DevWorkflowRisk, rhs: DevWorkflowRisk) -> Bool { + lhs.rank < rhs.rank + } + + private var rank: Int { + switch self { + case .normal: + 0 + case .busy: + 1 + case .heavy: + 2 + } + } +} + +public struct DevWorkflow: Identifiable, Equatable, Sendable { + public let id: String + public let title: String + public let subtitle: String + public let kind: DevWorkflowKind + public let processIDs: [Int32] + public let primaryProject: String? + public let tags: [DevProcessTag] + public let totalCPU: Double + public let totalMemoryBytes: Int64 + public let risk: DevWorkflowRisk + public let confidence: Double + public let summary: String + public let suggestedAction: String + + public var processCount: Int { + processIDs.count + } + + public init( + id: String, + title: String, + subtitle: String, + kind: DevWorkflowKind, + processIDs: [Int32], + primaryProject: String?, + tags: [DevProcessTag], + totalCPU: Double, + totalMemoryBytes: Int64, + risk: DevWorkflowRisk, + confidence: Double, + summary: String, + suggestedAction: String + ) { + self.id = id + self.title = title + self.subtitle = subtitle + self.kind = kind + self.processIDs = processIDs + self.primaryProject = primaryProject + self.tags = tags + self.totalCPU = totalCPU + self.totalMemoryBytes = totalMemoryBytes + self.risk = risk + self.confidence = confidence + self.summary = summary + self.suggestedAction = suggestedAction + } +} + +public struct DevProcessInsight: Equatable, Sendable { + public let title: String + public let role: String + public let resourceBehavior: String + public let workflowContext: String + public let safeAction: String + public let confidence: Double +} + +public enum ProcessIntelligence { + public static let workflowIDPrefix = "workflow:" + private static let maximumWorkflowTitleLength = 72 + private static let maximumWorkflowCount = 12 + + private struct WorkflowProject: Hashable { + let id: String + let title: String + } + + public static func workflows(for items: [ClassifiedDevProcess]) -> [DevWorkflow] { + workflows(for: items, workspaceOwnerComponents: configuredWorkspaceOwnerComponents) + } + + static func uncappedWorkflows(for items: [ClassifiedDevProcess]) -> [DevWorkflow] { + uncappedWorkflows(for: items, workspaceOwnerComponents: configuredWorkspaceOwnerComponents) + } + + static func workflows( + for items: [ClassifiedDevProcess], + workspaceOwnerComponents: Set + ) -> [DevWorkflow] { + presentedWorkflows( + uncappedWorkflows(for: items, workspaceOwnerComponents: workspaceOwnerComponents) + ) + } + + private static func uncappedWorkflows( + for items: [ClassifiedDevProcess], + workspaceOwnerComponents: Set + ) -> [DevWorkflow] { + let aiItems = items.filter(isAIMLRelated) + var workflows: [DevWorkflow] = [] + + if !aiItems.isEmpty { + workflows.append(makeWorkflow( + id: "\(workflowIDPrefix)ai-ml-lab", + title: "AI / ML Lab", + kind: .aiMLLab, + items: aiItems, + primaryProject: primaryProject( + for: aiItems, + workspaceOwnerComponents: workspaceOwnerComponents + ), + summary: "Notebook kernels, training jobs, LLM services, vector stores, APIs, and data apps detected in the current local development session.", + suggestedAction: "Use this view to inspect related AI/ML processes before terminating model servers or kernels." + )) + } + + let llmItems = items.filter { item in + item.classification.kind == .ai || + item.classification.tags.contains(.llmServer) || + item.classification.tags.contains(.vectorDB) + } + if !llmItems.isEmpty { + workflows.append(makeWorkflow( + id: "\(workflowIDPrefix)local-llm-stack", + title: "Local LLM Stack", + kind: .localLLMStack, + items: llmItems, + primaryProject: primaryProject( + for: llmItems, + workspaceOwnerComponents: workspaceOwnerComponents + ), + summary: "Local model servers, inference runtimes, or vector database processes are active.", + suggestedAction: "Check memory pressure before closing model servers; they can be expensive to restart." + )) + } + + let projectGroups = Dictionary(grouping: items) { item in + canonicalProject( + for: item, + workspaceOwnerComponents: workspaceOwnerComponents + ) ?? WorkflowProject(id: "unassigned", title: "Unassigned") + } + + for (project, groupedItems) in projectGroups where shouldCreateProjectWorkflow(project: project, items: groupedItems) { + let kind = workflowKind(for: groupedItems) + workflows.append(makeWorkflow( + id: "\(workflowIDPrefix)project-\(project.id)-\(kind.rawValue.lowercased().replacingOccurrences(of: " ", with: "-"))", + title: workflowTitle(project: project.title, kind: kind), + kind: kind, + items: groupedItems, + primaryProject: project.id == "unassigned" ? nil : project.title, + summary: workflowSummary(project: project.title, kind: kind, items: groupedItems), + suggestedAction: "Inspect the process tree before terminating this workflow; grouped rows may depend on each other." + )) + } + + return unique(workflows).filter { + let title = $0.title.trimmingCharacters(in: .whitespacesAndNewlines) + return !title.isEmpty && title.count <= maximumWorkflowTitleLength + } + } + + static func presentedWorkflows(_ workflows: [DevWorkflow]) -> [DevWorkflow] { + let sortedWorkflows = workflows + .sorted { lhs, rhs in + if lhs.risk != rhs.risk { + return lhs.risk > rhs.risk + } + let lhsKindPriority = workflowKindPriority(lhs.kind) + let rhsKindPriority = workflowKindPriority(rhs.kind) + if lhsKindPriority != rhsKindPriority { + return lhsKindPriority < rhsKindPriority + } + if lhs.primaryProject != rhs.primaryProject { + let projectComparison = (lhs.primaryProject ?? "") + .localizedStandardCompare(rhs.primaryProject ?? "") + if projectComparison != .orderedSame { + return projectComparison == .orderedAscending + } + } + let titleComparison = lhs.title.localizedStandardCompare(rhs.title) + if titleComparison != .orderedSame { + return titleComparison == .orderedAscending + } + return lhs.id < rhs.id + } + + guard sortedWorkflows.count > maximumWorkflowCount else { + return sortedWorkflows + } + + let aggregateWorkflows = sortedWorkflows.filter { + $0.kind == .aiMLLab || $0.kind == .localLLMStack + } + let projectWorkflows = sortedWorkflows.filter { + $0.kind != .aiMLLab && $0.kind != .localLLMStack + } + let retainedIDs = Set( + (aggregateWorkflows + projectWorkflows) + .prefix(maximumWorkflowCount) + .map(\.id) + ) + return sortedWorkflows.filter { retainedIDs.contains($0.id) } + } + + public static func workflow(containing item: ClassifiedDevProcess, in workflows: [DevWorkflow]) -> DevWorkflow? { + workflows.first { $0.processIDs.contains(item.process.pid) } + } + + static func rehydratedWorkflows( + _ workflows: [DevWorkflow], + for items: [ClassifiedDevProcess] + ) -> [DevWorkflow] { + let itemsByPID = Dictionary( + items.map { ($0.process.pid, $0) }, + uniquingKeysWith: { _, latest in latest } + ) + + return workflows.map { workflow in + let workflowItems = workflow.processIDs.compactMap { itemsByPID[$0] } + let totalCPU = workflowItems.reduce(0) { + $0 + ($1.process.resourceUsage?.cpuPercent ?? 0) + } + let totalMemory = workflowItems.reduce(Int64(0)) { + $0 + ($1.process.resourceUsage?.residentMemoryBytes ?? 0) + } + return DevWorkflow( + id: workflow.id, + title: workflow.title, + subtitle: workflowSubtitle( + count: workflow.processIDs.count, + cpu: totalCPU, + memory: totalMemory + ), + kind: workflow.kind, + processIDs: workflow.processIDs, + primaryProject: workflow.primaryProject, + tags: workflow.tags, + totalCPU: totalCPU, + totalMemoryBytes: totalMemory, + risk: workflowRisk(totalCPU: totalCPU, totalMemoryBytes: totalMemory), + confidence: workflow.confidence, + summary: workflow.summary, + suggestedAction: workflow.suggestedAction + ) + } + } + + public static func insight( + for item: ClassifiedDevProcess, + workflow: DevWorkflow?, + familySummary: ProcessFamilySummary?, + metricHistory: [DevProcessMetricSample], + actionDecision: ProcessActionDecision = .allowed + ) -> DevProcessInsight { + let tags = item.classification.tags + let role = processRole(for: item) + let behavior = resourceBehavior(for: item, metricHistory: metricHistory) + let workflowContext = workflow.map { + "\($0.title) includes \($0.processCount) related process\($0.processCount == 1 ? "" : "es")." + } ?? "No larger workflow was detected for this process yet." + let safeAction: String + if let reason = actionDecision.reason { + safeAction = "Protected: \(reason)." + } else if let familySummary, familySummary.descendantCount > 0 { + safeAction = "Prefer TERM Tree only when you intend to stop this entire user workflow; \(familySummary.descendantCount) descendants were detected." + } else if tags.contains(.llmServer) || item.classification.kind == .ai { + safeAction = "Model servers can hold large memory allocations; confirm no clients are active before stopping it." + } else { + safeAction = "TERM is the safest first signal. Use force only when the process ignores TERM." + } + + return DevProcessInsight( + title: processInsightTitle(for: item), + role: role, + resourceBehavior: behavior, + workflowContext: workflowContext, + safeAction: safeAction, + confidence: tags.isEmpty ? 0.72 : 0.88 + ) + } + + private static func isAIMLRelated(_ item: ClassifiedDevProcess) -> Bool { + item.classification.kind == .ai || !item.classification.tags.isEmpty + } + + private static func makeWorkflow( + id: String, + title: String, + kind: DevWorkflowKind, + items: [ClassifiedDevProcess], + primaryProject: String?, + summary: String, + suggestedAction: String + ) -> DevWorkflow { + let items = orderedAggregationItems(items) + let processIDs = items.map(\.process.pid).sorted() + let totalCPU = items.reduce(0) { $0 + ($1.process.resourceUsage?.cpuPercent ?? 0) } + let totalMemory = items.reduce(Int64(0)) { $0 + ($1.process.resourceUsage?.residentMemoryBytes ?? 0) } + let tags = uniqueTags(items.flatMap(\.classification.tags)) + let risk = workflowRisk(totalCPU: totalCPU, totalMemoryBytes: totalMemory) + + return DevWorkflow( + id: id, + title: title, + subtitle: workflowSubtitle(count: processIDs.count, cpu: totalCPU, memory: totalMemory), + kind: kind, + processIDs: processIDs, + primaryProject: primaryProject, + tags: tags, + totalCPU: totalCPU, + totalMemoryBytes: totalMemory, + risk: risk, + confidence: tags.isEmpty ? 0.78 : 0.9, + summary: summary, + suggestedAction: suggestedAction + ) + } + + private static func orderedAggregationItems( + _ items: [ClassifiedDevProcess] + ) -> [ClassifiedDevProcess] { + items.sorted { lhs, rhs in + let left = lhs.process + let right = rhs.process + if left.pid != right.pid { + return left.pid < right.pid + } + if left.birthToken != right.birthToken { + switch (left.birthToken, right.birthToken) { + case let (.some(leftBirth), .some(rightBirth)): + if leftBirth.seconds != rightBirth.seconds { + return leftBirth.seconds < rightBirth.seconds + } + return leftBirth.microseconds < rightBirth.microseconds + case (.none, .some): + return true + case (.some, .none): + return false + case (.none, .none): + break + } + } + if left.parentPID != right.parentPID { + return left.parentPID < right.parentPID + } + if left.executable != right.executable { + return left.executable < right.executable + } + if left.command != right.command { + return left.command < right.command + } + return (left.currentDirectory ?? "") < (right.currentDirectory ?? "") + } + } + + private static func workflowKind(for items: [ClassifiedDevProcess]) -> DevWorkflowKind { + let tags = Set(items.flatMap(\.classification.tags)) + let kinds = Set(items.map(\.classification.kind)) + + if tags.contains(.training) { + return .trainingRun + } + if tags.contains(.notebook) { + return .notebookSession + } + if tags.contains(.dataApp) { + return .dataApp + } + if tags.contains(.api) { + return .apiService + } + if tags.contains(.vectorDB) { + return .vectorDatabase + } + if kinds.contains(.swift) || kinds.contains(.rust) || kinds.contains(.go) || kinds.contains(.flutter) { + return .buildWorkspace + } + if kinds.contains(.javascript) || kinds.contains(.webServer) { + return .webWorkspace + } + return .projectWorkspace + } + + private static func workflowTitle(project: String, kind: DevWorkflowKind) -> String { + if project == "Unassigned" { + return kind.rawValue + } + + let lowerProject = project.lowercased() + switch kind { + case .trainingRun: + if lowerProject == "training" { + return "Training Run" + } + return "\(project) Training" + case .notebookSession: + if lowerProject == "notebook" { + return "Notebook Session" + } + return "\(project) Notebook" + case .apiService: + if lowerProject == "api" { + return "API Service" + } + return "\(project) API" + case .dataApp: + if lowerProject == "data" || lowerProject == "data app" { + return "Data App" + } + return "\(project) Data App" + case .vectorDatabase: + if lowerProject == "vector" || lowerProject == "vector db" || lowerProject == "vector database" { + return "Vector Database" + } + return "\(project) Vector DB" + case .buildWorkspace: + if lowerProject == "build" { + return "Build Workspace" + } + return "\(project) Build" + case .webWorkspace: + if lowerProject == "web" { + return "Web Workspace" + } + return "\(project) Web" + case .projectWorkspace: + return "\(project) Workspace" + case .aiMLLab, .localLLMStack, .runtimeGroup: + return kind.rawValue + } + } + + private static func workflowSummary(project: String, kind: DevWorkflowKind, items: [ClassifiedDevProcess]) -> String { + let processWord = items.count == 1 ? "process" : "processes" + if project == "Unassigned" { + return "\(items.count) related \(processWord) detected without a stable project folder." + } + + return "\(items.count) \(processWord) in \(project) are grouped as \(kind.rawValue.lowercased())." + } + + private static func workflowSubtitle(count: Int, cpu: Double, memory: Int64) -> String { + let memoryMB = Double(memory) / 1_048_576 + let memoryText = memoryMB >= 1024 + ? String(format: "%.1f GB", memoryMB / 1024) + : String(format: "%.0f MB", memoryMB) + let processWord = count == 1 ? "OS process" : "OS processes" + return "\(count) \(processWord) · \(String(format: "%.0f%%", cpu)) CPU · \(memoryText)" + } + + private static func workflowRisk(totalCPU: Double, totalMemoryBytes: Int64) -> DevWorkflowRisk { + if totalCPU >= 80 || totalMemoryBytes >= 4_294_967_296 { + return .heavy + } + if totalCPU >= 25 || totalMemoryBytes >= 1_073_741_824 { + return .busy + } + return .normal + } + + private static func shouldCreateProjectWorkflow(project: WorkflowProject, items: [ClassifiedDevProcess]) -> Bool { + guard project.id != "unassigned", + !isNoisyProjectComponent(project.title), + items.contains(where: isDevelopmentWorkflowCandidate) else { + return false + } + + if items.count > 1 { + return true + } + + guard let item = items.first else { + return false + } + + let totalCPU = item.process.resourceUsage?.cpuPercent ?? 0 + let totalMemory = item.process.resourceUsage?.residentMemoryBytes ?? 0 + if totalCPU >= 25 || totalMemory >= 1_073_741_824 || !item.classification.tags.isEmpty { + return true + } + + if isDevelopmentWorkflowCandidate(item) { + return hasDevelopmentContext(item) + } + + return false + } + + private static func isDevelopmentWorkflowCandidate(_ item: ClassifiedDevProcess) -> Bool { + switch item.classification.kind { + case .javascript, .python, .swift, .rust, .go, .flutter, .java, + .database, .container, .webServer, .ai, .mcp: + return true + case .browser, .macApp, .backgroundAgent, .systemService, .shell, .other: + return false + } + } + + private static func hasDevelopmentContext(_ item: ClassifiedDevProcess) -> Bool { + if let currentDirectory = item.process.currentDirectory, + canonicalProject(fromPath: currentDirectory) != nil { + return true + } + return canonicalProject(fromCommand: item.process.command) != nil + } + + private static func workflowKindPriority(_ kind: DevWorkflowKind) -> Int { + switch kind { + case .aiMLLab: + 0 + case .localLLMStack: + 1 + case .trainingRun: + 2 + case .notebookSession: + 3 + case .dataApp: + 4 + case .apiService: + 5 + case .vectorDatabase: + 6 + case .buildWorkspace: + 7 + case .webWorkspace: + 8 + case .projectWorkspace: + 9 + case .runtimeGroup: + 10 + } + } + + private static func processRole(for item: ClassifiedDevProcess) -> String { + let tags = item.classification.tags + if tags.contains(.training) { + return "Training or fine-tuning workload" + } + if tags.contains(.notebook) { + return "Notebook kernel or interactive research session" + } + if tags.contains(.llmServer) { + return "Local LLM serving process" + } + if tags.contains(.vectorDB) { + return "Vector storage or retrieval service" + } + if tags.contains(.api) { + return "Local API server" + } + if tags.contains(.dataApp) { + return "Interactive data application" + } + if item.classification.kind == .ai { + return "Local AI runtime" + } + return "\(item.classification.kind.rawValue) development process" + } + + private static func processInsightTitle(for item: ClassifiedDevProcess) -> String { + let tags = item.classification.tags + if tags.contains(.training) { + return "Training workload" + } + if tags.contains(.notebook) { + return "Notebook session" + } + if tags.contains(.llmServer) { + return "LLM server" + } + if tags.contains(.vectorDB) { + return "Vector database" + } + if tags.contains(.api) { + return "API service" + } + return item.classification.displayName + } + + private static func resourceBehavior(for item: ClassifiedDevProcess, metricHistory: [DevProcessMetricSample]) -> String { + let cpu = item.process.resourceUsage?.cpuPercent ?? 0 + let memory = item.process.resourceUsage?.residentMemoryBytes ?? 0 + if cpu >= 80 { + return "High CPU now; this process is actively consuming compute." + } + if memory >= 4_294_967_296 { + return "High memory footprint; check whether this is a model, notebook, or build cache." + } + if metricHistory.count >= 3, + let first = metricHistory.first, + let last = metricHistory.last, + last.residentMemoryBytes > first.residentMemoryBytes + 536_870_912 { + return "Memory increased by more than 512 MB during the visible history window." + } + return "Resource usage is within the normal range for the current sample." + } + + private static func primaryProject( + for items: [ClassifiedDevProcess], + workspaceOwnerComponents: Set + ) -> String? { + let counts = Dictionary( + grouping: items.compactMap { + canonicalProject( + for: $0, + workspaceOwnerComponents: workspaceOwnerComponents + )?.title + }, + by: { $0 } + ) + .mapValues(\.count) + return counts.sorted { lhs, rhs in + if lhs.value != rhs.value { + return lhs.value > rhs.value + } + return lhs.key.localizedStandardCompare(rhs.key) == .orderedAscending + } + .first? + .key + } + + private static func canonicalProject( + for item: ClassifiedDevProcess, + workspaceOwnerComponents: Set = configuredWorkspaceOwnerComponents + ) -> WorkflowProject? { + if let currentDirectory = item.process.currentDirectory, + let project = canonicalProject( + fromPath: currentDirectory, + workspaceOwnerComponents: workspaceOwnerComponents + ) { + return project + } + + if let project = canonicalProject( + fromCommand: item.process.command, + workspaceOwnerComponents: workspaceOwnerComponents + ) { + return project + } + + return nil + } + + private static func commandPathCandidates(_ command: String) -> [String] { + let home = NSHomeDirectory() + return commandTokens(command) + .filter { token in + token.hasPrefix(home + "/") && + !token.contains("=") && + !token.contains(".app/Contents/") + } + } + + private static func commandTokens(_ command: String) -> [String] { + var tokens: [String] = [] + var token = "" + var quote: Character? + var requiresBoundary = false + + for character in command { + if let activeQuote = quote { + if character == activeQuote { + quote = nil + requiresBoundary = true + } else { + token.append(character) + } + } else if requiresBoundary { + guard character.isWhitespace else { + return [] + } + appendCommandToken(&token, to: &tokens) + requiresBoundary = false + } else if character == "\"" || character == "'" { + guard token.isEmpty else { + return [] + } + quote = character + } else if character.isWhitespace { + appendCommandToken(&token, to: &tokens) + } else { + token.append(character) + } + } + + guard quote == nil else { + return [] + } + appendCommandToken(&token, to: &tokens) + return tokens + } + + private static func appendCommandToken(_ token: inout String, to tokens: inout [String]) { + guard !token.isEmpty else { + return + } + tokens.append(token) + token.removeAll(keepingCapacity: true) + } + + private static func canonicalProject( + fromCommand command: String, + workspaceOwnerComponents: Set = configuredWorkspaceOwnerComponents + ) -> WorkflowProject? { + commandPathCandidates(command).lazy.compactMap { + canonicalProject( + fromPath: $0, + workspaceOwnerComponents: workspaceOwnerComponents + ) + }.first + } + + static func lexicallyNormalizedAbsolutePath( + _ rawValue: String, + constrainedTo root: String? = nil + ) -> String? { + guard rawValue.hasPrefix("/") else { + return nil + } + + let requiredComponents: [Substring] + if let root { + guard let normalizedRoot = lexicallyNormalizedAbsolutePath(root) else { + return nil + } + requiredComponents = normalizedRoot.split(separator: "/") + } else { + requiredComponents = [] + } + + var components: [Substring] = [] + for component in rawValue.split(separator: "/", omittingEmptySubsequences: true) { + switch component { + case ".": + continue + case "..": + guard components.count > requiredComponents.count else { + return nil + } + components.removeLast() + default: + components.append(component) + let componentIndex = components.count - 1 + if componentIndex < requiredComponents.count, + component != requiredComponents[componentIndex] { + return nil + } + } + } + + guard components.count >= requiredComponents.count else { + return nil + } + guard !components.isEmpty else { + return "/" + } + return "/" + components.joined(separator: "/") + } + + private static func canonicalProject( + fromPath rawValue: String, + workspaceOwnerComponents: Set = configuredWorkspaceOwnerComponents + ) -> WorkflowProject? { + guard let home = lexicallyNormalizedAbsolutePath(NSHomeDirectory()), + let path = lexicallyNormalizedAbsolutePath(rawValue, constrainedTo: home) else { + return nil + } + guard path.hasPrefix(home + "/") else { + return nil + } + + let relativePath = String(path.dropFirst(home.count + 1)) + let components = relativePath.split(separator: "/").map(String.init) + guard !components.isEmpty else { + return nil + } + + let lowerComponents = components.map { $0.lowercased() } + guard !lowerComponents.contains(where: { $0.hasSuffix(".app") }) else { + return nil + } + + let root = lowerComponents[0] + if root == ".codex" { + return canonicalCodexProject(from: components) + } + + guard devWorkspaceMarkers.contains(root) else { + return nil + } + let remaining = Array(components.dropFirst()) + if let component = stableWorkspaceComponent( + from: remaining, + workspaceOwnerComponents: workspaceOwnerComponents + ) { + return WorkflowProject(id: stableID(component), title: readableProjectTitle(component)) + } + + return nil + } + + private static func canonicalCodexProject(from components: [String]) -> WorkflowProject { + if let cacheIndex = components.firstIndex(of: "cache") { + let remaining = Array(components.dropFirst(cacheIndex + 1)) + .filter { !isGenericProjectComponent($0) && !isNoisyProjectComponent($0) } + let pluginComponent = remaining.first { !$0.hasPrefix("openai-") } ?? "codex" + return WorkflowProject(id: "codex-\(stableID(pluginComponent))", title: readableProjectTitle(pluginComponent)) + } + + if components.contains(".agents") { + return WorkflowProject(id: "codex-agents", title: "Codex Agents") + } + + return WorkflowProject(id: "codex", title: "Codex") + } + + private static func stableWorkspaceComponent( + from components: S, + workspaceOwnerComponents: Set + ) -> String? where S.Element == String { + let values = Array(components) + let ownerComponents = Set(workspaceOwnerComponents.map { $0.lowercased() }) + .union(workspaceBucketComponents) + for (index, component) in values.enumerated() { + let lowered = component.lowercased() + if (ownerComponents.contains(lowered) || devWorkspaceMarkers.contains(lowered)), + index + 1 < values.count { + continue + } + if let stableComponent = stableProjectComponent(component) { + return stableComponent + } + } + return nil + } + + private static var devWorkspaceMarkers: Set { + ["apps", "code", "dev", "developer", "projects", "source", "src", "workspace", "workspaces"] + } + + private static var workspaceBucketComponents: Set { + ["example", "github", "personal", "repos", "team", "work"] + } + + private static func stableProjectComponent(_ value: String) -> String? { + guard !isGenericProjectComponent(value), !isNoisyProjectComponent(value) else { + return nil + } + return value + } + + private static var configuredWorkspaceOwnerComponents: Set { + let configured = ProcessInfo.processInfo.environment["DEVSCOPE_WORKSPACE_OWNER_COMPONENTS"] ?? "" + return Set( + configured + .split(separator: ",") + .map { $0.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() } + .filter { !$0.isEmpty } + ) + } + + private static func isGenericProjectComponent(_ value: String) -> Bool { + let lowered = value.lowercased() + return [ + "cache", "plugins", "skills", "runtime", "runtimes", "node_modules", + "dist", "build", ".build", "target", "tmp", "temp", "logs", "lib", + "library", "application support", "contents", "resources" + ].contains(lowered) + } + + private static func isNoisyProjectComponent(_ value: String) -> Bool { + let lowered = value + .trimmingCharacters(in: .whitespacesAndNewlines) + .trimmingCharacters(in: CharacterSet(charactersIn: "()[]{}")) + .lowercased() + if lowered.range(of: #"^\d+\.\d+(\.\d+)?([.-][a-z0-9]+)?$"#, options: .regularExpression) != nil { + return true + } + if lowered.range(of: #"^(python|python3|node|ruby|perl|java|swift|bash|zsh|sh)(\d+(\.\d+)*)?$"#, options: .regularExpression) != nil { + return true + } + if lowered.range(of: #"^[a-f0-9]{7,40}$"#, options: .regularExpression) != nil { + return true + } + if lowered.range(of: #"^kernel-[a-f0-9-]{12,}$"#, options: .regularExpression) != nil { + return true + } + if lowered.count >= 22, lowered.range(of: #"^[a-z0-9_-]+$"#, options: .regularExpression) != nil { + return true + } + return false + } + + private static func readableProjectTitle(_ value: String) -> String { + let words = value + .replacingOccurrences(of: #"[_-]+"#, with: " ", options: .regularExpression) + .split(separator: " ") + .map(String.init) + + guard !words.isEmpty else { + return value + } + + return words.map { word in + let lower = word.lowercased() + switch lower { + case "ai", "ml", "llm", "mcp", "api", "ios", "ui", "gpu", "cpu": + return lower.uppercased() + case "xcodebuildmcp": + return "XcodeBuildMCP" + case "devscope": + return "DevScope" + case "codex": + return "Codex" + default: + return lower.prefix(1).uppercased() + String(lower.dropFirst()) + } + } + .joined(separator: " ") + } + + private static func stableID(_ value: String) -> String { + let canonical = value + .precomposedStringWithCanonicalMapping + .lowercased() + let slug = canonical + .replacingOccurrences(of: #"[^a-z0-9]+"#, with: "-", options: .regularExpression) + .trimmingCharacters(in: CharacterSet(charactersIn: "-")) + + guard slug != canonical else { + return slug + } + + let escaped = canonical.utf8 + .map { String(format: "%02x", $0) } + .joined() + return "\(slug.isEmpty ? "project" : slug)--\(escaped)" + } + + private static func unique(_ workflows: [DevWorkflow]) -> [DevWorkflow] { + var seen = Set() + return workflows.filter { workflow in + seen.insert(workflow.id).inserted + } + } + + private static func uniqueTags(_ tags: [DevProcessTag]) -> [DevProcessTag] { + var seen = Set() + return tags + .sorted { lhs, rhs in + if lhs.id != rhs.id { + return lhs.id < rhs.id + } + if lhs.title != rhs.title { + return lhs.title < rhs.title + } + return lhs.symbolName < rhs.symbolName + } + .filter { tag in + seen.insert(tag.id).inserted + } + } +} diff --git a/Sources/DevScopeCore/ProcessKiller.swift b/Sources/DevScopeCore/ProcessKiller.swift new file mode 100644 index 0000000..318be6b --- /dev/null +++ b/Sources/DevScopeCore/ProcessKiller.swift @@ -0,0 +1,322 @@ +import Darwin +import Foundation + +public enum ProcessTree { + public static func descendants(of rootPID: Int32, in processes: [DevProcess]) -> [DevProcess] { + var childrenByParent: [Int32: [DevProcess]] = [:] + for process in processes { + childrenByParent[process.parentPID, default: []].append(process) + } + + var result: [DevProcess] = [] + var queue = childrenByParent[rootPID] ?? [] + var cursor = 0 + var visited = Set([rootPID]) + + while cursor < queue.count { + let next = queue[cursor] + cursor += 1 + guard visited.insert(next.pid).inserted else { + continue + } + result.append(next) + queue.append(contentsOf: childrenByParent[next.pid] ?? []) + } + + return result + } +} + +public struct ProcessSignalTarget: Equatable, Sendable { + public let expectedIdentity: ProcessIdentity + public let classifiedProcess: ClassifiedDevProcess + + init(classifiedProcess: ClassifiedDevProcess) { + expectedIdentity = ProcessIdentity(process: classifiedProcess.process) + self.classifiedProcess = classifiedProcess + } + + init(expectedProcess: DevProcess, classifiedProcess: ClassifiedDevProcess) { + expectedIdentity = ProcessIdentity(process: expectedProcess) + self.classifiedProcess = classifiedProcess + } +} + +public struct ProcessSignalPlan: Equatable, Sendable { + public let targets: [ProcessSignalTarget] + + public static func single( + _ item: ClassifiedDevProcess, + currentProcessID: Int32 + ) throws -> ProcessSignalPlan { + try validated( + targets: [ProcessSignalTarget(classifiedProcess: item)], + currentProcessID: currentProcessID + ) + } + + public static func tree( + root: ClassifiedDevProcess, + processes: [DevProcess], + classifiedProcesses: [ClassifiedDevProcess], + currentProcessID: Int32 + ) throws -> ProcessSignalPlan { + let classifiedByPID = Dictionary( + classifiedProcesses.map { ($0.process.pid, $0) }, + uniquingKeysWith: { _, latest in latest } + ) + let descendants = ProcessTree.descendants(of: root.process.pid, in: processes) + let descendantTargets = try descendants.reversed().map { process in + guard let classifiedProcess = classifiedByPID[process.pid] else { + throw ProcessKillError.targetClassificationUnavailable(pid: process.pid) + } + let target = ProcessSignalTarget( + expectedProcess: process, + classifiedProcess: classifiedProcess + ) + if target.expectedIdentity.birthToken != nil, + !target.expectedIdentity.hasSameBirthIdentity( + as: ProcessIdentity(process: classifiedProcess.process) + ) { + throw ProcessKillError.targetClassificationMismatch(pid: process.pid) + } + return target + } + return try validated( + targets: descendantTargets + [ProcessSignalTarget(classifiedProcess: root)], + currentProcessID: currentProcessID + ) + } + + private static func validated( + targets: [ProcessSignalTarget], + currentProcessID: Int32 + ) throws -> ProcessSignalPlan { + for target in targets { + guard target.expectedIdentity.birthToken != nil else { + throw ProcessKillError.expectedIdentityUnavailable( + pid: target.expectedIdentity.pid + ) + } + let decision = ProcessActionPolicy.decision( + for: target.classifiedProcess, + currentProcessID: currentProcessID + ) + if let reason = decision.reason { + throw ProcessKillError.protectedTarget( + pid: target.expectedIdentity.pid, + reason: reason + ) + } + } + return ProcessSignalPlan(targets: targets) + } +} + +public struct ProcessKiller: Sendable { + public typealias IdentityResolver = @Sendable (Int32) -> ProcessLiveIdentityResolution + public typealias SignalSender = @Sendable (Int32, Int32) throws -> Void + + private let identityResolver: IdentityResolver + private let signalSender: SignalSender + + public init() { + identityResolver = Self.resolveLiveIdentity + signalSender = Self.sendDarwinSignal + } + + public init( + identityResolver: @escaping IdentityResolver, + signalSender: @escaping SignalSender + ) { + self.identityResolver = identityResolver + self.signalSender = signalSender + } + + public func terminate( + _ item: ClassifiedDevProcess, + currentProcessID: Int32 + ) throws -> [ProcessIdentity] { + let plan = try ProcessSignalPlan.single( + item, + currentProcessID: currentProcessID + ) + return try execute(plan, signal: SIGTERM) + } + + public func forceTerminate( + _ item: ClassifiedDevProcess, + currentProcessID: Int32 + ) throws -> [ProcessIdentity] { + let plan = try ProcessSignalPlan.single( + item, + currentProcessID: currentProcessID + ) + return try execute(plan, signal: SIGKILL) + } + + public func terminateTree( + root: ClassifiedDevProcess, + processes: [DevProcess], + classifiedProcesses: [ClassifiedDevProcess], + currentProcessID: Int32 + ) throws -> [ProcessIdentity] { + let plan = try ProcessSignalPlan.tree( + root: root, + processes: processes, + classifiedProcesses: classifiedProcesses, + currentProcessID: currentProcessID + ) + return try execute(plan, signal: SIGTERM) + } + + public func forceTerminateTree( + root: ClassifiedDevProcess, + processes: [DevProcess], + classifiedProcesses: [ClassifiedDevProcess], + currentProcessID: Int32 + ) throws -> [ProcessIdentity] { + let plan = try ProcessSignalPlan.tree( + root: root, + processes: processes, + classifiedProcesses: classifiedProcesses, + currentProcessID: currentProcessID + ) + return try execute(plan, signal: SIGKILL) + } + + private func validateLiveIdentity(_ expected: ProcessIdentity) throws { + guard expected.birthToken != nil else { + throw ProcessKillError.expectedIdentityUnavailable(pid: expected.pid) + } + switch identityResolver(expected.pid) { + case let .identity(liveIdentity): + guard liveIdentity.birthToken != nil else { + throw ProcessKillError.liveIdentityUnavailable(pid: expected.pid) + } + guard expected.hasSameBirthIdentity(as: liveIdentity) else { + throw ProcessKillError.identityMismatch(pid: expected.pid) + } + case .notRunning: + throw ProcessKillError.targetNotRunning(pid: expected.pid) + case .unverifiable: + throw ProcessKillError.liveIdentityUnavailable(pid: expected.pid) + } + } + + private func execute(_ plan: ProcessSignalPlan, signal: Int32) throws -> [ProcessIdentity] { + let targets = plan.targets.map(\.expectedIdentity) + for target in targets { + try validateLiveIdentity(target) + } + var signaledIdentities: [ProcessIdentity] = [] + for target in targets { + do { + try validateLiveIdentity(target) + try signalSender(target.pid, signal) + signaledIdentities.append(target) + } catch { + guard !signaledIdentities.isEmpty else { + throw error + } + throw ProcessSignalExecutionFailure( + signaledIdentities: signaledIdentities, + failedTarget: target, + underlyingError: error + ) + } + } + return targets + } + + static func resolveLiveIdentity(pid: Int32) -> ProcessLiveIdentityResolution { + var info = proc_bsdinfo() + let size = MemoryLayout.stride + let result = withUnsafeMutablePointer(to: &info) { pointer in + pointer.withMemoryRebound(to: UInt8.self, capacity: size) { buffer in + proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, buffer, Int32(size)) + } + } + + if result == Int32(size), info.pbi_pid > 0 { + return .identity( + ProcessIdentity( + pid: Int32(bitPattern: info.pbi_pid), + birthToken: ProcessBirthToken( + seconds: info.pbi_start_tvsec, + microseconds: info.pbi_start_tvusec + ) + ) + ) + } + + if kill(pid, 0) == 0 || errno == EPERM { + return .unverifiable + } + return errno == ESRCH ? .notRunning : .unverifiable + } + + private static func sendDarwinSignal(pid: Int32, signal: Int32) throws { + guard kill(pid, signal) == 0 else { + throw ProcessKillError.signalFailed(pid: pid, errnoCode: errno) + } + } +} + +public enum ProcessLiveIdentityResolution: Equatable, Sendable { + case identity(ProcessIdentity) + case notRunning + case unverifiable +} + +public struct ProcessSignalExecutionFailure: LocalizedError { + public let signaledIdentities: [ProcessIdentity] + public let failedTarget: ProcessIdentity + public let underlyingError: any Error + + public init( + signaledIdentities: [ProcessIdentity], + failedTarget: ProcessIdentity, + underlyingError: any Error + ) { + self.signaledIdentities = signaledIdentities + self.failedTarget = failedTarget + self.underlyingError = underlyingError + } + + public var errorDescription: String? { + "Signaled \(signaledIdentities.count) process\(signaledIdentities.count == 1 ? "" : "es") before PID \(failedTarget.pid) failed: \(underlyingError.localizedDescription)" + } +} + +public enum ProcessKillError: LocalizedError, Equatable { + case protectedTarget(pid: Int32, reason: String) + case targetClassificationUnavailable(pid: Int32) + case targetClassificationMismatch(pid: Int32) + case expectedIdentityUnavailable(pid: Int32) + case liveIdentityUnavailable(pid: Int32) + case targetNotRunning(pid: Int32) + case identityMismatch(pid: Int32) + case signalFailed(pid: Int32, errnoCode: Int32) + + public var errorDescription: String? { + switch self { + case let .protectedTarget(pid, reason): + "Refused to signal PID \(pid): \(reason)" + case let .targetClassificationUnavailable(pid): + "Refused to signal process tree because PID \(pid) has no current classification" + case let .targetClassificationMismatch(pid): + "Refused to signal process tree because PID \(pid) has a stale classification identity" + case let .expectedIdentityUnavailable(pid): + "Refused to signal PID \(pid) because its original birth identity is unknown" + case let .liveIdentityUnavailable(pid): + "Refused to signal PID \(pid) because its current birth identity could not be read" + case let .targetNotRunning(pid): + "Refused to signal PID \(pid) because the selected process is no longer running" + case let .identityMismatch(pid): + "PID \(pid) no longer belongs to the selected process" + case let .signalFailed(pid, errnoCode): + "Could not signal PID \(pid): \(String(cString: strerror(errnoCode)))" + } + } +} diff --git a/Sources/DevScopeCore/ProcessMetricHistoryStore.swift b/Sources/DevScopeCore/ProcessMetricHistoryStore.swift new file mode 100644 index 0000000..97c0144 --- /dev/null +++ b/Sources/DevScopeCore/ProcessMetricHistoryStore.swift @@ -0,0 +1,56 @@ +import Foundation + +public struct ProcessMetricHistoryStore: Sendable { + private struct Entry: Sendable { + let identity: ProcessCacheIdentity + var samples: [DevProcessMetricSample] + } + + private let limit: Int + private var entries: [Int32: Entry] = [:] + + public init(limit: Int) { + precondition(limit > 0, "Metric history limit must be positive") + self.limit = limit + } + + public mutating func record( + processes: [DevProcess], + gpuMetric: DevGPUMetric?, + timestamp: Date + ) { + let activeProcessIDs = Set(processes.map(\.pid)) + let inactiveProcessIDs = entries.keys.filter { !activeProcessIDs.contains($0) } + for processID in inactiveProcessIDs { + entries.removeValue(forKey: processID) + } + + for process in processes { + let identity = ProcessCacheIdentity(process: process) + if entries[process.pid]?.identity != identity { + entries[process.pid] = Entry(identity: identity, samples: []) + } + guard let usage = process.resourceUsage else { + continue + } + + let emptyEntry = Entry(identity: identity, samples: []) + entries[process.pid, default: emptyEntry].samples.append( + DevProcessMetricSample( + timestamp: timestamp, + cpuPercent: usage.cpuPercent, + residentMemoryBytes: usage.residentMemoryBytes, + gpuPercent: gpuMetric?.utilizationPercent + ) + ) + let overflow = (entries[process.pid]?.samples.count ?? 0) - limit + if overflow > 0 { + entries[process.pid]?.samples.removeFirst(overflow) + } + } + } + + public func history(for processID: Int32) -> [DevProcessMetricSample] { + entries[processID]?.samples ?? [] + } +} diff --git a/Sources/DevScopeCore/ProcessPresentation.swift b/Sources/DevScopeCore/ProcessPresentation.swift new file mode 100644 index 0000000..3f40d22 --- /dev/null +++ b/Sources/DevScopeCore/ProcessPresentation.swift @@ -0,0 +1,692 @@ +import CryptoKit +import Foundation + +public struct DevProcessCategory: Identifiable, Equatable, Sendable { + public let id: String + public let title: String + public let symbolName: String + public let count: Int + + public static func all(count: Int) -> DevProcessCategory { + DevProcessCategory(id: "all", title: "All", symbolName: "scope", count: count) + } +} + +public struct ProcessPresentationSummary: Equatable, Sendable { + public let totalCount: Int + public let countsByKind: [DevRuntimeKind: Int] + public let projectCounts: [String: Int] + + public var projectCount: Int { + projectCounts.count + } + + public var primaryProject: String? { + projectCounts.sorted { lhs, rhs in + if lhs.value != rhs.value { + return lhs.value > rhs.value + } + return lhs.key.localizedStandardCompare(rhs.key) == .orderedAscending + } + .first? + .key + } + + public func count(for kind: DevRuntimeKind) -> Int { + countsByKind[kind, default: 0] + } +} + +public struct ProcessFamilySummary: Equatable, Sendable { + public let parentPID: Int32 + public let childCount: Int + public let descendantCount: Int + + public init(parentPID: Int32, childCount: Int, descendantCount: Int) { + self.parentPID = parentPID + self.childCount = childCount + self.descendantCount = descendantCount + } +} + +public struct ProcessDashboardStats: Equatable, Sendable { + public let visibleCount: Int + public let totalCount: Int + public let aiMLCount: Int + public let runtimeCounts: [DevRuntimeKind: Int] + public let topCPU: ClassifiedDevProcess? + public let topMemory: ClassifiedDevProcess? + public let latestGPUPercent: Double? + + public init( + visibleCount: Int, + totalCount: Int, + aiMLCount: Int, + runtimeCounts: [DevRuntimeKind: Int], + topCPU: ClassifiedDevProcess?, + topMemory: ClassifiedDevProcess?, + latestGPUPercent: Double? + ) { + self.visibleCount = visibleCount + self.totalCount = totalCount + self.aiMLCount = aiMLCount + self.runtimeCounts = runtimeCounts + self.topCPU = topCPU + self.topMemory = topMemory + self.latestGPUPercent = latestGPUPercent + } +} + +public enum ProcessSortOption: String, CaseIterable, Identifiable, Sendable { + case cpuDescending + case memoryDescending + case processNameAscending + case runtimeAscending + case pidAscending + + public var id: String { rawValue } + + public var title: String { + switch self { + case .cpuDescending: + "CPU" + case .memoryDescending: + "Memory" + case .processNameAscending: + "Name" + case .runtimeAscending: + "Kind" + case .pidAscending: + "PID" + } + } + + public var symbolName: String { + switch self { + case .cpuDescending: + "cpu" + case .memoryDescending: + "memorychip" + case .processNameAscending: + "textformat" + case .runtimeAscending: + "square.grid.2x2" + case .pidAscending: + "number" + } + } +} + +public enum ProcessPresentation { + public static let allCategoryID = "all" + public static let favoritesCategoryID = "favorites" + public static let watchedCategoryID = "watched" + + public static func filtered( + _ items: [ClassifiedDevProcess], + categoryID: String, + searchText: String, + favoriteKeys: Set = [], + watchedKeys: Set = [] + ) -> [ClassifiedDevProcess] { + let terms = searchTerms(from: searchText) + + return items.filter { item in + let categoryMatches = + switch categoryID { + case allCategoryID: + true + case favoritesCategoryID: + isSaved(item, in: favoriteKeys) + case watchedCategoryID: + isSaved(item, in: watchedKeys) + default: + categoryID == item.classification.kind.id + } + + guard categoryMatches else { + return false + } + + guard !terms.isEmpty else { + return true + } + + let searchableText = searchableText(for: item) + return terms.allSatisfy { searchableText.contains($0) } + } + } + + public static func categories( + for items: [ClassifiedDevProcess], + favoriteKeys: Set = [], + watchedKeys: Set = [] + ) -> [DevProcessCategory] { + let grouped = Dictionary(grouping: items, by: \.classification.kind) + let favoriteCount = savedCount(in: items, keys: favoriteKeys) + let watchedCount = savedCount(in: items, keys: watchedKeys) + let savedCategories = [ + DevProcessCategory( + id: favoritesCategoryID, title: "Favorites", symbolName: "star.fill", count: favoriteCount), + DevProcessCategory( + id: watchedCategoryID, title: "Watched", symbolName: "eye.fill", count: watchedCount), + ].filter { $0.count > 0 } + let detected = grouped.map { kind, groupedItems in + DevProcessCategory( + id: kind.id, + title: kind.rawValue, + symbolName: kind.symbolName, + count: groupedItems.count + ) + } + .sorted { lhs, rhs in + if lhs.count != rhs.count { + return lhs.count > rhs.count + } + return lhs.title.localizedStandardCompare(rhs.title) == .orderedAscending + } + + return [.all(count: items.count)] + savedCategories + detected + } + + static func savedCount( + in items: [ClassifiedDevProcess], + keys: Set, + matching: (ClassifiedDevProcess, Set) -> Bool = ProcessPresentation.isSaved + ) -> Int { + guard !keys.isEmpty else { return 0 } + return items.filter { matching($0, keys) }.count + } + + public static func sorted( + _ items: [ClassifiedDevProcess], + by option: ProcessSortOption + ) -> [ClassifiedDevProcess] { + items.sorted { lhs, rhs in + switch option { + case .cpuDescending: + if cpu(lhs) != cpu(rhs) { + return cpu(lhs) > cpu(rhs) + } + case .memoryDescending: + if memory(lhs) != memory(rhs) { + return memory(lhs) > memory(rhs) + } + case .processNameAscending: + let comparison = lhs.classification.displayName.localizedStandardCompare( + rhs.classification.displayName) + if comparison != .orderedSame { + return comparison == .orderedAscending + } + case .runtimeAscending: + let comparison = lhs.classification.kind.rawValue.localizedStandardCompare( + rhs.classification.kind.rawValue) + if comparison != .orderedSame { + return comparison == .orderedAscending + } + case .pidAscending: + if lhs.process.pid != rhs.process.pid { + return lhs.process.pid < rhs.process.pid + } + } + + return stableFallback(lhs, rhs) + } + } + + public static func summary(for items: [ClassifiedDevProcess]) -> ProcessPresentationSummary { + var countsByKind: [DevRuntimeKind: Int] = [:] + var projectCounts: [String: Int] = [:] + + for item in items { + countsByKind[item.classification.kind, default: 0] += 1 + if let projectName = projectName(for: item) { + projectCounts[projectName, default: 0] += 1 + } + } + + return ProcessPresentationSummary( + totalCount: items.count, + countsByKind: countsByKind, + projectCounts: projectCounts + ) + } + + public static func dashboardStats( + visibleItems: [ClassifiedDevProcess], + totalItems: [ClassifiedDevProcess], + dashboardMetricHistory: [DevProcessMetricSample] = [] + ) -> ProcessDashboardStats { + let runtimeCounts = Dictionary(grouping: totalItems, by: \.classification.kind) + .mapValues(\.count) + let aiMLCount = totalItems.filter { item in + item.classification.kind == .ai || !item.classification.tags.isEmpty + } + .count + let topCPU = totalItems.max { lhs, rhs in + cpu(lhs) < cpu(rhs) + } + let topMemory = totalItems.max { lhs, rhs in + memory(lhs) < memory(rhs) + } + let latestGPUPercent = dashboardMetricHistory.last { $0.gpuPercent != nil }?.gpuPercent + + return ProcessDashboardStats( + visibleCount: visibleItems.count, + totalCount: totalItems.count, + aiMLCount: aiMLCount, + runtimeCounts: runtimeCounts, + topCPU: topCPU, + topMemory: topMemory, + latestGPUPercent: latestGPUPercent + ) + } + + public static func familySummary( + for process: DevProcess, + in processes: [DevProcess] + ) -> ProcessFamilySummary { + ProcessFamilySummary( + parentPID: process.parentPID, + childCount: processes.filter { $0.parentPID == process.pid }.count, + descendantCount: ProcessTree.descendants(of: process.pid, in: processes).count + ) + } + + public static func searchableText(for item: ClassifiedDevProcess) -> String { + [ + item.classification.displayName, + item.classification.kind.rawValue, + item.classification.projectHint ?? "", + item.process.executable, + item.process.executableName, + item.process.command, + item.process.currentDirectory ?? "", + "\(item.process.pid)", + "\(item.process.parentPID)", + ] + .joined(separator: " ") + .lowercased() + } + + public static func projectName(for item: ClassifiedDevProcess) -> String? { + if let projectHint = item.classification.projectHint, !projectHint.isEmpty { + return projectHint + } + + guard let currentDirectory = item.process.currentDirectory else { + return nil + } + + let name = URL(fileURLWithPath: currentDirectory).lastPathComponent + return name.isEmpty || name == "/" ? nil : name + } + + public static func contextLabel(for item: ClassifiedDevProcess) -> String { + if let projectName = projectName(for: item) { + return projectName + } + + let executableName = item.process.executableName.trimmingCharacters(in: .whitespacesAndNewlines) + let displayName = item.classification.displayName.trimmingCharacters( + in: .whitespacesAndNewlines) + + if executableName.isEmpty + || shouldHideExecutableContext(executableName, displayName: displayName) + { + return item.classification.kind.rawValue + } + + return executableName + } + + public static func executablePath(for item: ClassifiedDevProcess) -> String { + let executableName = item.process.executableName.trimmingCharacters(in: .whitespacesAndNewlines) + let displayName = item.classification.displayName.trimmingCharacters( + in: .whitespacesAndNewlines) + + if shouldHideExecutableContext(executableName, displayName: displayName), + let commandPath = leadingAbsoluteCommandPath(item.process.command) + { + return commandPath + } + + return item.process.executable + } + + public static func identityKey(for item: ClassifiedDevProcess) -> String { + hashedIdentityKey( + executableName: item.process.executableName, + currentDirectory: item.process.currentDirectory ?? "", + command: item.process.command + ) + } + + public static func identityKeys(for item: ClassifiedDevProcess) -> Set { + let fallback = hashedIdentityKey( + executableName: item.process.executableName, + currentDirectory: "", + command: item.process.command + ) + + return [identityKey(for: item), fallback] + } + + private static func hashedIdentityKey( + executableName: String, + currentDirectory: String, + command: String + ) -> String { + let source = [executableName, currentDirectory, command] + .joined(separator: "\u{1F}") + .lowercased() + return hashedIdentityKey(source: source) + } + + private static func hashedIdentityKey(source: String) -> String { + let digest = SHA256.hash(data: Data(source.utf8)) + .map { String(format: "%02x", $0) } + .joined() + return "v2:\(digest)" + } + + public static func isSaved(_ item: ClassifiedDevProcess, in keys: Set) -> Bool { + isSaved(item, in: keys, identityKeys: identityKeys(for:)) + } + + static func isSaved( + _ item: ClassifiedDevProcess, + in keys: Set, + identityKeys: (ClassifiedDevProcess) -> Set + ) -> Bool { + guard !keys.isEmpty else { return false } + return !identityKeys(item).isDisjoint(with: keys) + } + + public static func sanitizedSavedIdentityKeys(_ keys: Set) -> Set { + Set( + keys.compactMap { key in + if key.hasPrefix("v2:"), key.count == 67, + key.dropFirst(3).allSatisfy({ $0.isHexDigit && !$0.isUppercase }) + { + return key + } + + let legacyComponents = key.split( + separator: "\u{1F}", omittingEmptySubsequences: false) + guard legacyComponents.count == 3 else { + return nil + } + return hashedIdentityKey(source: key.lowercased()) + }) + } + + public static func elapsedSeconds(_ value: String) -> Int64 { + let dayAndTime = value.split(separator: "-", maxSplits: 1, omittingEmptySubsequences: false) + guard dayAndTime.count <= 2 else { + return -1 + } + + let days: Int64 + let time: Substring + if dayAndTime.count == 2 { + guard let parsedDays = Int64(dayAndTime[0]), + parsedDays >= 0, + parsedDays <= Int64.max / 86_400 + else { + return -1 + } + days = parsedDays + time = dayAndTime[1] + } else { + days = 0 + time = dayAndTime[0] + } + + let components = time.split(separator: ":", omittingEmptySubsequences: false) + guard (2...3).contains(components.count), + let seconds = Int64(components[components.count - 1]), + let minutes = Int64(components[components.count - 2]), + seconds >= 0, seconds < 60, + minutes >= 0, minutes < 60 + else { + return -1 + } + let hours = components.count == 3 ? Int64(components[0]) : 0 + guard let hours, hours >= 0, hours < 24 else { + return -1 + } + return days * 86_400 + hours * 3_600 + minutes * 60 + seconds + } + + public static func redactedCommand(_ command: String) -> String { + let commandWithRedactedHeaders = redactingSensitiveHeaderValues(in: command) + let sensitiveReplacements = [ + ( + #"(?i)(?" + ), + ( + #"(?i)(?" + ), + ( + #"(?i)\b(bearer)(\s+)([A-Za-z0-9._~+/\-=]+)"#, + "$1$2" + ), + ( + #"([A-Za-z][A-Za-z0-9+.-]*://)([^/\s:@]+):([^/\s@]+)@"#, + "$1@" + ), + ( + #"(?i)(\bredis-cli\b[^\r\n]*?\s-a)(\s+).*?(?=[\r\n]|$)"#, + "$1$2" + ), + (#"(?i)(\bmysql\b[^\r\n]*?\s-p).*?(?=[\r\n]|$)"#, "$1"), + ( + #"(?i)(\bcurl\b[^\r\n]*?\s--(?:user|proxy-user))(=|\s+).*?(?=[\r\n]|$)"#, + "$1$2" + ), + ( + #"(?i)(\bcurl\b[^\r\n]*?\s-u)(\s+).*?(?=[\r\n]|$)"#, + "$1$2" + ), + ] + + return sensitiveReplacements.reduce(commandWithRedactedHeaders) { partial, replacement in + partial.replacingOccurrences( + of: replacement.0, + with: replacement.1, + options: .regularExpression + ) + } + } + + private static func redactingSensitiveHeaderValues(in command: String) -> String { + let headerPattern = #"(?i)\b((?:proxy-)?authorization|cookie|set-cookie|[A-Za-z0-9_-]*(?:token|api[-_]?key|secret|credential)[A-Za-z0-9_-]*)(\s*:\s*)((?:basic|bearer|digest|negotiate|aws4-hmac-sha256)\s+)?"# + guard let regex = try? NSRegularExpression(pattern: headerPattern) else { + return command + } + + let redacted = NSMutableString(string: command) + let replacement = "" + var searchLocation = 0 + + while searchLocation < redacted.length { + let current = redacted as String + let searchRange = NSRange(location: searchLocation, length: redacted.length - searchLocation) + guard let match = regex.firstMatch(in: current, range: searchRange) else { + break + } + + let valueStart = NSMaxRange(match.range) + let enclosingQuote = activeShellQuote(at: match.range.location, in: redacted) + let valueEnd = sensitiveHeaderValueEnd( + from: valueStart, + enclosingQuote: enclosingQuote, + in: redacted + ) + redacted.replaceCharacters( + in: NSRange(location: valueStart, length: valueEnd - valueStart), + with: replacement + ) + searchLocation = valueStart + replacement.utf16.count + if enclosingQuote != nil, searchLocation < redacted.length { + searchLocation += 1 + } + } + + return redacted as String + } + + private static func activeShellQuote(at location: Int, in value: NSString) -> unichar? { + var quote: unichar? + var index = 0 + + while index < location { + let character = value.character(at: index) + if quote == 0x27 { + if character == 0x27 { + quote = nil + } + index += 1 + continue + } + if character == 0x5C { + index += 2 + continue + } + if character == 0x22 { + quote = quote == 0x22 ? nil : 0x22 + } else if character == 0x27, quote == nil { + quote = 0x27 + } + index += 1 + } + + return quote + } + + private static func sensitiveHeaderValueEnd( + from start: Int, + enclosingQuote: unichar?, + in value: NSString + ) -> Int { + var index = start + while index < value.length { + let character = value.character(at: index) + if let enclosingQuote { + if enclosingQuote == 0x22, character == 0x5C { + index += 2 + continue + } + if character == enclosingQuote { + return index + } + } else { + if character == 0x5C { + index += 2 + continue + } + if character == 0x09 || character == 0x0A || character == 0x0D || character == 0x20 { + return index + } + } + index += 1 + } + return value.length + } + + public static func exportRows(_ items: [ClassifiedDevProcess]) -> String { + let header = "pid\tppid\tkind\tcpu\tmemory\tproject\tprocess\tcommand" + let rows = items.map { item in + [ + "\(item.process.pid)", + "\(item.process.parentPID)", + item.classification.kind.rawValue, + String(format: "%.1f", item.process.resourceUsage?.cpuPercent ?? 0), + "\(item.process.resourceUsage?.residentMemoryBytes ?? 0)", + exportCell(projectName(for: item) ?? ""), + exportCell(item.classification.displayName), + exportCell(redactedCommand(item.process.command)), + ].joined(separator: "\t") + } + + return ([header] + rows).joined(separator: "\n") + } + + private static func exportCell(_ value: String) -> String { + let singleLine = + value + .replacingOccurrences(of: "\t", with: " ") + .replacingOccurrences(of: "\r", with: " ") + .replacingOccurrences(of: "\n", with: " ") + guard let first = singleLine.first, "=+-@".contains(first) else { + return singleLine + } + return "'" + singleLine + } + + private static func shouldHideExecutableContext(_ executableName: String, displayName: String) + -> Bool + { + let executable = executableName.lowercased() + let display = displayName.lowercased() + + if [ + "a", + "bin", + "contents", + "coreservices", + "frameworks", + "helpers", + "library", + "macos", + "privateframeworks", + "resources", + "support", + "versions", + "xpcservices", + ].contains(executable) { + return true + } + + return executable.count <= 3 && display.hasPrefix(executable) + && display.count > executable.count + } + + private static func leadingAbsoluteCommandPath(_ command: String) -> String? { + let trimmed = command.trimmingCharacters(in: .whitespacesAndNewlines) + guard trimmed.hasPrefix("/") else { + return nil + } + + return trimmed.split(whereSeparator: { $0.isWhitespace }).first.map(String.init) + } + + private static func searchTerms(from searchText: String) -> [String] { + searchText + .split(whereSeparator: { $0.isWhitespace }) + .map { $0.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() } + .filter { !$0.isEmpty } + } + + private static func cpu(_ item: ClassifiedDevProcess) -> Double { + item.process.resourceUsage?.cpuPercent ?? 0 + } + + private static func memory(_ item: ClassifiedDevProcess) -> Int64 { + item.process.resourceUsage?.residentMemoryBytes ?? 0 + } + + private static func stableFallback(_ lhs: ClassifiedDevProcess, _ rhs: ClassifiedDevProcess) + -> Bool + { + let nameComparison = lhs.classification.displayName.localizedStandardCompare( + rhs.classification.displayName) + if nameComparison != .orderedSame { + return nameComparison == .orderedAscending + } + return lhs.process.pid < rhs.process.pid + } +} diff --git a/Sources/DevScopeCore/ProcessScanner.swift b/Sources/DevScopeCore/ProcessScanner.swift new file mode 100644 index 0000000..467e35c --- /dev/null +++ b/Sources/DevScopeCore/ProcessScanner.swift @@ -0,0 +1,733 @@ +import Darwin +import Foundation + +public protocol ProcessProviding: Sendable { + func snapshot(includeCurrentDirectories: Bool) throws -> [DevProcess] +} + +protocol NativeProcessMetadataProviding: Sendable { + func metadata(for processID: Int32) -> DevProcess? +} + +protocol ProcessArgumentVectorProviding: Sendable { + func argumentVector(for processID: Int32) -> [String]? +} + +protocol ProcessBirthTokenProviding: Sendable { + func birthToken(for processID: Int32) -> ProcessBirthToken? +} + +struct KernelProcessBirthTokenProvider: ProcessBirthTokenProviding { + func birthToken(for processID: Int32) -> ProcessBirthToken? { + guard processID > 0 else { return nil } + var info = proc_bsdinfo() + let size = MemoryLayout.stride + let result = withUnsafeMutablePointer(to: &info) { pointer in + pointer.withMemoryRebound(to: UInt8.self, capacity: size) { buffer in + proc_pidinfo(processID, PROC_PIDTBSDINFO, 0, buffer, Int32(size)) + } + } + guard result == Int32(size), info.pbi_pid > 0 else { return nil } + return ProcessBirthToken( + seconds: info.pbi_start_tvsec, + microseconds: info.pbi_start_tvusec + ) + } +} + +struct KernelProcessArgumentVectorProvider: ProcessArgumentVectorProviding { + private static let maximumBufferSize = 1_048_576 + private static let maximumArgumentCount: Int32 = 4_096 + + func argumentVector(for processID: Int32) -> [String]? { + guard processID > 0 else { return nil } + var mib = [Int32(CTL_KERN), Int32(KERN_PROCARGS2), processID] + var requiredSize = 0 + guard sysctl(&mib, u_int(mib.count), nil, &requiredSize, nil, 0) == 0, + requiredSize >= MemoryLayout.size, + requiredSize <= Self.maximumBufferSize else { return nil } + var buffer = [UInt8](repeating: 0, count: requiredSize) + let status = buffer.withUnsafeMutableBytes { rawBuffer in + sysctl(&mib, u_int(mib.count), rawBuffer.baseAddress, &requiredSize, nil, 0) + } + guard status == 0, requiredSize <= buffer.count else { return nil } + return Self.parse(buffer: Array(buffer.prefix(requiredSize))) + } + + static func parse(buffer: [UInt8]) -> [String]? { + guard buffer.count >= MemoryLayout.size, + buffer.count <= maximumBufferSize else { return nil } + var argumentCount: Int32 = 0 + withUnsafeMutableBytes(of: &argumentCount) { destination in + buffer.prefix(MemoryLayout.size).withUnsafeBytes { source in + destination.copyBytes(from: source) + } + } + guard argumentCount > 0, argumentCount <= maximumArgumentCount else { return nil } + + var cursor = MemoryLayout.size + guard let executableEnd = buffer[cursor...].firstIndex(of: 0), + executableEnd > cursor, + let executable = String(bytes: buffer[cursor.. Bool { + let bytes = Array(value.utf8) + guard let equals = bytes.firstIndex(of: 61), equals > 0 else { return false } + let name = bytes[.. String? +} + +struct FoundationProcessBundleIdentityProvider: ProcessBundleIdentityProviding { + func bundleIdentifier(forExecutableURL executableURL: URL, processID _: Int32) -> String? { + let executable = executableURL.resolvingSymlinksInPath().standardizedFileURL + var candidate = executable.deletingLastPathComponent() + while candidate.path != "/" { + if candidate.pathExtension.caseInsensitiveCompare("app") == .orderedSame { + if let bundle = Bundle(url: candidate), + let bundleExecutable = bundle.executableURL?.resolvingSymlinksInPath().standardizedFileURL, + bundleExecutable.path == executable.path, + let identifier = bundle.bundleIdentifier, + !identifier.isEmpty { + return identifier + } + } + candidate.deleteLastPathComponent() + } + return nil + } +} + +public enum ProcessScanner { + static func bundleIdentifier( + forExecutableURL executableURL: URL, + processID: Int32, + provider: any ProcessBundleIdentityProviding + ) -> String? { + provider.bundleIdentifier(forExecutableURL: executableURL, processID: processID) + } + + public static func parsePSLine(_ line: String) -> DevProcess? { + let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines) + guard !trimmed.isEmpty else { + return nil + } + + let parts = trimmed.split(separator: " ", maxSplits: 6, omittingEmptySubsequences: true) + guard parts.count >= 3, + let pid = Int32(parts[0]), + let parentPID = Int32(parts[1]) else { + return nil + } + + if parts.count >= 6, + let cpuPercent = Double(parts[2]), + let residentMemoryKilobytes = Int64(parts[3]) { + let elapsedTime = String(parts[4]) + let executable = String(parts[5]) + let command = parts.count == 7 ? String(parts[6]) : executable + return DevProcess( + pid: pid, + parentPID: parentPID, + executable: executable, + command: command, + resourceUsage: DevProcessResourceUsage( + cpuPercent: cpuPercent, + residentMemoryBytes: residentMemoryKilobytes * 1024, + elapsedTime: elapsedTime + ) + ) + } + + let legacyParts = trimmed.split(separator: " ", maxSplits: 3, omittingEmptySubsequences: true) + guard legacyParts.count >= 3 else { + return nil + } + + let executable = String(legacyParts[2]) + let command = legacyParts.count == 4 ? String(legacyParts[3]) : executable + return DevProcess(pid: pid, parentPID: parentPID, executable: executable, command: command) + } + + public static func parseLsofCurrentDirectories(_ output: String) -> [Int32: String] { + var currentPID: Int32? + var result: [Int32: String] = [:] + + for line in output.split(separator: "\n", omittingEmptySubsequences: true) { + if line.hasPrefix("p") { + currentPID = Int32(line.dropFirst()) + } else if line.hasPrefix("n"), let currentPID { + result[currentPID] = String(line.dropFirst()) + } + } + + return result + } + + static func excludingScannerHelper( + from processes: [DevProcess], + helperPID: Int32 + ) -> [DevProcess] { + processes.filter { $0.pid != helperPID } + } +} + +public final class SystemProcessScanner: @unchecked Sendable, ProcessProviding { + private struct NativeMetadataIdentity: Equatable { + let parentPID: Int32 + let executable: String + let command: String + let birthToken: ProcessBirthToken? + } + + private struct NativeMetadataCacheEntry { + let identity: NativeMetadataIdentity + let process: DevProcess? + let attemptedAt: Date + } + + private static let negativeMetadataRetryInterval: TimeInterval = 120 + private static let negativeMetadataRetryBudget = 8 + private let fallbackScanner: NativeProcessScanner + private let nativeMetadataProvider: any NativeProcessMetadataProviding + private let commandRunner: any SystemCommandRunning + private let lock = NSLock() + private var cachedNativeMetadata: [Int32: NativeMetadataCacheEntry] = [:] + + public init() { + let nativeScanner = NativeProcessScanner() + fallbackScanner = nativeScanner + nativeMetadataProvider = nativeScanner + commandRunner = BoundedSystemCommandRunner() + } + + init( + nativeMetadataProvider: any NativeProcessMetadataProviding, + commandRunner: any SystemCommandRunning = BoundedSystemCommandRunner() + ) { + fallbackScanner = NativeProcessScanner() + self.nativeMetadataProvider = nativeMetadataProvider + self.commandRunner = commandRunner + } + + public func snapshot(includeCurrentDirectories: Bool = true) throws -> [DevProcess] { + let result: BoundedSystemCommandResult + do { + result = try commandRunner.run( + executableURL: URL(fileURLWithPath: "/bin/ps"), + arguments: ["-axo", "pid=,ppid=,%cpu=,rss=,etime=,comm=,args="] + ) + } catch { + return try fallbackScanner.snapshot(includeCurrentDirectories: false) + } + let output = String(decoding: result.standardOutput, as: UTF8.self) + let errorOutput = String(decoding: result.standardError, as: UTF8.self) + + guard result.status == 0 else { + if errorOutput.localizedCaseInsensitiveContains("operation not permitted") { + return try fallbackScanner.snapshot(includeCurrentDirectories: false) + } + + throw ProcessScannerError.psFailed(status: result.status, output: errorOutput) + } + + let psProcesses = ProcessScanner.excludingScannerHelper( + from: output + .split(separator: "\n", omittingEmptySubsequences: true) + .compactMap { ProcessScanner.parsePSLine(String($0)) }, + helperPID: result.processIdentifier ?? -1 + ) + guard !psProcesses.isEmpty else { + return try fallbackScanner.snapshot(includeCurrentDirectories: false) + } + + let processIDs = Set(psProcesses.map(\.pid)) + let birthTokens = fallbackScanner.birthTokens(for: processIDs) + let nativeByPID = nativeMetadata(for: psProcesses, birthTokens: birthTokens) + let currentDirectories = includeCurrentDirectories ? currentDirectoryMap() : [:] + + return psProcesses.map { process in + ProcessScanner.mergedProcess( + psProcess: process, + nativeProcess: nativeByPID[process.pid], + currentDirectory: currentDirectories[process.pid], + birthToken: birthTokens[process.pid] + ) + } + } + + func nativeMetadata( + for processes: [DevProcess], + birthTokens: [Int32: ProcessBirthToken], + now: Date = Date() + ) -> [Int32: DevProcess] { + lock.withLock { + let activeProcessIDs = Set(processes.map(\.pid)) + let inactiveProcessIDs = cachedNativeMetadata.keys.filter { !activeProcessIDs.contains($0) } + for processID in inactiveProcessIDs { + cachedNativeMetadata.removeValue(forKey: processID) + } + + let expiredNegativeRetries = processes.compactMap { process -> (processID: Int32, attemptedAt: Date)? in + let identity = NativeMetadataIdentity( + parentPID: process.parentPID, + executable: process.executable, + command: process.command, + birthToken: birthTokens[process.pid] + ) + guard let cached = cachedNativeMetadata[process.pid], + cached.identity == identity, + cached.process == nil, + now.timeIntervalSince(cached.attemptedAt) >= Self.negativeMetadataRetryInterval else { + return nil + } + return (process.pid, cached.attemptedAt) + } + .sorted { lhs, rhs in + if lhs.attemptedAt != rhs.attemptedAt { + return lhs.attemptedAt < rhs.attemptedAt + } + return lhs.processID < rhs.processID + } + let negativeRetryProcessIDs = Set( + expiredNegativeRetries.prefix(Self.negativeMetadataRetryBudget).map(\.processID) + ) + + var result: [Int32: DevProcess] = [:] + for process in processes { + let identity = NativeMetadataIdentity( + parentPID: process.parentPID, + executable: process.executable, + command: process.command, + birthToken: birthTokens[process.pid] + ) + let metadata: DevProcess? + let cached = cachedNativeMetadata[process.pid] + let hasStableCachedIdentity = cached.map { $0.identity == identity } ?? false + let shouldQuery = !hasStableCachedIdentity || + negativeRetryProcessIDs.contains(process.pid) + if shouldQuery { + let candidate = nativeMetadataProvider.metadata(for: process.pid) + metadata = candidate.flatMap { nativeProcess in + guard nativeProcess.pid == process.pid, + let birthToken = birthTokens[process.pid], + nativeProcess.birthToken == birthToken else { + return nil + } + return nativeProcess + } + cachedNativeMetadata[process.pid] = NativeMetadataCacheEntry( + identity: identity, + process: metadata, + attemptedAt: now + ) + } else { + metadata = cached?.process + } + + if let metadata { + result[process.pid] = metadata + } + } + return result + } + } + + private func currentDirectoryMap() -> [Int32: String] { + let result: BoundedSystemCommandResult + do { + result = try commandRunner.run( + executableURL: URL(fileURLWithPath: "/usr/sbin/lsof"), + arguments: ["-a", "-d", "cwd", "-n", "-w", "-F", "pcn"] + ) + } catch { + return [:] + } + + guard result.status == 0 else { + return [:] + } + + return ProcessScanner.parseLsofCurrentDirectories( + String(decoding: result.standardOutput, as: UTF8.self) + ) + } +} + +extension ProcessScanner { + static func mergedProcess( + psProcess: DevProcess, + nativeProcess: DevProcess?, + currentDirectory: String?, + birthToken: ProcessBirthToken? + ) -> DevProcess { + let nativeProcess = nativeProcess.flatMap { nativeProcess -> DevProcess? in + guard let birthToken, + nativeProcess.birthToken == birthToken else { + return nil + } + return nativeProcess + } + + guard let nativeProcess else { + return DevProcess( + pid: psProcess.pid, + parentPID: psProcess.parentPID, + executable: psProcess.executable, + command: psProcess.command, + argumentVector: nil, + currentDirectory: currentDirectory, + resourceUsage: psProcess.resourceUsage, + birthToken: birthToken, + bundleIdentifier: nil, + launchLabel: nil + ) + } + + let nativeExecutable = nativeProcess.command.contains("/") ? nativeProcess.command : nativeProcess.executable + let useNativeExecutable = shouldPreferNativeExecutable( + psExecutable: psProcess.executable, + nativeExecutable: nativeExecutable + ) + let mergedCommand = shouldPreferNativeCommand( + psCommand: psProcess.command, + psExecutable: psProcess.executable + ) && useNativeExecutable ? nativeExecutable : psProcess.command + + return DevProcess( + pid: psProcess.pid, + parentPID: psProcess.parentPID, + executable: useNativeExecutable ? nativeExecutable : psProcess.executable, + command: mergedCommand, + argumentVector: nativeProcess.argumentVector, + currentDirectory: currentDirectory, + resourceUsage: psProcess.resourceUsage, + birthToken: birthToken, + bundleIdentifier: nativeProcess.bundleIdentifier, + launchLabel: nativeProcess.launchLabel + ) + } + + public static func carriedCurrentDirectory( + from previousProcess: DevProcess, + to currentProcess: DevProcess + ) -> String? { + guard let currentDirectory = previousProcess.currentDirectory, + let previousBirthToken = previousProcess.birthToken, + let currentBirthToken = currentProcess.birthToken, + previousBirthToken == currentBirthToken else { + return nil + } + return currentDirectory + } + + static func shouldPreferNativeExecutable(psExecutable: String, nativeExecutable: String) -> Bool { + let psExecutable = psExecutable.trimmingCharacters(in: .whitespacesAndNewlines) + let nativeExecutable = nativeExecutable.trimmingCharacters(in: .whitespacesAndNewlines) + guard !nativeExecutable.isEmpty else { + return false + } + + let psName = lexicalLastPathComponent(psExecutable) + guard !psName.isEmpty else { + return true + } + + if isGenericExecutableName(psName) { + return true + } + + let lowerPSName = psName.lowercased() + let nativeName = lexicalLastPathComponent(nativeExecutable) + let lowerNativeName = nativeName.lowercased() + return lowerNativeName.hasPrefix(lowerPSName) && nativeName.count > psName.count + } + + private static func shouldPreferNativeCommand(psCommand: String, psExecutable: String) -> Bool { + let command = psCommand.trimmingCharacters(in: .whitespacesAndNewlines) + let executable = psExecutable.trimmingCharacters(in: .whitespacesAndNewlines) + + guard !command.isEmpty else { + return true + } + + if command == executable { + return true + } + + if !command.contains("/"), command.split(whereSeparator: { $0.isWhitespace }).count == 1 { + return true + } + + return false + } + + private static func lexicalLastPathComponent(_ path: String) -> String { + path.split(separator: "/", omittingEmptySubsequences: true).last.map(String.init) ?? "" + } + + private static func isGenericExecutableName(_ name: String) -> Bool { + [ + "a", + "bin", + "contents", + "coreservices", + "frameworks", + "helpers", + "library", + "macos", + "privateframeworks", + "resources", + "support", + "versions", + "xpcservices" + ].contains(name.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()) + } +} + +public struct NativeProcessScanner: ProcessProviding, NativeProcessMetadataProviding { + private let pathBufferSize = 4096 + private let bundleIdentityProvider: any ProcessBundleIdentityProviding + private let argumentVectorProvider: any ProcessArgumentVectorProviding + private let finalBirthTokenProvider: any ProcessBirthTokenProviding + + public init() { + bundleIdentityProvider = FoundationProcessBundleIdentityProvider() + argumentVectorProvider = KernelProcessArgumentVectorProvider() + finalBirthTokenProvider = KernelProcessBirthTokenProvider() + } + + init( + bundleIdentityProvider: any ProcessBundleIdentityProviding, + argumentVectorProvider: any ProcessArgumentVectorProviding = KernelProcessArgumentVectorProvider(), + finalBirthTokenProvider: any ProcessBirthTokenProviding = KernelProcessBirthTokenProvider() + ) { + self.bundleIdentityProvider = bundleIdentityProvider + self.argumentVectorProvider = argumentVectorProvider + self.finalBirthTokenProvider = finalBirthTokenProvider + } + + public func snapshot(includeCurrentDirectories: Bool = false) throws -> [DevProcess] { + let pids = allProcessIDs() + let processes = pids.compactMap(process) + + if processes.isEmpty { + throw ProcessScannerError.nativeScannerEmpty + } + + return processes + } + + func birthTokens(for processIDs: Set) -> [Int32: ProcessBirthToken] { + Dictionary( + uniqueKeysWithValues: processIDs.compactMap { pid in + birthToken(for: pid).map { (pid, $0) } + } + ) + } + + func birthToken(for pid: Int32) -> ProcessBirthToken? { + guard let info = bsdInfo(pid: pid) else { + return nil + } + return birthToken(from: info) + } + + func metadata(for processID: Int32) -> DevProcess? { + process(pid: processID) + } + + func argumentVector(for processID: Int32) -> [String]? { + argumentVectorProvider.argumentVector(for: processID) + } + + private func allProcessIDs() -> [pid_t] { + let requiredBytes = proc_listpids(UInt32(PROC_ALL_PIDS), 0, nil, 0) + guard requiredBytes > 0 else { + return [] + } + + let capacity = max(1, Int(requiredBytes) / MemoryLayout.stride) + let pids = UnsafeMutablePointer.allocate(capacity: capacity) + defer { pids.deallocate() } + + let actualBytes = proc_listpids(UInt32(PROC_ALL_PIDS), 0, pids, requiredBytes) + guard actualBytes > 0 else { + return [] + } + + let count = Int(actualBytes) / MemoryLayout.stride + return (0.. 0 ? pid : nil + } + } + + private func process(pid: pid_t) -> DevProcess? { + guard let info = bsdInfo(pid: pid) else { + return nil + } + + let initialBirthToken = birthToken(from: info) + let executablePath = path(pid: pid) + let executable = executableName(from: info) ?? executablePath?.lastPathComponent ?? "pid-\(pid)" + let command = executablePath ?? executable + let argumentVector = argumentVectorProvider.argumentVector(for: Int32(pid)) + let resourceUsage = resourceUsage(for: pid, startTime: TimeInterval(info.pbi_start_tvsec)) + let bundleIdentifier = executablePath.flatMap { + ProcessScanner.bundleIdentifier( + forExecutableURL: URL(fileURLWithPath: $0), + processID: Int32(pid), + provider: bundleIdentityProvider + ) + } + let launchLabel: String? = nil + + // This must remain the last metadata read. A recycled PID must not inherit path, + // argv, bundle, or future trusted launch-label evidence gathered for its predecessor. + guard finalBirthTokenProvider.birthToken(for: Int32(pid)) == initialBirthToken else { + return nil + } + + return DevProcess( + pid: Int32(bitPattern: info.pbi_pid), + parentPID: Int32(bitPattern: info.pbi_ppid), + executable: executable, + command: command, + argumentVector: argumentVector, + currentDirectory: nil, + resourceUsage: resourceUsage, + birthToken: initialBirthToken, + bundleIdentifier: bundleIdentifier, + launchLabel: launchLabel + ) + } + + private func birthToken(from info: proc_bsdinfo) -> ProcessBirthToken { + ProcessBirthToken( + seconds: info.pbi_start_tvsec, + microseconds: info.pbi_start_tvusec + ) + } + + private func bsdInfo(pid: pid_t) -> proc_bsdinfo? { + var info = proc_bsdinfo() + let size = MemoryLayout.stride + let result = withUnsafeMutablePointer(to: &info) { pointer in + pointer.withMemoryRebound(to: UInt8.self, capacity: size) { buffer in + proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, buffer, Int32(size)) + } + } + + guard result == Int32(size), info.pbi_pid > 0 else { + return nil + } + + return info + } + + private func executableName(from info: proc_bsdinfo) -> String? { + withUnsafePointer(to: info.pbi_name) { pointer in + pointer.withMemoryRebound(to: CChar.self, capacity: MemoryLayout.size(ofValue: info.pbi_name)) { buffer in + let value = String(cString: buffer) + return value.isEmpty ? nil : value + } + } + } + + private func path(pid: pid_t) -> String? { + var buffer = [CChar](repeating: 0, count: pathBufferSize) + let result = proc_pidpath(pid, &buffer, UInt32(buffer.count)) + guard result > 0 else { + return nil + } + + let value = buffer.withUnsafeBufferPointer { pointer in + guard let baseAddress = pointer.baseAddress else { + return "" + } + return String(cString: baseAddress) + } + return value.isEmpty ? nil : value + } + + private func resourceUsage(for pid: pid_t, startTime: TimeInterval) -> DevProcessResourceUsage { + DevProcessResourceUsage( + cpuPercent: 0, + residentMemoryBytes: 0, + elapsedTime: elapsedTime(since: startTime) + ) + } + + private func elapsedTime(since startTime: TimeInterval) -> String { + let elapsed = max(0, Int(Date().timeIntervalSince1970 - startTime)) + let hours = elapsed / 3600 + let minutes = (elapsed % 3600) / 60 + let seconds = elapsed % 60 + + if hours >= 24 { + let days = hours / 24 + return String(format: "%d-%02d:%02d:%02d", days, hours % 24, minutes, seconds) + } + + if hours > 0 { + return String(format: "%02d:%02d:%02d", hours, minutes, seconds) + } + + return String(format: "%02d:%02d", minutes, seconds) + } +} + +private extension String { + var lastPathComponent: String { + NSString(string: self).lastPathComponent + } +} + +public enum ProcessScannerError: LocalizedError, Equatable { + case psFailed(status: Int32, output: String) + case nativeScannerEmpty + + public var errorDescription: String? { + switch self { + case let .psFailed(status, output): + "ps failed with status \(status): \(output)" + case .nativeScannerEmpty: + "Native process scanning returned no visible processes" + } + } +} diff --git a/Sources/DevScopeCore/ProcessScopePresentation.swift b/Sources/DevScopeCore/ProcessScopePresentation.swift new file mode 100644 index 0000000..73dfc02 --- /dev/null +++ b/Sources/DevScopeCore/ProcessScopePresentation.swift @@ -0,0 +1,456 @@ +import Foundation + +public enum ProcessActivityScope: String, CaseIterable, Identifiable, Sendable { + case applications + case processes + case hierarchy + case workflows + + public var id: String { rawValue } + + public var title: String { + switch self { + case .applications: + "Applications" + case .processes: + "Processes" + case .hierarchy: + "Hierarchy" + case .workflows: + "Workflows" + } + } + + public var symbolName: String { + switch self { + case .applications: + "macwindow.on.rectangle" + case .processes: + "list.bullet.rectangle" + case .hierarchy: + "point.3.connected.trianglepath.dotted" + case .workflows: + "square.stack.3d.up" + } + } +} +public enum ApplicationProcessRole: String, Equatable, Hashable, Sendable { + case application + case helper + case worker + + public var title: String { + switch self { + case .application: + "App" + case .helper: + "Helper" + case .worker: + "Worker" + } + } + + public var symbolName: String { + switch self { + case .application: + "macwindow" + case .helper: + "gearshape.2" + case .worker: + "hammer" + } + } +} + +public struct ApplicationProcessMember: Identifiable, Equatable, Sendable { + public let item: ClassifiedDevProcess + public let role: ApplicationProcessRole + public let depth: Int + + public var id: Int32 { item.process.pid } + + public init(item: ClassifiedDevProcess, role: ApplicationProcessRole, depth: Int) { + self.item = item + self.role = role + self.depth = depth + } +} + +public struct ProcessApplicationFamily: Identifiable, Equatable, Sendable { + public let bundlePath: String + public let title: String + public let members: [ApplicationProcessMember] + public let applicationCount: Int + public let helperCount: Int + public let workerCount: Int + public let totalCPU: Double + public let totalMemoryBytes: Int64 + + public var id: String { bundlePath } + public var processIDs: [Int32] { members.map(\.id).sorted() } + + public init( + bundlePath: String, + title: String, + members: [ApplicationProcessMember], + applicationCount: Int, + helperCount: Int, + workerCount: Int, + totalCPU: Double, + totalMemoryBytes: Int64 + ) { + self.bundlePath = bundlePath + self.title = title + self.members = members + self.applicationCount = applicationCount + self.helperCount = helperCount + self.workerCount = workerCount + self.totalCPU = totalCPU + self.totalMemoryBytes = totalMemoryBytes + } +} + +public struct ProcessHierarchyNode: Identifiable, Equatable, Sendable { + public let item: ClassifiedDevProcess + public let children: [ProcessHierarchyNode] + + public var id: Int32 { item.process.pid } + public var outlineChildren: [ProcessHierarchyNode]? { + children.isEmpty ? nil : children + } + + public init(item: ClassifiedDevProcess, children: [ProcessHierarchyNode]) { + self.item = item + self.children = children + } +} + +public enum ProcessScopePresentation { + public static func applicationFamilies( + for items: S, + searchText: String = "" + ) -> [ProcessApplicationFamily] where S.Element == ClassifiedDevProcess { + let items = canonicalItems(Array(items)) + let itemsByPID = Dictionary(uniqueKeysWithValues: items.map { ($0.process.pid, $0) }) + let directBundleByPID = Dictionary( + uniqueKeysWithValues: items.compactMap { item in + outerApplicationBundlePath(for: item.process).map { (item.process.pid, $0) } + } + ) + + var groupedItems: [String: [ClassifiedDevProcess]] = [:] + for item in items { + guard + let bundlePath = resolvedApplicationBundlePath( + for: item.process.pid, + itemsByPID: itemsByPID, + directBundleByPID: directBundleByPID + ) + else { + continue + } + groupedItems[bundlePath, default: []].append(item) + } + + let terms = searchTerms(searchText) + return groupedItems.map { bundlePath, familyItems in + makeFamily( + bundlePath: bundlePath, + items: familyItems, + directBundleByPID: directBundleByPID + ) + } + .filter { family in + terms.isEmpty || terms.allSatisfy { familySearchText(family).contains($0) } + } + .sorted { lhs, rhs in + let titleComparison = lhs.title.localizedStandardCompare(rhs.title) + if titleComparison != .orderedSame { + return titleComparison == .orderedAscending + } + return lhs.bundlePath < rhs.bundlePath + } + } + + public static func hierarchy( + for items: S, + searchText: String = "" + ) -> [ProcessHierarchyNode] where S.Element == ClassifiedDevProcess { + let items = canonicalItems(Array(items)) + let itemsByPID = Dictionary(uniqueKeysWithValues: items.map { ($0.process.pid, $0) }) + let processIDs = Set(itemsByPID.keys) + let childrenByParent = Dictionary(grouping: items, by: \.process.parentPID) + .mapValues { $0.map(\.process.pid).sorted() } + let rootIDs = items + .filter { + $0.process.parentPID == $0.process.pid + || !processIDs.contains($0.process.parentPID) + } + .map(\.process.pid) + .sorted() + var visited = Set() + + func makeNode(_ processID: Int32) -> ProcessHierarchyNode? { + guard visited.insert(processID).inserted, let item = itemsByPID[processID] else { + return nil + } + let children = childrenByParent[processID, default: []].compactMap(makeNode) + return ProcessHierarchyNode(item: item, children: children) + } + + var nodes = rootIDs.compactMap(makeNode) + for processID in processIDs.sorted() where !visited.contains(processID) { + if let node = makeNode(processID) { + nodes.append(node) + } + } + + let terms = searchTerms(searchText) + guard !terms.isEmpty else { + return nodes + } + return nodes.compactMap { filteredHierarchyNode($0, terms: terms) } + } + + public static func flattened( + _ nodes: [ProcessHierarchyNode] + ) -> [ProcessHierarchyNode] { + nodes.flatMap { node in + [node] + flattened(node.children) + } + } + + private static func makeFamily( + bundlePath: String, + items: [ClassifiedDevProcess], + directBundleByPID: [Int32: String] + ) -> ProcessApplicationFamily { + let itemsByPID = Dictionary(uniqueKeysWithValues: items.map { ($0.process.pid, $0) }) + let members = orderedMembers( + items: items, + roleForPID: { processID in + guard let item = itemsByPID[processID] else { + return .worker + } + guard directBundleByPID[processID] == bundlePath else { + return .worker + } + return isApplicationInstance(item.process, bundlePath: bundlePath) + ? .application : .helper + } + ) + let roleCounts = Dictionary(grouping: members, by: \.role).mapValues(\.count) + + return ProcessApplicationFamily( + bundlePath: bundlePath, + title: familyTitle(bundlePath: bundlePath, items: items), + members: members, + applicationCount: roleCounts[.application, default: 0], + helperCount: roleCounts[.helper, default: 0], + workerCount: roleCounts[.worker, default: 0], + totalCPU: items.reduce(0) { $0 + ($1.process.resourceUsage?.cpuPercent ?? 0) }, + totalMemoryBytes: items.reduce(0) { + $0 + ($1.process.resourceUsage?.residentMemoryBytes ?? 0) + } + ) + } + + private static func orderedMembers( + items: [ClassifiedDevProcess], + roleForPID: (Int32) -> ApplicationProcessRole + ) -> [ApplicationProcessMember] { + let itemsByPID = Dictionary(uniqueKeysWithValues: items.map { ($0.process.pid, $0) }) + let processIDs = Set(itemsByPID.keys) + let childrenByParent = Dictionary(grouping: items, by: \.process.parentPID) + .mapValues { $0.map(\.process.pid).sorted() } + let roots = items + .filter { !processIDs.contains($0.process.parentPID) } + .map(\.process.pid) + .sorted() + var visited = Set() + var result: [ApplicationProcessMember] = [] + + func visit(_ processID: Int32, depth: Int) { + guard visited.insert(processID).inserted, let item = itemsByPID[processID] else { + return + } + result.append( + ApplicationProcessMember( + item: item, + role: roleForPID(processID), + depth: depth + ) + ) + for childID in childrenByParent[processID, default: []] { + visit(childID, depth: depth + 1) + } + } + + for rootID in roots { + visit(rootID, depth: 0) + } + for processID in processIDs.sorted() where !visited.contains(processID) { + visit(processID, depth: 0) + } + return result + } + + private static func resolvedApplicationBundlePath( + for processID: Int32, + itemsByPID: [Int32: ClassifiedDevProcess], + directBundleByPID: [Int32: String] + ) -> String? { + var currentProcessID = processID + var visited = Set() + + while visited.insert(currentProcessID).inserted { + if let bundlePath = directBundleByPID[currentProcessID] { + return bundlePath + } + guard let item = itemsByPID[currentProcessID] else { + return nil + } + currentProcessID = item.process.parentPID + } + return nil + } + + private static func outerApplicationBundlePath(for process: DevProcess) -> String? { + outerApplicationBundlePath(in: process.executable) + ?? outerApplicationBundlePath(in: process.command) + } + + private static func outerApplicationBundlePath(in rawValue: String) -> String? { + let value = rawValue.trimmingCharacters(in: .whitespacesAndNewlines) + .trimmingCharacters(in: CharacterSet(charactersIn: "\"'")) + let roots = ["/Applications/", "/System/", "/Users/", "/private/", "/Volumes/"] + let start = roots.compactMap { value.range(of: $0)?.lowerBound }.min() + ?? (value.hasPrefix("/") ? value.startIndex : nil) + guard let start else { + return nil + } + + var searchStart = start + while searchStart < value.endIndex, + let appRange = value.range( + of: ".app", + options: [.caseInsensitive], + range: searchStart.. Bool { + guard process.parentPID == 1 else { + return false + } + let mainExecutablePrefix = bundlePath + "/Contents/MacOS/" + return process.executable.hasPrefix(mainExecutablePrefix) + || process.command.hasPrefix(mainExecutablePrefix) + } + + private static func familyTitle( + bundlePath: String, + items: [ClassifiedDevProcess] + ) -> String { + let baseTitle = URL(fileURLWithPath: bundlePath) + .deletingPathExtension() + .lastPathComponent + if baseTitle.caseInsensitiveCompare("ChatGPT") == .orderedSame, + items.contains(where: { + $0.process.executable.localizedCaseInsensitiveContains("codex") + || $0.process.command.localizedCaseInsensitiveContains("codex") + }) + { + return "ChatGPT / Codex" + } + return baseTitle + } + + private static func canonicalItems( + _ items: [ClassifiedDevProcess] + ) -> [ClassifiedDevProcess] { + Dictionary(grouping: items, by: \.process.pid) + .compactMap { _, candidates in + candidates.sorted(by: stableItemOrder).first + } + .sorted(by: stableItemOrder) + } + + private static func stableItemOrder( + _ lhs: ClassifiedDevProcess, + _ rhs: ClassifiedDevProcess + ) -> Bool { + let left = lhs.process + let right = rhs.process + if left.pid != right.pid { return left.pid < right.pid } + if left.parentPID != right.parentPID { return left.parentPID < right.parentPID } + if left.executable != right.executable { return left.executable < right.executable } + if left.command != right.command { return left.command < right.command } + return (left.currentDirectory ?? "") < (right.currentDirectory ?? "") + } + + private static func searchTerms(_ searchText: String) -> [String] { + searchText + .lowercased() + .split(whereSeparator: \.isWhitespace) + .map(String.init) + } + + private static func familySearchText(_ family: ProcessApplicationFamily) -> String { + ([family.title, family.bundlePath] + + family.members.flatMap { member in + let item = member.item + return [ + String(item.process.pid), + item.classification.displayName, + item.classification.projectHint ?? "", + item.process.executable, + item.process.command, + item.process.currentDirectory ?? "", + ] + }) + .joined(separator: " ") + .lowercased() + } + + private static func filteredHierarchyNode( + _ node: ProcessHierarchyNode, + terms: [String] + ) -> ProcessHierarchyNode? { + let children = node.children.compactMap { filteredHierarchyNode($0, terms: terms) } + guard terms.allSatisfy({ itemSearchText(node.item).contains($0) }) || !children.isEmpty else { + return nil + } + return ProcessHierarchyNode(item: node.item, children: children) + } + + private static func itemSearchText(_ item: ClassifiedDevProcess) -> String { + [ + String(item.process.pid), + String(item.process.parentPID), + item.classification.displayName, + item.classification.projectHint ?? "", + item.process.executable, + item.process.command, + item.process.currentDirectory ?? "", + ] + .joined(separator: " ") + .lowercased() + } +} diff --git a/Sources/DevScopeCore/ProcessSignalObservation.swift b/Sources/DevScopeCore/ProcessSignalObservation.swift new file mode 100644 index 0000000..2ea9670 --- /dev/null +++ b/Sources/DevScopeCore/ProcessSignalObservation.swift @@ -0,0 +1,161 @@ +public struct ProcessIdentity: Equatable, Hashable, Sendable { + public let pid: Int32 + public let executable: String + public let command: String + public let birthToken: ProcessBirthToken? + + public init(process: DevProcess) { + pid = process.pid + executable = process.executable + command = process.command + birthToken = process.birthToken + } + + public init( + pid: Int32, + executable: String = "", + command: String = "", + birthToken: ProcessBirthToken? + ) { + self.pid = pid + self.executable = executable + self.command = command + self.birthToken = birthToken + } + + public func hasSameBirthIdentity(as other: ProcessIdentity) -> Bool { + guard let birthToken, let otherBirthToken = other.birthToken else { + return false + } + return pid == other.pid && birthToken == otherBirthToken + } +} + +public enum ProcessSignalObservation { + public static func observe( + targets: [ProcessIdentity], + in currentProcesses: [DevProcess] + ) -> ProcessSignalObservationSummary { + let currentByPID = Dictionary( + currentProcesses.map { ($0.pid, $0) }, + uniquingKeysWith: { _, latest in latest } + ) + let observations = targets.map { target in + let state: ProcessSignalObservationState + guard let expectedBirthToken = target.birthToken else { + state = .unverifiable + return ProcessSignalTargetObservation(target: target, state: state) + } + guard let current = currentByPID[target.pid] else { + state = .exitedOrReplaced + return ProcessSignalTargetObservation(target: target, state: state) + } + guard let currentBirthToken = current.birthToken else { + state = .unverifiable + return ProcessSignalTargetObservation(target: target, state: state) + } + state = currentBirthToken == expectedBirthToken ? .stillRunning : .exitedOrReplaced + return ProcessSignalTargetObservation(target: target, state: state) + } + return ProcessSignalObservationSummary(observations: observations) + } +} + +public enum ProcessSignalObservationState: Equatable, Sendable { + case stillRunning + case exitedOrReplaced + case unverifiable +} + +public struct ProcessSignalTargetObservation: Equatable, Sendable { + public let target: ProcessIdentity + public let state: ProcessSignalObservationState + + public init(target: ProcessIdentity, state: ProcessSignalObservationState) { + self.target = target + self.state = state + } +} + +public struct ProcessSignalObservationSummary: Equatable, Sendable { + public let observations: [ProcessSignalTargetObservation] + + public init(observations: [ProcessSignalTargetObservation]) { + self.observations = observations + } + + public var verifiesAllTargetsStopped: Bool { + !observations.isEmpty && observations.allSatisfy { $0.state == .exitedOrReplaced } + } + + public var stillRunning: [ProcessIdentity] { + targets(with: .stillRunning) + } + + public var exitedOrReplaced: [ProcessIdentity] { + targets(with: .exitedOrReplaced) + } + + public var unverifiable: [ProcessIdentity] { + targets(with: .unverifiable) + } + + private func targets(with state: ProcessSignalObservationState) -> [ProcessIdentity] { + observations.compactMap { observation in + observation.state == state ? observation.target : nil + } + } +} + +public struct ProcessPartialSignalVerificationPresentation: Equatable, Sendable { + public let title: String + public let statusMessage: String + public let detail: String + public let symbolName: String + + public static func make( + signalName: String, + observation: ProcessSignalObservationSummary, + failedTarget: ProcessIdentity, + reason: String + ) -> ProcessPartialSignalVerificationPresentation { + let stopped = observation.exitedOrReplaced + let stillRunning = observation.stillRunning + let unverifiable = observation.unverifiable + let stoppedText = "\(stopped.count) signaled process\(stopped.count == 1 ? "" : "es") stopped" + let failedText = "PID \(failedTarget.pid) failed: \(reason)" + let unsignaledText = "The failed target and all remaining tree targets were not signaled" + + if stillRunning.isEmpty, unverifiable.isEmpty { + return ProcessPartialSignalVerificationPresentation( + title: "Partial result verified", + statusMessage: "\(signalName) partial result verified", + detail: "\(stoppedText). \(failedText). \(unsignaledText).", + symbolName: "exclamationmark.triangle.fill" + ) + } + + var details: [String] = [] + if !stopped.isEmpty { + details.append(stoppedText) + } + if !stillRunning.isEmpty { + details.append("Still running: \(pidList(stillRunning))") + } + if !unverifiable.isEmpty { + details.append("Could not verify: \(pidList(unverifiable))") + } + details.append(failedText) + details.append(unsignaledText) + return ProcessPartialSignalVerificationPresentation( + title: "Partial result not fully verified", + statusMessage: "\(signalName) partial result remains unresolved", + detail: details.joined(separator: ". ") + ".", + symbolName: "exclamationmark.triangle.fill" + ) + } + + private static func pidList(_ identities: [ProcessIdentity]) -> String { + identities.map(\.pid).sorted().map(String.init).joined(separator: ", ") + } +} diff --git a/Sources/DevScopeCore/ProcessSnapshotBuilder.swift b/Sources/DevScopeCore/ProcessSnapshotBuilder.swift new file mode 100644 index 0000000..123a7d8 --- /dev/null +++ b/Sources/DevScopeCore/ProcessSnapshotBuilder.swift @@ -0,0 +1,67 @@ +import Foundation + +public struct DevProcessSnapshot: Equatable, Sendable { + public let processes: [DevProcess] + public let classified: [ClassifiedDevProcess] + public let workflows: [DevWorkflow] + public let liveProcessIDs: Set + + public init( + processes: [DevProcess], + classified: [ClassifiedDevProcess], + workflows: [DevWorkflow], + liveProcessIDs: Set + ) { + self.processes = processes + self.classified = classified + self.workflows = workflows + self.liveProcessIDs = liveProcessIDs + } +} + +public struct ProcessSnapshotBuilder: Sendable { + private var classificationCache = ProcessClassificationCache() + private var stabilizer = ClassifiedProcessSnapshotStabilizer() + private var workflowCache = ProcessWorkflowCache() + private let workspaceFactsCache: WorkspaceFactsCache + + public init(workspaceFactsCache: WorkspaceFactsCache = WorkspaceFactsCache()) { + self.workspaceFactsCache = workspaceFactsCache + } + + public mutating func invalidateWorkspaceFacts() { + workspaceFactsCache.invalidateAll() + workflowCache.invalidate() + } + + public mutating func build( + processes: [DevProcess], + now: Date, + graceInterval: TimeInterval + ) -> DevProcessSnapshot { + let classified = classificationCache.classified( + processes, + workspaceFactsCache: workspaceFactsCache + ).sorted { lhs, rhs in + let leftProject = lhs.classification.projectHint ?? "" + let rightProject = rhs.classification.projectHint ?? "" + if leftProject != rightProject { + return leftProject < rightProject + } + return lhs.classification.displayName < rhs.classification.displayName + } + let stabilized = stabilizer.merge( + liveItems: classified, + now: now, + graceInterval: graceInterval + ) + let workflows = workflowCache.workflows(for: stabilized, now: now) + + return DevProcessSnapshot( + processes: stabilized.map(\.process), + classified: stabilized, + workflows: workflows, + liveProcessIDs: stabilizer.liveProcessIDs + ) + } +} diff --git a/Sources/DevScopeCore/ProcessSnapshotStabilizer.swift b/Sources/DevScopeCore/ProcessSnapshotStabilizer.swift new file mode 100644 index 0000000..f2ca6dd --- /dev/null +++ b/Sources/DevScopeCore/ProcessSnapshotStabilizer.swift @@ -0,0 +1,44 @@ +import Foundation + +public struct ClassifiedProcessSnapshotStabilizer: Sendable { + private struct Entry: Sendable { + var item: ClassifiedDevProcess + let firstSeen: Date + var lastSeen: Date + } + + private var entriesByPID: [Int32: Entry] = [:] + public private(set) var liveProcessIDs: Set = [] + + public init() {} + + public mutating func merge( + liveItems: [ClassifiedDevProcess], + now: Date, + graceInterval: TimeInterval + ) -> [ClassifiedDevProcess] { + liveProcessIDs = Set(liveItems.map(\.process.pid)) + + for item in liveItems { + let previous = entriesByPID[item.process.pid] + entriesByPID[item.process.pid] = Entry( + item: item, + firstSeen: previous?.firstSeen ?? now, + lastSeen: now + ) + } + + entriesByPID = entriesByPID.filter { pid, entry in + liveProcessIDs.contains(pid) || now.timeIntervalSince(entry.lastSeen) <= graceInterval + } + + return entriesByPID.values + .sorted { lhs, rhs in + if lhs.firstSeen != rhs.firstSeen { + return lhs.firstSeen < rhs.firstSeen + } + return lhs.item.process.pid < rhs.item.process.pid + } + .map(\.item) + } +} diff --git a/Sources/DevScopeCore/ProcessWorkflowCache.swift b/Sources/DevScopeCore/ProcessWorkflowCache.swift new file mode 100644 index 0000000..672f180 --- /dev/null +++ b/Sources/DevScopeCore/ProcessWorkflowCache.swift @@ -0,0 +1,85 @@ +import Foundation + +struct ProcessWorkflowCache: Sendable { + typealias Build = @Sendable ([ClassifiedDevProcess]) -> [DevWorkflow] + + private struct Fingerprint: Equatable, Sendable { + let processes: [Process] + + init(_ items: [ClassifiedDevProcess]) { + processes = items.map(Process.init) + } + } + + private struct Process: Equatable, Sendable { + let pid: Int32 + let birthToken: ProcessBirthToken? + let parentPID: Int32 + let executable: String + let command: String + let currentDirectory: String? + let kind: DevRuntimeKind + let displayName: String + let projectHint: String? + let tags: [DevProcessTag] + + init(_ item: ClassifiedDevProcess) { + pid = item.process.pid + birthToken = item.process.birthToken + parentPID = item.process.parentPID + executable = item.process.executable + command = item.process.command + currentDirectory = item.process.currentDirectory + kind = item.classification.kind + displayName = item.classification.displayName + projectHint = item.classification.projectHint + tags = item.classification.tags + } + } + + private let build: Build + private var cachedFingerprint: Fingerprint? + private var cachedWorkflows: [DevWorkflow] = [] + + init( + build: @escaping Build = { ProcessIntelligence.uncappedWorkflows(for: $0) } + ) { + self.build = build + } + + mutating func workflows( + for items: [ClassifiedDevProcess], + now _: Date + ) -> [DevWorkflow] { + let workflowItems = items.filter(Self.isWorkflowRelevant) + let fingerprint = Fingerprint(workflowItems) + if fingerprint == cachedFingerprint { + let rehydrated = ProcessIntelligence.rehydratedWorkflows(cachedWorkflows, for: items) + return ProcessIntelligence.presentedWorkflows(rehydrated) + } + + let workflows = build(workflowItems) + cachedFingerprint = fingerprint + cachedWorkflows = workflows + return ProcessIntelligence.presentedWorkflows(workflows) + } + + mutating func invalidate() { + cachedFingerprint = nil + cachedWorkflows = [] + } + + private static func isWorkflowRelevant(_ item: ClassifiedDevProcess) -> Bool { + if item.classification.projectHint != nil || !item.classification.tags.isEmpty { + return true + } + + switch item.classification.kind { + case .javascript, .python, .swift, .rust, .go, .flutter, .java, + .database, .container, .webServer, .ai, .mcp: + return true + case .browser, .macApp, .backgroundAgent, .systemService, .shell, .other: + return false + } + } +} diff --git a/Sources/DevScopeCore/WorkspaceFactsCache.swift b/Sources/DevScopeCore/WorkspaceFactsCache.swift new file mode 100644 index 0000000..5f69ea3 --- /dev/null +++ b/Sources/DevScopeCore/WorkspaceFactsCache.swift @@ -0,0 +1,144 @@ +import Foundation + +public struct WorkspaceFacts: Hashable, Sendable { + public let isFlutterWorkspace: Bool + + public init(isFlutterWorkspace: Bool) { + self.isFlutterWorkspace = isFlutterWorkspace + } +} + +public final class WorkspaceFactsCache: @unchecked Sendable { + public typealias FileExists = (String) -> Bool + + private struct Entry { + var facts: WorkspaceFacts + var validatedGeneration: UInt64 + var lastAccessGeneration: UInt64 + } + + private struct RevalidationCycle { + let identifier: UInt64 + let generation: UInt64 + var remainingStaleEntries: Int + var claimedDirectories: Set = [] + } + + private enum Lookup { + case cached(WorkspaceFacts) + case probe(expectedGeneration: UInt64) + } + + private let fileExists: FileExists + private let lock = NSLock() + private var entries: [String: Entry] = [:] + private var generation: UInt64 = 0 + private var nextCycleIdentifier: UInt64 = 0 + private var revalidationCycle: RevalidationCycle? + + public init(fileExists: @escaping FileExists = { FileManager.default.fileExists(atPath: $0) }) { + self.fileExists = fileExists + } + + public func facts(for directory: String?) -> WorkspaceFacts { + guard let directory else { + return WorkspaceFacts(isFlutterWorkspace: false) + } + + while true { + let lookup = lock.withLock { () -> Lookup in + guard var entry = entries[directory] else { + return .probe(expectedGeneration: generation) + } + + entry.lastAccessGeneration = generation + entries[directory] = entry + + guard entry.validatedGeneration != generation else { + return .cached(entry.facts) + } + + guard var cycle = revalidationCycle, cycle.generation == generation else { + return .probe(expectedGeneration: generation) + } + guard !cycle.claimedDirectories.contains(directory) else { + return .cached(entry.facts) + } + guard cycle.remainingStaleEntries > 0 else { + return .cached(entry.facts) + } + + cycle.claimedDirectories.insert(directory) + cycle.remainingStaleEntries -= 1 + revalidationCycle = cycle + return .probe(expectedGeneration: generation) + } + + switch lookup { + case .cached(let facts): + return facts + case .probe(let expectedGeneration): + let components = URL(fileURLWithPath: directory).pathComponents.map { $0.lowercased() } + let pathIdentifiesFlutter = components.contains("flutter") || components.contains(".dart_tool") + let manifestIdentifiesFlutter = fileExists(directory + "/pubspec.yaml") || + fileExists(directory + "/.dart_tool/package_config.json") + let facts = WorkspaceFacts(isFlutterWorkspace: pathIdentifiesFlutter || manifestIdentifiesFlutter) + + let storedFacts = lock.withLock { () -> WorkspaceFacts? in + guard generation == expectedGeneration else { + return nil + } + if let cached = entries[directory], cached.validatedGeneration == generation { + return cached.facts + } + entries[directory] = Entry( + facts: facts, + validatedGeneration: generation, + lastAccessGeneration: generation + ) + return facts + } + + if let storedFacts { + return storedFacts + } + } + } + } + + public func withRevalidationCycle( + maximumStaleEntries: Int, + _ operation: () throws -> Result + ) rethrows -> Result { + let cycleIdentifier = lock.withLock { () -> UInt64 in + nextCycleIdentifier &+= 1 + let identifier = nextCycleIdentifier + revalidationCycle = RevalidationCycle( + identifier: identifier, + generation: generation, + remainingStaleEntries: max(0, maximumStaleEntries) + ) + return identifier + } + defer { + lock.withLock { + if revalidationCycle?.identifier == cycleIdentifier { + revalidationCycle = nil + } + } + } + return try operation() + } + + public func isFlutterWorkspace(_ directory: String?) -> Bool { + facts(for: directory).isFlutterWorkspace + } + + public func invalidateAll() { + lock.withLock { + entries = entries.filter { $0.value.lastAccessGeneration == generation } + generation &+= 1 + revalidationCycle = nil + } + } +} diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md new file mode 100644 index 0000000..1168e76 --- /dev/null +++ b/THIRD_PARTY_NOTICES.md @@ -0,0 +1,13 @@ +# Third-Party Notices + +The Swift package currently declares no external package dependencies. + +DevScope uses Apple platform frameworks supplied by macOS and the Xcode SDK. +Those frameworks are not redistributed by this repository and remain subject to +Apple's applicable terms. The release process must regenerate this dependency +statement if an external dependency or redistributed third-party asset is added. + +Repository automation uses GitHub's `actions/checkout` at a pinned revision and +downloads the Gitleaks CLI at an explicit version with its release SHA-256 +checksum verified before execution. They are CI tooling and are not linked into +or redistributed with the DevScope application. diff --git a/TRADEMARKS.md b/TRADEMARKS.md new file mode 100644 index 0000000..1f379ad --- /dev/null +++ b/TRADEMARKS.md @@ -0,0 +1,17 @@ +# Trademarks + +The Apache License 2.0 applies to the source code and documentation, but it does +not grant permission to use project names, logos, or other brand identifiers +except for reasonable and customary use describing the origin of the work. + +You may accurately state that your work is based on DevScope. Do not imply that +an altered build, fork, service, or distribution is official, endorsed, or +maintained by the DevScope project. Forks that could cause confusion should use +a distinct product name and visual identity. + +This policy does not claim registration of any mark and does not limit uses +permitted by applicable law. + +DevScope is maintained publicly by RSI Tech. This statement identifies the +project source and does not claim trademark registration. Contact +`info@rsitech.ai` for brand-use questions. diff --git a/Tests/DevScopeCoreTests/AutomationCapabilityPolicyTests.swift b/Tests/DevScopeCoreTests/AutomationCapabilityPolicyTests.swift new file mode 100644 index 0000000..ebc3ad1 --- /dev/null +++ b/Tests/DevScopeCoreTests/AutomationCapabilityPolicyTests.swift @@ -0,0 +1,237 @@ +import XCTest +@testable import DevScopeCore + +final class AutomationCapabilityPolicyTests: XCTestCase { + func testApprovedPathAllowsVerifiedMissingChildButRejectsPrefixEscapeAndSymlink() { + let root = URL(fileURLWithPath: "/Users/test/Library/LaunchAgents") + let destination = root.appending(path: "new.plist") + + XCTAssertTrue(AutomationPathAuthorization.isApprovedDestination( + destination, + approvedRoot: root, + destinationExists: false, + verifiedMetadataURL: root, + metadataIsSymbolicLink: false + )) + XCTAssertFalse(AutomationPathAuthorization.isApprovedDestination( + URL(fileURLWithPath: "/Users/test/Library/LaunchAgents-escape/new.plist"), + approvedRoot: root, + destinationExists: false, + verifiedMetadataURL: URL(fileURLWithPath: "/Users/test/Library/LaunchAgents-escape"), + metadataIsSymbolicLink: false + )) + XCTAssertFalse(AutomationPathAuthorization.isApprovedDestination( + destination, + approvedRoot: root, + destinationExists: true, + verifiedMetadataURL: destination, + metadataIsSymbolicLink: true + )) + } + + func testCurrentUserLaunchAgentReceivesFullCapabilities() { + let decision = AutomationCapabilityPolicy.decision( + for: Fixtures.userAgent, + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501) + ) + + XCTAssertEqual(decision.capabilities, Set(AutomationCapability.allCases)) + XCTAssertNil(decision.reason) + } + + func testLaunchAgentWithUnresolvedRuntimeStateIsInspectionOnly() { + let unresolved = copyRecord( + Fixtures.userAgent, + ownership: .user, + enabledState: .unknown, + loadState: .unknown + ) + let decision = AutomationCapabilityPolicy.decision( + for: unresolved, + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501) + ) + + XCTAssertEqual(decision.capabilities, [.exportRecord]) + XCTAssertEqual( + decision.reason, + "DevScope could not verify this LaunchAgent's enabled and loaded state, so management is unavailable." + ) + } + + func testSymlinkEscapeIsReadOnly() { + let decision = AutomationCapabilityPolicy.decision( + for: Fixtures.userAgent, + context: .fixture( + currentUID: 501, + canonicalPathIsApproved: false, + ownerUID: 501, + isSymlink: true + ) + ) + + XCTAssertEqual(decision.capabilities, [.exportRecord]) + XCTAssertEqual(decision.reason, "The source resolves outside your LaunchAgents folder.") + } + + func testManagedAutomationHasExactInspectionOnlyReason() { + let decision = AutomationCapabilityPolicy.decision( + for: copyRecord(Fixtures.userAgent, ownership: .managed), + context: .fixture( + currentUID: 501, + canonicalPathIsApproved: true, + ownerUID: 501, + isManaged: true + ) + ) + + XCTAssertEqual(decision.capabilities, [.exportRecord]) + XCTAssertEqual(decision.reason, "This automation is managed by your organization.") + } + + func testModernCrossAppBackgroundItemIsInspectionOnly() { + let decision = AutomationCapabilityPolicy.decision( + for: Fixtures.backgroundCopyOfUserAgent, + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501) + ) + + XCTAssertEqual(decision.capabilities, [.exportRecord]) + XCTAssertEqual( + decision.reason, + "Modern background items must be managed by their owning app or System Settings." + ) + } + + func testWrongOwnerAndSymbolicLinkHaveDistinctReasons() { + let wrongOwner = AutomationCapabilityPolicy.decision( + for: Fixtures.userAgent, + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 502) + ) + let symbolicLink = AutomationCapabilityPolicy.decision( + for: Fixtures.userAgent, + context: .fixture( + currentUID: 501, + canonicalPathIsApproved: true, + ownerUID: 501, + isSymlink: true + ) + ) + + XCTAssertEqual(wrongOwner.reason, "The automation source is not owned by the current user.") + XCTAssertEqual(symbolicLink.reason, "Symbolic-link automation sources are inspection only.") + } + + func testCronExposesOnlyInstalledCapabilitiesWithSourceSpecificReason() { + let cronRecord = automationRecord( + copying: Fixtures.userAgent, + kind: .cron, + sourceKind: .crontab, + sourceURL: nil + ) + let decision = AutomationCapabilityPolicy.decision( + for: cronRecord, + context: .fixture( + currentUID: 501, + canonicalPathIsApproved: true, + ownerUID: 501, + implementedCapabilities: [.exportRecord] + ) + ) + + XCTAssertEqual(decision.capabilities, [.exportRecord]) + XCTAssertEqual( + decision.reason, + "Current-user crontab management is unavailable until recoverable source access is installed." + ) + } + + func testExportAvailabilityIsGatedConsistentlyAndSurvivesPartialMutationAdapters() { + let protected = AutomationCapabilityPolicy.decision( + for: copyRecord(Fixtures.userAgent, ownership: .managed), + context: .fixture( + currentUID: 501, + canonicalPathIsApproved: true, + ownerUID: 501, + isManaged: true, + implementedCapabilities: [] + ) + ) + XCTAssertFalse(protected.capabilities.contains(.exportRecord)) + + let partialUser = AutomationCapabilityPolicy.decision( + for: Fixtures.userAgent, + context: .fixture( + currentUID: 501, + canonicalPathIsApproved: true, + ownerUID: 501, + implementedCapabilities: [.disable, .exportRecord] + ) + ) + XCTAssertEqual(partialUser.capabilities, [.disable, .exportRecord]) + } +} + +private func copyRecord( + _ record: AutomationRecord, + ownership: AutomationOwnership, + enabledState: AutomationEnabledState? = nil, + loadState: AutomationLoadState? = nil +) -> AutomationRecord { + AutomationRecord( + id: record.id, + kind: record.kind, + sourceKind: record.sourceKind, + label: record.label, + displayName: record.displayName, + providerBundleIdentifier: record.providerBundleIdentifier, + ownerUID: record.ownerUID, + ownership: ownership, + executable: record.executable, + arguments: record.arguments, + commandSignature: record.commandSignature, + environment: record.environment, + workingDirectory: record.workingDirectory, + schedule: record.schedule, + sourceURL: record.sourceURL, + sourceChecksum: record.sourceChecksum, + enabledState: enabledState ?? record.enabledState, + loadState: loadState ?? record.loadState, + approvalState: record.approvalState, + state: record.state, + evidence: record.evidence, + capabilities: record.capabilities, + validationFindings: record.validationFindings + ) +} + +private func automationRecord( + copying record: AutomationRecord, + kind: AutomationKind, + sourceKind: AutomationSourceKind, + sourceURL: URL? +) -> AutomationRecord { + AutomationRecord( + id: record.id, + kind: kind, + sourceKind: sourceKind, + label: record.label, + displayName: record.displayName, + providerBundleIdentifier: record.providerBundleIdentifier, + ownerUID: record.ownerUID, + ownership: record.ownership, + executable: record.executable, + arguments: record.arguments, + commandSignature: record.commandSignature, + environment: record.environment, + workingDirectory: record.workingDirectory, + schedule: record.schedule, + sourceURL: sourceURL, + sourceChecksum: record.sourceChecksum, + enabledState: record.enabledState, + loadState: record.loadState, + approvalState: record.approvalState, + state: record.state, + evidence: record.evidence, + capabilities: record.capabilities, + validationFindings: record.validationFindings + ) +} diff --git a/Tests/DevScopeCoreTests/AutomationEventDetectorTests.swift b/Tests/DevScopeCoreTests/AutomationEventDetectorTests.swift new file mode 100644 index 0000000..c04f59f --- /dev/null +++ b/Tests/DevScopeCoreTests/AutomationEventDetectorTests.swift @@ -0,0 +1,304 @@ +import XCTest +@testable import DevScopeCore + +final class AutomationEventDetectorTests: XCTestCase { + func testLongRunningTransitionEmitsOnceAndNilBirthNeverNotifies() { + var detector = AutomationEventDetector() + let before = presentation(elapsed: "03:59:59") + let boundary = presentation(elapsed: "04:00:00") + let after = presentation(elapsed: "04:00:01") + + XCTAssertEqual(detector.events( + previous: before, current: boundary, now: Date(timeIntervalSince1970: 14_400) + ), [.crossedLongRunningThreshold( + process: eventIdentity(process(elapsed: "04:00:00")), + recordID: expectedRecord.id + )]) + XCTAssertTrue(detector.events( + previous: boundary, current: after, now: Date(timeIntervalSince1970: 14_401) + ).isEmpty) + + let nilBefore = presentation(elapsed: "03:59:59", birthToken: nil) + let nilBoundary = presentation(elapsed: "04:00:00", birthToken: nil) + XCTAssertTrue(detector.events( + previous: nilBefore, current: nilBoundary, now: Date(timeIntervalSince1970: 20_000) + ).isEmpty) + } + + func testUnexpectedExitRequiresTwoCompleteHealthyExpectedRunningAbsences() { + var detector = AutomationEventDetector() + let running = presentation(elapsed: "00:01:00") + let absent = presentation(processes: []) + + XCTAssertTrue(detector.events(previous: nil, current: running, now: date(1_000)).isEmpty) + XCTAssertTrue(detector.events(previous: running, current: absent, now: date(1_001)).isEmpty) + XCTAssertEqual(detector.events(previous: absent, current: absent, now: date(1_002)), [ + .unexpectedExit( + recordID: expectedRecord.id, + process: eventIdentity(process(elapsed: "00:01:00")) + ), + ]) + XCTAssertTrue(detector.events(previous: absent, current: absent, now: date(1_003)).isEmpty) + } + + func testIncompleteOrFailedSourceResetsExitConfirmation() { + var detector = AutomationEventDetector() + let running = presentation(elapsed: "00:01:00") + let incomplete = presentation(complete: false, processes: []) + let absent = presentation(processes: []) + let failed = presentation(sourceHealth: .failed, processes: []) + + XCTAssertTrue(detector.events(previous: running, current: incomplete, now: date(2_000)).isEmpty) + XCTAssertTrue(detector.events(previous: incomplete, current: absent, now: date(2_001)).isEmpty) + XCTAssertTrue(detector.events(previous: absent, current: failed, now: date(2_002)).isEmpty) + XCTAssertTrue(detector.events(previous: failed, current: absent, now: date(2_003)).isEmpty) + XCTAssertTrue(detector.events(previous: absent, current: absent, now: date(2_004)).isEmpty) + } + + func testUnhealthyPreviousSourceCannotSeedExitConfirmation() { + var detector = AutomationEventDetector() + let unhealthyRunning = presentation(elapsed: "00:01:00", sourceHealth: .failed) + let healthyAbsent = presentation(processes: []) + + XCTAssertTrue(detector.events( + previous: unhealthyRunning, current: healthyAbsent, now: date(2_100) + ).isEmpty) + XCTAssertTrue(detector.events( + previous: healthyAbsent, current: healthyAbsent, now: date(2_101) + ).isEmpty) + } + + func testLongRunningPIDRecycleEmitsTransitionForNewBirthIdentity() { + var detector = AutomationEventDetector() + let old = presentation(elapsed: "04:00:00") + let newBirth = ProcessBirthToken(seconds: 30_000, microseconds: 7) + let recycled = presentation(elapsed: "04:00:01", birthToken: newBirth) + + XCTAssertEqual(detector.events( + previous: old, current: recycled, now: date(30_001) + ), [.crossedLongRunningThreshold( + process: ProcessIdentity(pid: Fixtures.runningBackup.pid, birthToken: newBirth), + recordID: expectedRecord.id + )]) + } + + func testLongRunningTransitionDoesNotAttributeAnAmbiguousProcessToOneAutomation() throws { + var detector = AutomationEventDetector() + let before = presentation(elapsed: "03:59:59") + let boundary = presentation(elapsed: "04:00:00") + let identity = try XCTUnwrap(boundary.processIdentitiesByID[Fixtures.runningBackup.pid]) + let primaryLink = try XCTUnwrap(boundary.linksByProcessID[Fixtures.runningBackup.pid]) + let secondLink = AutomationProcessLink( + recordID: AutomationRecord.ID(rawValue: "second-strong-record"), + processIdentity: identity, + strength: .strong, + evidence: [] + ) + let ambiguous = AutomationPresentationSnapshot( + inventory: boundary.inventory, + linksByProcessID: boundary.linksByProcessID, + allLinksByProcessID: [identity.pid: [primaryLink, secondLink]], + longRunningProcessIDs: boundary.longRunningProcessIDs, + longRunningProcessIdentities: boundary.longRunningProcessIdentities, + processIdentitiesByID: boundary.processIdentitiesByID, + isProcessSnapshotComplete: boundary.isProcessSnapshotComplete + ) + + XCTAssertEqual(detector.events( + previous: before, current: ambiguous, now: Date(timeIntervalSince1970: 14_400) + ), [.crossedLongRunningThreshold( + process: eventIdentity(process(elapsed: "04:00:00")), + recordID: nil + )]) + } + + func testUnexpectedExitDoesNotAttributeOneAmbiguousProcessToMultipleAutomations() throws { + var detector = AutomationEventDetector() + let running = presentation(elapsed: "00:01:00") + let identity = try XCTUnwrap(running.processIdentitiesByID[Fixtures.runningBackup.pid]) + let primaryLink = try XCTUnwrap(running.linksByProcessID[Fixtures.runningBackup.pid]) + let secondRecord = copyExpectedRecord( + id: AutomationRecord.ID(rawValue: "second-strong-record") + ) + let secondLink = AutomationProcessLink( + recordID: secondRecord.id, + processIdentity: identity, + strength: .strong, + evidence: [] + ) + let inventory = AutomationInventorySnapshot( + generation: running.inventory.generation, + records: [expectedRecord, secondRecord], + health: running.inventory.health, + refreshedAt: running.inventory.refreshedAt + ) + let ambiguousRunning = AutomationPresentationSnapshot( + inventory: inventory, + linksByProcessID: running.linksByProcessID, + allLinksByProcessID: [identity.pid: [primaryLink, secondLink]], + longRunningProcessIDs: running.longRunningProcessIDs, + longRunningProcessIdentities: running.longRunningProcessIdentities, + processIdentitiesByID: running.processIdentitiesByID, + isProcessSnapshotComplete: true + ) + let ambiguousAbsent = AutomationPresentationSnapshot( + inventory: inventory, + linksByProcessID: [:], + allLinksByProcessID: [:], + longRunningProcessIDs: [], + longRunningProcessIdentities: [], + processIdentitiesByID: [:], + isProcessSnapshotComplete: true + ) + + XCTAssertTrue(detector.events( + previous: ambiguousRunning, current: ambiguousAbsent, now: date(2_200) + ).isEmpty) + XCTAssertTrue(detector.events( + previous: ambiguousAbsent, current: ambiguousAbsent, now: date(2_201) + ).isEmpty) + } + + func testPIDReplacementDoesNotBecomeUnexpectedExit() { + var detector = AutomationEventDetector() + let running = presentation(elapsed: "00:01:00") + let replacement = process( + elapsed: "00:00:01", + birthToken: ProcessBirthToken(seconds: 9_999, microseconds: 9) + ) + let replaced = presentation(processes: [replacement]) + + XCTAssertTrue(detector.events(previous: running, current: replaced, now: date(3_000)).isEmpty) + XCTAssertTrue(detector.events(previous: replaced, current: replaced, now: date(3_001)).isEmpty) + } + + func testThreeVerifiedExitsAtInclusiveTenMinuteBoundaryEmitRepeatedFailureOnce() { + var detector = AutomationEventDetector() + var previous = presentation(elapsed: "00:01:00") + var emitted: [[AutomationEvent]] = [] + + for timestamp in [1_000.0, 1_300.0, 1_600.0] { + let absent = presentation(processes: []) + _ = detector.events(previous: previous, current: absent, now: date(timestamp - 1)) + emitted.append(detector.events(previous: absent, current: absent, now: date(timestamp))) + previous = presentation(elapsed: "00:01:00") + _ = detector.events(previous: absent, current: previous, now: date(timestamp + 0.5)) + } + + XCTAssertEqual(emitted[0].count, 1) + XCTAssertEqual(emitted[1].count, 1) + XCTAssertEqual(emitted[2], [ + .unexpectedExit( + recordID: expectedRecord.id, + process: eventIdentity(process(elapsed: "00:01:00")) + ), + .repeatedFailure(recordID: expectedRecord.id, observedExitCount: 3), + ]) + } + + func testRepeatedFailureIsSuppressedWhileThreeExitsRemainInWindow() { + var detector = AutomationEventDetector() + let outputs = [4_000.0, 4_100.0, 4_200.0, 4_300.0].map { timestamp in + emitVerifiedExit(detector: &detector, timestamp: timestamp) + } + + XCTAssertEqual(outputs.flatMap { $0 }.filter { + if case .repeatedFailure = $0 { return true } + return false + }.count, 1) + XCTAssertEqual(outputs.map(\.count), [1, 1, 2, 1]) + } +} + +private let expectedRecord: AutomationRecord = { + let source = Fixtures.runningUserAgent + return AutomationRecord( + id: source.id, kind: source.kind, sourceKind: source.sourceKind, + label: source.label, displayName: source.displayName, + providerBundleIdentifier: source.providerBundleIdentifier, ownerUID: source.ownerUID, + ownership: .user, executable: source.executable, arguments: source.arguments, + commandSignature: source.commandSignature, environment: source.environment, + workingDirectory: source.workingDirectory, + schedule: AutomationSchedule(triggers: [.keepAlive], summary: "Keep alive"), + sourceURL: source.sourceURL, sourceChecksum: source.sourceChecksum, + enabledState: .enabled, loadState: .loaded, approvalState: source.approvalState, + state: .running, evidence: source.evidence, capabilities: source.capabilities, + validationFindings: source.validationFindings + ) +}() + +private func copyExpectedRecord(id: AutomationRecord.ID) -> AutomationRecord { + AutomationRecord( + id: id, kind: expectedRecord.kind, sourceKind: expectedRecord.sourceKind, + label: "\(expectedRecord.label).second", displayName: expectedRecord.displayName, + providerBundleIdentifier: expectedRecord.providerBundleIdentifier, + ownerUID: expectedRecord.ownerUID, ownership: expectedRecord.ownership, + executable: expectedRecord.executable, arguments: expectedRecord.arguments, + commandSignature: expectedRecord.commandSignature, environment: expectedRecord.environment, + workingDirectory: expectedRecord.workingDirectory, schedule: expectedRecord.schedule, + sourceURL: expectedRecord.sourceURL, sourceChecksum: expectedRecord.sourceChecksum, + enabledState: expectedRecord.enabledState, loadState: expectedRecord.loadState, + approvalState: expectedRecord.approvalState, state: expectedRecord.state, + evidence: expectedRecord.evidence, capabilities: expectedRecord.capabilities, + validationFindings: expectedRecord.validationFindings + ) +} + +private func process( + elapsed: String, + birthToken: ProcessBirthToken? = ProcessBirthToken(seconds: 10_000, microseconds: 42) +) -> DevProcess { + DevProcess( + pid: Fixtures.runningBackup.pid, parentPID: 1, + executable: Fixtures.runningBackup.executable, command: Fixtures.runningBackup.command, + argumentVector: Fixtures.runningBackup.argumentVector, + resourceUsage: DevProcessResourceUsage( + cpuPercent: 0, residentMemoryBytes: 0, elapsedTime: elapsed + ), + birthToken: birthToken + ) +} + +private func presentation( + elapsed: String = "00:00:00", + birthToken: ProcessBirthToken? = ProcessBirthToken(seconds: 10_000, microseconds: 42), + complete: Bool = true, + sourceHealth: AutomationSourceHealthState = .healthy, + processes: [DevProcess]? = nil +) -> AutomationPresentationSnapshot { + let inventory = AutomationInventorySnapshot( + generation: 7, + records: [expectedRecord], + health: [.launchAgent: AutomationSourceHealth( + kind: .launchAgent, state: sourceHealth, message: nil, refreshedAt: date(0) + )], + refreshedAt: date(0) + ) + return AutomationPresentationSnapshot.build( + inventory: inventory, + processes: processes ?? [process(elapsed: elapsed, birthToken: birthToken)], + longRunningThreshold: 14_400, + now: date(20_000), + isProcessSnapshotComplete: complete + ) +} + +private func date(_ seconds: TimeInterval) -> Date { + Date(timeIntervalSince1970: seconds) +} + +private func eventIdentity(_ process: DevProcess) -> ProcessIdentity { + ProcessIdentity(pid: process.pid, birthToken: process.birthToken) +} + +private func emitVerifiedExit( + detector: inout AutomationEventDetector, + timestamp: TimeInterval +) -> [AutomationEvent] { + let running = presentation(elapsed: "00:01:00") + let absent = presentation(processes: []) + _ = detector.events(previous: running, current: absent, now: date(timestamp - 1)) + let events = detector.events(previous: absent, current: absent, now: date(timestamp)) + _ = detector.events(previous: absent, current: running, now: date(timestamp + 0.1)) + return events +} diff --git a/Tests/DevScopeCoreTests/AutomationExecutorsTests.swift b/Tests/DevScopeCoreTests/AutomationExecutorsTests.swift new file mode 100644 index 0000000..a494c5e --- /dev/null +++ b/Tests/DevScopeCoreTests/AutomationExecutorsTests.swift @@ -0,0 +1,1170 @@ +import CryptoKit +import Darwin +import XCTest +@testable import DevScopeCore + +final class AutomationExecutorsTests: XCTestCase { + func testCronPublicCompositionInitializerAcceptsFreshProcessSnapshotProvider() async throws { + let runner = RecordingAutomationCommandRunner() + let record = cronRecord(command: "/usr/bin/true", scheduleExpression: "* * * * *") + let executor = CronAutomationExecutor( + runner: runner, + fileSystem: InMemoryAutomationFileSystem(), + currentUID: 501, + processSnapshot: { [] } + ) + + let confirmation = AutomationRunToCompletionConfirmation( + recordID: record.id, + sourceChecksum: record.sourceChecksum, + exactCommand: "/usr/bin/true" + ) + let result = try await executor.apply(.confirmedRunToCompletion(confirmation), to: record) + + XCTAssertEqual(result.postconditions, [.targetResolved, .runCompleted]) + } + + func testCronChecksumNormalizesLineEndingsAndExactlyOneTerminalNewline() throws { + let zero = Data("0 * * * * /usr/bin/true".utf8) + let one = Data("0 * * * * /usr/bin/true\n".utf8) + let multiple = Data("0 * * * * /usr/bin/true\n\n\n".utf8) + let crlf = Data("0 * * * * /usr/bin/true\r\n".utf8) + + XCTAssertEqual(CronDocumentChecksum.checksum(zero), CronDocumentChecksum.checksum(one)) + XCTAssertEqual(CronDocumentChecksum.checksum(one), CronDocumentChecksum.checksum(multiple)) + XCTAssertEqual(CronDocumentChecksum.checksum(one), CronDocumentChecksum.checksum(crlf)) + XCTAssertNotEqual( + CronDocumentChecksum.checksum(Data("# interior\n\n0 * * * * /usr/bin/true\n".utf8)), + CronDocumentChecksum.checksum(Data("# interior\n0 * * * * /usr/bin/true\n".utf8)) + ) + } + + func testLaunchdStartUsesKickstartWithAnArgumentArray() async throws { + let runner = RecordingAutomationCommandRunner() + let executor = LaunchdAutomationExecutor(runner: runner, guiUID: 501) + + _ = try await executor.apply(.startNow, to: Fixtures.runningUserAgent) + + XCTAssertEqual(runner.invocations, [ + AutomationCommand( + executable: "/bin/launchctl", + arguments: ["kickstart", "gui/501/com.example.backup"] + ), + ]) + } + + func testLaunchdStartBootstrapsCanonicalSourceBeforeKickstartingAnUnloadedService() async throws { + let runner = RecordingAutomationCommandRunner() + let executor = LaunchdAutomationExecutor(runner: runner, guiUID: 501) + + _ = try await executor.apply(.startNow, to: Fixtures.userAgent) + + XCTAssertEqual(runner.invocations.map(\.arguments), [ + ["bootstrap", "gui/501", Fixtures.userAgent.sourceURL!.path], + ["kickstart", "gui/501/com.example.backup"], + ]) + } + + func testLaunchdDisablePreventsFutureLaunchesWithoutClaimingTheProcessStopped() async throws { + let runner = RecordingAutomationCommandRunner() + let executor = LaunchdAutomationExecutor(runner: runner, guiUID: 501) + + let result = try await executor.apply(.disable, to: Fixtures.runningUserAgent) + + XCTAssertEqual(runner.invocations, [ + AutomationCommand( + executable: "/bin/launchctl", + arguments: ["disable", "gui/501/com.example.backup"] + ), + ]) + XCTAssertEqual(result.postconditions, [.futureLaunchesDisabled]) + XCTAssertFalse(result.postconditions.contains(.noLinkedProcess)) + } + + func testLaunchdDisableAndStopDisablesThenBootsOutTheExactService() async throws { + let birth = ProcessBirthToken(seconds: 9_000, microseconds: 7) + let process = classifiedProcess(pid: 44_001, birth: birth) + let runner = RecordingAutomationCommandRunner() + let executor = LaunchdAutomationExecutor(runner: runner, guiUID: 501) + + let result = try await executor.apply( + .disableAndStop, + to: Fixtures.runningUserAgent, + linkedProcesses: [process], + proposedSourceURL: nil + ) + + XCTAssertEqual(runner.invocations.map(\.arguments), [ + ["disable", "gui/501/com.example.backup"], + ["bootout", "gui/501/com.example.backup"], + ]) + XCTAssertEqual(result.postconditions, [.futureLaunchesDisabled, .noLinkedProcess]) + } + + func testLaunchdEnableBootstrapsCanonicalPlistOnlyWhenTargetIsNotLoaded() async throws { + let runner = ScriptedAutomationCommandRunner(results: [ + missingLaunchctlServiceResult(), + successfulCommandResult(), + successfulCommandResult(), + ]) + let executor = LaunchdAutomationExecutor(runner: runner, guiUID: 501) + + let result = try await executor.apply(.enable, to: Fixtures.userAgent) + + XCTAssertEqual(runner.invocations.map(\.arguments), [ + ["print", "gui/501/com.example.backup"], + ["enable", "gui/501/com.example.backup"], + ["bootstrap", "gui/501", Fixtures.userAgent.sourceURL!.path], + ]) + XCTAssertEqual(result.postconditions, [.futureLaunchesEnabled, .targetResolved]) + XCTAssertFalse(result.postconditions.contains(.currentRunStarted)) + } + + func testLaunchdEnableFailsClosedWhenPrintIsPermissionDenied() async { + let runner = ScriptedAutomationCommandRunner(results: [ + AutomationCommandResult( + status: 113, + standardOutput: Data(), + standardError: Data("Operation not permitted\n".utf8) + ), + ]) + let executor = LaunchdAutomationExecutor(runner: runner, guiUID: 501) + + await XCTAssertThrowsErrorAsync { + _ = try await executor.apply(.enable, to: Fixtures.userAgent) + } + + XCTAssertEqual(runner.invocations.map(\.arguments), [ + ["print", "gui/501/com.example.backup"], + ]) + XCTAssertEqual(runner.invocations.first?.environment, ["LC_ALL": "C"]) + } + + func testLaunchctlMissingServiceClassifierRejectsUnexpectedStatusOrDiagnostic() { + let unexpectedDiagnostic = AutomationCommandResult( + status: 113, + standardOutput: Data(), + standardError: Data("Operation not permitted\n".utf8) + ) + let unexpectedStatus = AutomationCommandResult( + status: 5, + standardOutput: Data(), + standardError: missingLaunchctlServiceResult().standardError + ) + + XCTAssertEqual( + LaunchctlServiceTargetClassifier.classify( + unexpectedDiagnostic, + label: "com.example.backup", + guiUID: 501 + ), + .unknown + ) + XCTAssertEqual( + LaunchctlServiceTargetClassifier.classify( + unexpectedStatus, + label: "com.example.backup", + guiUID: 501 + ), + .unknown + ) + } + + func testLaunchdDuplicateExplicitlyDisablesTheDistinctCopyWithoutLoadingIt() async throws { + let label = "com.example.backup.copy" + let destination = URL(fileURLWithPath: "/tmp/devscope-fixtures/\(label).plist") + let data = try PropertyListSerialization.data( + fromPropertyList: [ + "Label": label, + "ProgramArguments": ["/bin/sleep", "14400"], + ], + format: .xml, + options: 0 + ) + let fileSystem = InMemoryAutomationFileSystem(files: [destination: data]) + let runner = RecordingAutomationCommandRunner() + let executor = LaunchdAutomationExecutor( + runner: runner, + guiUID: 501, + fileSystem: fileSystem + ) + let payload = AutomationEditPayload( + label: label, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: data, + destination: destination + ) + + let result = try await executor.apply( + .duplicate(payload), + to: Fixtures.userAgent, + linkedProcesses: [], + proposedSourceURL: destination + ) + + XCTAssertEqual(runner.invocations.map(\.arguments), [ + ["disable", "gui/501/\(label)"], + ]) + XCTAssertTrue(result.postconditions.contains(.futureLaunchesDisabled)) + XCTAssertFalse(result.postconditions.contains(.targetResolved)) + } + + func testLaunchdStopBootsOutTheExactServiceInsteadOfSignallingItsKeepAliveProcess() async throws { + let birth = ProcessBirthToken(seconds: 9_000, microseconds: 7) + let process = classifiedProcess(pid: 44_001, birth: birth) + let runner = RecordingAutomationCommandRunner() + let executor = LaunchdAutomationExecutor(runner: runner, guiUID: 501) + + let result = try await executor.apply( + .stopCurrentRun, + to: Fixtures.runningUserAgent, + linkedProcesses: [process], + proposedSourceURL: nil + ) + + XCTAssertEqual(runner.invocations.map(\.arguments), [ + ["bootout", "gui/501/com.example.backup"], + ]) + XCTAssertEqual(result.postconditions, [.noLinkedProcess]) + } + + func testLaunchdStopFailsClosedWhenBootoutFails() async { + let birth = ProcessBirthToken(seconds: 9_000, microseconds: 7) + let process = classifiedProcess(pid: 44_002, birth: birth) + let runner = ScriptedAutomationCommandRunner(statuses: [5]) + let executor = LaunchdAutomationExecutor(runner: runner, guiUID: 501) + + do { + _ = try await executor.apply( + .stopCurrentRun, + to: Fixtures.runningUserAgent, + linkedProcesses: [process], + proposedSourceURL: nil + ) + XCTFail("Expected failed bootout to fail closed") + } catch { + XCTAssertEqual( + error as? AutomationExecutorError, + .commandFailed(executable: "/bin/launchctl", status: 5) + ) + } + XCTAssertEqual(runner.invocations.map(\.arguments), [ + ["bootout", "gui/501/com.example.backup"], + ]) + } + + func testLaunchdRollbackRestoresDisabledButLoadedStateExactly() async throws { + let runner = ScriptedAutomationCommandRunner(results: [ + successfulCommandResult(), + missingLaunchctlServiceResult(), + successfulCommandResult(), + ]) + let executor = LaunchdAutomationExecutor(runner: runner, guiUID: 501) + let state = AutomationPreTransactionState( + enabledState: .disabled, + loadState: .loaded, + linkedProcesses: [] + ) + + let result = try await executor.restorePreTransactionState( + state, + for: Fixtures.runningUserAgent, + recovery: verifiedRecoveryInput( + data: Data(), + url: URL(fileURLWithPath: "/tmp/recovery.backup") + ), + linkedProcesses: [], + expectedAppliedState: nil + ) + + XCTAssertEqual(runner.invocations.map(\.arguments), [ + ["disable", "gui/501/com.example.backup"], + ["print", "gui/501/com.example.backup"], + ["bootstrap", "gui/501", Fixtures.runningUserAgent.sourceURL!.path], + ]) + XCTAssertEqual(result.postconditions, [.preTransactionStateRestored(state)]) + } + + func testLaunchdRollbackFailsClosedWhenPrintStateIsUnknown() async { + let runner = ScriptedAutomationCommandRunner(results: [ + successfulCommandResult(), + AutomationCommandResult( + status: 113, + standardOutput: Data(), + standardError: Data("Operation not permitted\n".utf8) + ), + ]) + let executor = LaunchdAutomationExecutor(runner: runner, guiUID: 501) + let state = AutomationPreTransactionState( + enabledState: .disabled, + loadState: .loaded, + linkedProcesses: [] + ) + + await XCTAssertThrowsErrorAsync { + _ = try await executor.restorePreTransactionState( + state, + for: Fixtures.runningUserAgent, + recovery: verifiedRecoveryInput( + data: Data(), + url: URL(fileURLWithPath: "/tmp/recovery.backup") + ), + linkedProcesses: [], + expectedAppliedState: nil + ) + } + + XCTAssertEqual(runner.invocations.map(\.arguments), [ + ["disable", "gui/501/com.example.backup"], + ["print", "gui/501/com.example.backup"], + ]) + } + + func testLaunchdRemoveUsesStillExistingTrashPathAndProvesTargetUnresolved() async throws { + let runner = ScriptedAutomationCommandRunner(results: [ + missingLaunchctlServiceResult(), + successfulCommandResult(), + missingLaunchctlServiceResult(), + ]) + let executor = LaunchdAutomationExecutor(runner: runner, guiUID: 501) + let trashURL = URL(fileURLWithPath: "/Users/test/.Trash/backup.plist") + + let result = try await executor.apply( + .remove, + to: Fixtures.userAgent, + linkedProcesses: [], + proposedSourceURL: trashURL + ) + + XCTAssertEqual(runner.invocations.map(\.arguments), [ + ["print", "gui/501/com.example.backup"], + ["bootout", "gui/501", trashURL.path], + ["print", "gui/501/com.example.backup"], + ]) + XCTAssertEqual(result.postconditions, [.sourceRemoved, .targetUnresolved]) + } + + func testLaunchdRemoveNeverClaimsSuccessWhenFallbackFailsOrTargetStillResolves() async { + let trashURL = URL(fileURLWithPath: "/Users/test/.Trash/backup.plist") + for statuses in [[113, 5], [0, 0, 0]] as [[Int32]] { + let runner = ScriptedAutomationCommandRunner(statuses: statuses) + let executor = LaunchdAutomationExecutor(runner: runner, guiUID: 501) + await XCTAssertThrowsErrorAsync { + _ = try await executor.apply( + .remove, + to: Fixtures.userAgent, + linkedProcesses: [], + proposedSourceURL: trashURL + ) + } + } + } + + func testLaunchdRemoveDoesNotClaimUnresolvedForUnexpectedFinalPrintFailure() async { + let trashURL = URL(fileURLWithPath: "/Users/test/.Trash/backup.plist") + let runner = ScriptedAutomationCommandRunner(results: [ + missingLaunchctlServiceResult(), + successfulCommandResult(), + AutomationCommandResult( + status: 113, + standardOutput: Data(), + standardError: Data("Operation not permitted\n".utf8) + ), + ]) + let executor = LaunchdAutomationExecutor(runner: runner, guiUID: 501) + + await XCTAssertThrowsErrorAsync { + _ = try await executor.apply( + .remove, + to: Fixtures.userAgent, + linkedProcesses: [], + proposedSourceURL: trashURL + ) + } + + XCTAssertEqual(runner.invocations.map(\.arguments), [ + ["print", "gui/501/com.example.backup"], + ["bootout", "gui/501", trashURL.path], + ["print", "gui/501/com.example.backup"], + ]) + } + + func testCommandFailureReportsOnlyExecutableAndStatus() async { + let secret = "TOKEN=do-not-log" + let runner = ScriptedAutomationCommandRunner(results: [ + AutomationCommandResult( + status: 78, + standardOutput: Data(secret.utf8), + standardError: Data(secret.utf8) + ), + ]) + let executor = LaunchdAutomationExecutor(runner: runner, guiUID: 501) + + do { + _ = try await executor.apply(.startNow, to: Fixtures.userAgent) + XCTFail("Expected the nonzero command to fail") + } catch { + XCTAssertEqual( + error as? AutomationExecutorError, + .commandFailed(executable: "/bin/launchctl", status: 78) + ) + XCTAssertFalse(String(describing: error).contains(secret)) + } + } + + func testCronRunNowRequiresTypedConfirmationAndPassesOneExactCommandArgument() async throws { + let runner = RecordingAutomationCommandRunner() + let record = cronRecord( + command: "printf '%s' \"$TOKEN\" | /usr/bin/logger", + environment: ["SHELL": "/bin/zsh"] + ) + let executor = CronAutomationExecutor(runner: runner, fileSystem: InMemoryAutomationFileSystem(), currentUID: 501) + let confirmation = AutomationRunToCompletionConfirmation( + recordID: record.id, + sourceChecksum: record.sourceChecksum, + exactCommand: record.commandSignature! + ) + + let result = try await executor.apply(.confirmedRunToCompletion(confirmation), to: record) + + XCTAssertEqual(runner.invocations, [ + AutomationCommand( + executable: "/bin/zsh", + arguments: ["-c", "printf '%s' \"$TOKEN\" | /usr/bin/logger"], + environment: record.environment + ), + ]) + XCTAssertEqual(result.postconditions, [.targetResolved, .runCompleted]) + } + + func testCronUnconfirmedStartNowFailsBeforeCommandInvocation() async { + let runner = RecordingAutomationCommandRunner() + let executor = CronAutomationExecutor(runner: runner, fileSystem: InMemoryAutomationFileSystem(), currentUID: 501) + + await XCTAssertThrowsErrorAsync { + _ = try await executor.apply(.startNow, to: cronRecord(command: "/usr/bin/true")) + } + + XCTAssertTrue(runner.invocations.isEmpty) + } + + func testCronEditInstallsAndVerifiesTheCompleteLosslessDocumentThroughA0600File() async throws { + let sourceURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-user.crontab") + let document = "SHELL=/bin/zsh\n# keep this comment\n0 * * * * /usr/bin/true\n" + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: Data(document.utf8)]) + let runner = ScriptedAutomationCommandRunner(results: [ + AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()), + AutomationCommandResult(status: 0, standardOutput: Data(document.utf8), standardError: Data()), + ]) + let executor = CronAutomationExecutor(runner: runner, fileSystem: fileSystem, currentUID: 501) + + let result = try await executor.apply( + .edit(AutomationEditPayload( + label: "Cron entry 1", + executable: "/usr/bin/true", + arguments: [], + environment: ["SHELL": "/bin/zsh"], + workingDirectory: nil, + schedule: AutomationSchedule(triggers: [.cron("0 * * * *")], summary: "Hourly"), + rawRepresentation: Data(document.utf8) + )), + to: cronRecord(command: "/usr/bin/true"), + linkedProcesses: [], + proposedSourceURL: sourceURL + ) + + guard case .writeTemporary(let temporaryURL, permissions: 0o600) = fileSystem.recordedOperations.first else { + return XCTFail("Expected an owner-only complete-document staging file") + } + XCTAssertEqual(runner.invocations, [ + AutomationCommand(executable: "/usr/bin/crontab", arguments: [temporaryURL.path]), + AutomationCommand(executable: "/usr/bin/crontab", arguments: ["-l"], environment: ["LC_ALL": "C"]), + ]) + let checksum = SHA256.hash(data: Data(document.utf8)).map { String(format: "%02x", $0) }.joined() + XCTAssertTrue(result.postconditions.contains(.sourceInstalled)) + XCTAssertTrue(result.postconditions.contains(.sourceChecksum(checksum))) + } + + func testCronInstallDoesNotClaimPostconditionsWhenRelistedDocumentDiffers() async { + let sourceURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-user.crontab") + let intended = Data("0 * * * * /usr/bin/true\n".utf8) + let stale = Data("0 * * * * /usr/bin/false\n".utf8) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: intended]) + let runner = ScriptedAutomationCommandRunner(results: [ + AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()), + AutomationCommandResult(status: 0, standardOutput: stale, standardError: Data()), + ]) + let executor = CronAutomationExecutor(runner: runner, fileSystem: fileSystem, currentUID: 501) + + do { + _ = try await executor.apply( + .edit(AutomationEditPayload( + label: "Cron entry 1", + executable: "/usr/bin/true", + arguments: [], + environment: [:], + workingDirectory: nil, + schedule: AutomationSchedule(triggers: [.cron("0 * * * *")], summary: "Hourly"), + rawRepresentation: intended + )), + to: cronRecord(command: "/usr/bin/true"), + linkedProcesses: [], + proposedSourceURL: sourceURL + ) + XCTFail("Expected live generation mismatch") + } catch let failure as AutomationExecutorMutationFailure { + XCTAssertEqual( + failure.appliedState, + .cronLiveDocument(normalizedChecksum: CronDocumentChecksum.checksum(intended)!) + ) + } catch { + XCTFail("Expected typed applied-state failure, got \(error)") + } + } + + func testCronRollbackRefusesToOverwriteExternallyEditedLiveDocument() async { + let backupURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/cron.backup") + let backup = Data("0 * * * * /usr/bin/original\n".utf8) + let owned = Data("0 * * * * /usr/bin/devscope-change\n".utf8) + let external = Data("0 * * * * /usr/bin/external-edit\n".utf8) + let fileSystem = InMemoryAutomationFileSystem(files: [backupURL: backup]) + let runner = InspectingAutomationCommandRunner { command in + XCTAssertEqual(command.arguments, ["-l"]) + XCTAssertTrue(fileSystem.recordedOperations.contains { + if case .writeTemporary = $0 { return true } + return false + }, "The authenticated backup must be staged before the final live-generation proof.") + return AutomationCommandResult(status: 0, standardOutput: external, standardError: Data()) + } + let executor = CronAutomationExecutor(runner: runner, fileSystem: fileSystem, currentUID: 501) + let state = AutomationPreTransactionState( + enabledState: .enabled, + loadState: .unknown, + linkedProcesses: [] + ) + let backupEntry = try! XCTUnwrap( + CronParser.parse(String(decoding: backup, as: UTF8.self)).entries.first + ) + let record = cronRecord( + command: "/usr/bin/original", + recordID: CronParser.recordID(for: backupEntry, ownerUID: 501) + ) + + await XCTAssertThrowsErrorAsync { + _ = try await executor.restorePreTransactionState( + state, + for: record, + recovery: verifiedRecoveryInput(data: backup, url: backupURL), + linkedProcesses: [], + expectedAppliedState: .cronLiveDocument( + normalizedChecksum: CronDocumentChecksum.checksum(owned)! + ) + ) + } + + XCTAssertEqual(runner.invocations, [ + AutomationCommand(executable: "/usr/bin/crontab", arguments: ["-l"], environment: ["LC_ALL": "C"]), + ]) + XCTAssertFalse(runner.invocations.contains { $0.arguments != ["-l"] }) + } + + func testCronRecoveryConsumesVerifiedBytesWhenBackupPathChangesAfterAuthentication() async throws { + let backupURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/cron.backup") + let verified = Data("0 * * * * /usr/bin/true\n5 * * * * /usr/bin/keep\n".utf8) + let altered = Data("0 * * * * /usr/bin/true\n10 * * * * /usr/bin/unrelated-change\n".utf8) + let owned = Data("# devscope-disabled: 0 * * * * /usr/bin/true\n5 * * * * /usr/bin/keep\n".utf8) + let fileSystem = InMemoryAutomationFileSystem(files: [backupURL: verified]) + let recovery = verifiedRecoveryInput(data: verified, url: backupURL) + fileSystem.setStoredData(altered, at: backupURL) + let callIndex = LockedInvocationIndex() + let runner = InspectingAutomationCommandRunner { command in + switch callIndex.next() { + case 1: + return AutomationCommandResult(status: 0, standardOutput: owned, standardError: Data()) + case 2: + XCTAssertEqual(fileSystem.storedData(at: URL(fileURLWithPath: command.arguments[0])), verified) + return AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()) + default: + return AutomationCommandResult(status: 0, standardOutput: verified, standardError: Data()) + } + } + let entry = try XCTUnwrap(CronParser.parse(String(decoding: verified, as: UTF8.self)).entries.first) + let record = cronRecord( + command: entry.command, + recordID: CronParser.recordID(for: entry, ownerUID: 501) + ) + let state = AutomationPreTransactionState( + enabledState: .enabled, + loadState: .unknown, + linkedProcesses: [] + ) + let executor = CronAutomationExecutor(runner: runner, fileSystem: fileSystem, currentUID: 501) + + let result = try await executor.restorePreTransactionState( + state, + for: record, + recovery: recovery, + linkedProcesses: [], + expectedAppliedState: .cronLiveDocument( + normalizedChecksum: try XCTUnwrap(CronDocumentChecksum.checksum(owned)) + ) + ) + + XCTAssertTrue(result.postconditions.contains(.preTransactionStateRestored(state))) + XCTAssertEqual(fileSystem.storedData(at: backupURL), altered) + } + + func testCronDisableAndStopPreservesOwnedGenerationForEveryPostInstallFailure() async { + let sourceURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-user.crontab") + let intended = Data("# devscope-disabled: * * * * * /usr/bin/true\n".utf8) + let checksum = try! XCTUnwrap(CronDocumentChecksum.checksum(intended)) + let (record, classified, snapshot) = cronProcessFixture() + + let scenarios: [(String, CronAutomationExecutor.ProcessSnapshot, ProcessKiller.IdentityResolver)] = [ + ("snapshot", { throw AutomationExecutorError.processControlUnavailable }, { _ in .notRunning }), + ("poll", { snapshot }, { _ in .identity(ProcessIdentity(process: classified.process)) }), + ] + + for (name, processSnapshot, resolver) in scenarios { + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: intended]) + let runner = ScriptedAutomationCommandRunner(results: [ + AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()), + AutomationCommandResult(status: 0, standardOutput: intended, standardError: Data()), + ]) + let executor = CronAutomationExecutor( + runner: runner, + fileSystem: fileSystem, + currentUID: 501, + processKiller: ProcessKiller( + identityResolver: resolver, + signalSender: { _, _ in } + ), + currentProcessID: 99_999, + processIdentityResolver: resolver, + processSnapshot: processSnapshot, + now: { Date(timeIntervalSince1970: 3_600) }, + terminationVerificationPolicy: .init(maximumAttempts: 1, interval: .zero), + verificationSleep: { _ in } + ) + + do { + _ = try await executor.apply( + .disableAndStop, + to: record, + linkedProcesses: [classified], + proposedSourceURL: sourceURL + ) + XCTFail("Expected \(name) failure") + } catch let failure as AutomationExecutorMutationFailure { + XCTAssertEqual( + failure.appliedState, + .cronLiveDocument(normalizedChecksum: checksum), + name + ) + } catch { + XCTFail("Expected owned-generation mutation failure for \(name), got \(error)") + } + } + } + + func testCronStopRejectsWrongAncestryAndWrongScheduleMinuteBeforeSignal() async { + let birth = ProcessBirthToken(seconds: 3_600, microseconds: 0) + let carrier = DevProcess( + pid: 55_001, + parentPID: 55_000, + executable: "/bin/sh", + command: "/bin/sh -c /usr/bin/true", + argumentVector: ["/bin/sh", "-c", "/usr/bin/true"], + resourceUsage: DevProcessResourceUsage( + cpuPercent: 0, + residentMemoryBytes: 1, + elapsedTime: "00:00:00" + ), + birthToken: birth + ) + let classified = ClassifiedDevProcess( + process: carrier, + classification: DevProcessClassification(kind: .shell, displayName: "cron", projectHint: nil) + ) + let cronDaemon = DevProcess( + pid: 55_000, + parentPID: 1, + executable: "/usr/sbin/cron", + command: "/usr/sbin/cron", + argumentVector: ["/usr/sbin/cron"], + birthToken: ProcessBirthToken(seconds: 1, microseconds: 0) + ) + let signals = RecordedSignals() + let killer = ProcessKiller( + identityResolver: { _ in .identity(ProcessIdentity(process: carrier)) }, + signalSender: signals.send + ) + let cases: [(AutomationRecord, [DevProcess])] = [ + (cronRecord(command: "/usr/bin/true", scheduleExpression: "* * * * *"), [carrier]), + (cronRecord(command: "/usr/bin/true", scheduleExpression: "30 * * * *"), [cronDaemon, carrier]), + ] + + for (record, snapshot) in cases { + let executor = CronAutomationExecutor( + runner: RecordingAutomationCommandRunner(), + fileSystem: InMemoryAutomationFileSystem(), + currentUID: 501, + processKiller: killer, + currentProcessID: 99_999, + processIdentityResolver: { _ in .notRunning }, + processSnapshot: { snapshot }, + now: { Date(timeIntervalSince1970: 3_600) } + ) + await XCTAssertThrowsErrorAsync { + _ = try await executor.apply( + .stopCurrentRun, + to: record, + linkedProcesses: [classified], + proposedSourceURL: nil + ) + } + } + + XCTAssertTrue(signals.values.isEmpty) + } + + func testLegacyLoginItemEnableUsesAFixedProgramAndPassesThePathAsAnArgument() async throws { + let runner = RecordingAutomationCommandRunner() + let record = legacyLoginItemRecord() + let listing = StaticLegacyLoginItemListing(items: [ + LegacyLoginItemDescriptor(name: record.label, path: record.executable!, isHidden: false), + ]) + let executor = LegacyLoginItemAutomationExecutor( + runner: runner, + listing: listing, + currentUID: 501 + ) + + let result = try await executor.apply(.enable, to: record) + + let invocation = try XCTUnwrap(runner.invocations.first) + XCTAssertEqual(invocation.executable, "/usr/bin/osascript") + XCTAssertEqual( + Array(invocation.arguments.suffix(4)), + ["--", "add", "/Applications/Backup.app", "false"] + ) + XCTAssertFalse(invocation.arguments[1].contains("/Applications/Backup.app")) + XCTAssertEqual(result.postconditions, [.futureLaunchesEnabled, .sourceInstalled]) + } + + func testLegacyExecutorRejectsModernBackgroundItemsBeforeAnyCommand() async { + let runner = RecordingAutomationCommandRunner() + let executor = LegacyLoginItemAutomationExecutor( + runner: runner, + listing: StaticLegacyLoginItemListing(items: []), + currentUID: 501 + ) + + await XCTAssertThrowsErrorAsync { + _ = try await executor.apply(.disable, to: Fixtures.backgroundCopyOfUserAgent) + } + + XCTAssertTrue(runner.invocations.isEmpty) + } + + func testLegacyRollbackRestoresTypedHiddenStateForTrueAndFalse() async throws { + let backupURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/legacy.backup") + let record = legacyLoginItemRecord() + let state = AutomationPreTransactionState( + enabledState: .enabled, + loadState: .unknown, + linkedProcesses: [] + ) + + for isHidden in [true, false] { + let descriptor = LegacyLoginItemDescriptor( + name: record.label, + path: record.executable!, + isHidden: isHidden + ) + let descriptorData = try LegacyLoginItemRecoveryDocument.encode( + selectedRecord: record, + descriptor: descriptor, + currentUID: 501 + ) + let fileSystem = InMemoryAutomationFileSystem(files: [backupURL: descriptorData]) + let runner = RecordingAutomationCommandRunner() + let executor = LegacyLoginItemAutomationExecutor( + runner: runner, + listing: StaticLegacyLoginItemListing(items: [ + LegacyLoginItemDescriptor( + name: record.label, + path: record.executable!, + isHidden: isHidden + ), + ]), + currentUID: 501 + ) + let recovery = verifiedRecoveryInput(data: descriptorData, url: backupURL) + fileSystem.setStoredData( + legacyRecoveryDescriptorData(record: record, isHidden: !isHidden), + at: backupURL + ) + + let result = try await executor.restorePreTransactionState( + state, + for: record, + recovery: recovery, + linkedProcesses: [], + expectedAppliedState: nil + ) + + let invocation = try XCTUnwrap(runner.invocations.first) + XCTAssertEqual( + Array(invocation.arguments.suffix(4)), + ["--", "add", record.executable!, isHidden ? "true" : "false"] + ) + XCTAssertEqual(result.postconditions, [.preTransactionStateRestored(state)]) + } + } + + func testLegacyRecoveryDocumentFactoryRejectsUIDAndPathMismatch() throws { + let record = legacyLoginItemRecord() + let matching = LegacyLoginItemDescriptor( + name: record.label, + path: record.executable!, + isHidden: true + ) + XCTAssertThrowsError(try LegacyLoginItemRecoveryDocument.encode( + selectedRecord: record, + descriptor: matching, + currentUID: 502 + )) + XCTAssertThrowsError(try LegacyLoginItemRecoveryDocument.encode( + selectedRecord: record, + descriptor: LegacyLoginItemDescriptor( + name: record.label, + path: "/Applications/Other.app", + isHidden: true + ), + currentUID: 501 + )) + } + + func testLegacyRollbackRejectsMalformedOrMismatchedRecoveryDescriptorBeforeCommand() async { + let backupURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/legacy.backup") + let record = legacyLoginItemRecord() + let state = AutomationPreTransactionState( + enabledState: .enabled, + loadState: .unknown, + linkedProcesses: [] + ) + let invalidDescriptors = [ + Data("{".utf8), + legacyRecoveryDescriptorData(record: record, isHidden: true, ownerUID: 502), + legacyRecoveryDescriptorData(record: record, isHidden: true, recordID: "other-record"), + legacyRecoveryDescriptorData( + record: record, + isHidden: true, + path: "/Applications/Other.app" + ), + ] + + for data in invalidDescriptors { + let runner = RecordingAutomationCommandRunner() + let executor = LegacyLoginItemAutomationExecutor( + runner: runner, + listing: StaticLegacyLoginItemListing(items: []), + currentUID: 501 + ) + await XCTAssertThrowsErrorAsync { + _ = try await executor.restorePreTransactionState( + state, + for: record, + recovery: verifiedRecoveryInput(data: data, url: backupURL), + linkedProcesses: [], + expectedAppliedState: nil + ) + } + XCTAssertTrue(runner.invocations.isEmpty) + } + } +} + +private final class ScriptedAutomationCommandRunner: AutomationCommandRunning, @unchecked Sendable { + private let lock = NSLock() + private var statuses: [Int32] + private var commands: [AutomationCommand] = [] + + private var results: [AutomationCommandResult]? + + init(statuses: [Int32]) { + self.statuses = statuses + results = nil + } + + init(results: [AutomationCommandResult]) { + statuses = [] + self.results = results + } + + var invocations: [AutomationCommand] { + lock.withLock { commands } + } + + func run(_ command: AutomationCommand) async throws -> AutomationCommandResult { + lock.withLock { + commands.append(command) + if var results { + let result = results.isEmpty + ? AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()) + : results.removeFirst() + self.results = results + return result + } + let status = statuses.isEmpty ? 0 : statuses.removeFirst() + return AutomationCommandResult( + status: status, + standardOutput: Data("sensitive stdout".utf8), + standardError: Data("sensitive stderr".utf8) + ) + } + } +} + +private func successfulCommandResult() -> AutomationCommandResult { + AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()) +} + +private func missingLaunchctlServiceResult( + label: String = "com.example.backup", + guiUID: uid_t = 501 +) -> AutomationCommandResult { + AutomationCommandResult( + status: 113, + standardOutput: Data(), + standardError: Data( + "Bad request.\nCould not find service \"\(label)\" in domain for user gui: \(guiUID)\n".utf8 + ) + ) +} + +private final class InspectingAutomationCommandRunner: AutomationCommandRunning, @unchecked Sendable { + private let lock = NSLock() + private let handler: @Sendable (AutomationCommand) -> AutomationCommandResult + private var commands: [AutomationCommand] = [] + + init(handler: @escaping @Sendable (AutomationCommand) -> AutomationCommandResult) { + self.handler = handler + } + + var invocations: [AutomationCommand] { lock.withLock { commands } } + + func run(_ command: AutomationCommand) async throws -> AutomationCommandResult { + lock.withLock { commands.append(command) } + return handler(command) + } +} + +private final class LockedInvocationIndex: @unchecked Sendable { + private let lock = NSLock() + private var value = 0 + + func next() -> Int { + lock.withLock { + value += 1 + return value + } + } +} + +private func cronRecord( + command: String, + environment: [String: String] = [:], + enabledState: AutomationEnabledState = .enabled, + sourceChecksum: String = "cron-checksum", + scheduleExpression: String = "0 * * * *", + recordID: AutomationRecord.ID = AutomationRecord.ID(rawValue: "cron-record") +) -> AutomationRecord { + return AutomationRecord( + id: recordID, + kind: .cron, + sourceKind: .crontab, + label: "Cron entry 1", + displayName: "Cron entry 1", + providerBundleIdentifier: nil, + ownerUID: 501, + ownership: .user, + executable: nil, + arguments: [], + commandSignature: command, + environment: environment, + workingDirectory: nil, + schedule: AutomationSchedule(triggers: [.cron(scheduleExpression)], summary: "Cron"), + sourceURL: nil, + sourceChecksum: sourceChecksum, + enabledState: enabledState, + loadState: .unknown, + approvalState: .notApplicable, + state: enabledState == .enabled ? .idle : .disabled, + evidence: [], + capabilities: [.startNow], + validationFindings: [] + ) +} + +private func classifiedProcess(pid: Int32, birth: ProcessBirthToken) -> ClassifiedDevProcess { + ClassifiedDevProcess( + process: DevProcess( + pid: pid, + parentPID: 1, + executable: "/bin/sleep", + command: "/bin/sleep 14400", + argumentVector: ["/bin/sleep", "14400"], + birthToken: birth, + launchLabel: Fixtures.userAgent.label + ), + classification: DevProcessClassification( + kind: .backgroundAgent, + displayName: "Backup", + projectHint: nil + ) + ) +} + +private func cronProcessFixture() -> ( + record: AutomationRecord, + classified: ClassifiedDevProcess, + snapshot: [DevProcess] +) { + let carrier = DevProcess( + pid: 55_101, + parentPID: 55_100, + executable: "/bin/sh", + command: "/bin/sh -c /usr/bin/true", + argumentVector: ["/bin/sh", "-c", "/usr/bin/true"], + resourceUsage: DevProcessResourceUsage( + cpuPercent: 0, + residentMemoryBytes: 1, + elapsedTime: "00:00:00" + ), + birthToken: ProcessBirthToken(seconds: 3_600, microseconds: 1) + ) + let daemon = DevProcess( + pid: 55_100, + parentPID: 1, + executable: "/usr/sbin/cron", + command: "/usr/sbin/cron", + argumentVector: ["/usr/sbin/cron"], + birthToken: ProcessBirthToken(seconds: 1, microseconds: 0) + ) + return ( + cronRecord(command: "/usr/bin/true", scheduleExpression: "* * * * *"), + ClassifiedDevProcess( + process: carrier, + classification: DevProcessClassification(kind: .shell, displayName: "cron", projectHint: nil) + ), + [daemon, carrier] + ) +} + +private func legacyLoginItemRecord() -> AutomationRecord { + let name = "Backup" + let path = "/Applications/Backup.app" + return AutomationRecord( + id: AutomationRecord.ID( + source: .legacyLoginItem, + ownerUID: 501, + label: name, + sourcePath: path + ), + kind: .loginItem, + sourceKind: .legacyLoginItem, + label: name, + displayName: name, + providerBundleIdentifier: nil, + ownerUID: 501, + ownership: .user, + executable: path, + arguments: [], + environment: [:], + workingDirectory: nil, + schedule: AutomationSchedule(triggers: [.atLogin], summary: "At login"), + sourceURL: URL(fileURLWithPath: "/Applications/Backup.app"), + sourceChecksum: nil, + enabledState: .disabled, + loadState: .unknown, + approvalState: .notApplicable, + state: .disabled, + evidence: [], + capabilities: [.enable, .disable], + validationFindings: [] + ) +} + +private func legacyRecoveryDescriptorData( + record: AutomationRecord, + isHidden: Bool, + ownerUID: uid_t = 501, + path: String? = nil, + recordID: String? = nil +) -> Data { + try! JSONSerialization.data(withJSONObject: [ + "version": 1, + "recordID": recordID ?? record.id.rawValue, + "ownerUID": Int(ownerUID), + "name": record.label, + "path": path ?? record.executable!, + "isHidden": isHidden, + ], options: [.sortedKeys]) +} + +private func verifiedRecoveryInput( + data: Data, + url: URL +) -> AutomationVerifiedRecoveryInput { + AutomationVerifiedRecoveryInput( + backupID: AutomationBackup.ID(rawValue: UUID()), + backupURL: url, + data: data, + checksum: SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined() + ) +} + +private struct StaticLegacyLoginItemListing: LegacyLoginItemListing { + let items: [LegacyLoginItemDescriptor] + + func currentUserLoginItems() async throws -> [LegacyLoginItemDescriptor] { items } +} + +private final class SequencedLiveIdentityResolver: @unchecked Sendable { + private let lock = NSLock() + private var values: [ProcessLiveIdentityResolution] + + init(values: [ProcessLiveIdentityResolution]) { self.values = values } + + func resolve(_ processID: Int32) -> ProcessLiveIdentityResolution { + lock.withLock { values.isEmpty ? .notRunning : values.removeFirst() } + } +} + +private final class RecordedSignals: @unchecked Sendable { + private let lock = NSLock() + private var recorded: [(Int32, Int32)] = [] + + var values: [(Int32, Int32)] { lock.withLock { recorded } } + + func send(_ processID: Int32, _ signal: Int32) { + lock.withLock { recorded.append((processID, signal)) } + } +} + +private func XCTAssertThrowsErrorAsync( + _ expression: () async throws -> Void, + file: StaticString = #filePath, + line: UInt = #line +) async { + do { + try await expression() + XCTFail("Expected an error", file: file, line: line) + } catch {} +} diff --git a/Tests/DevScopeCoreTests/AutomationInventoryServiceTests.swift b/Tests/DevScopeCoreTests/AutomationInventoryServiceTests.swift new file mode 100644 index 0000000..f975f21 --- /dev/null +++ b/Tests/DevScopeCoreTests/AutomationInventoryServiceTests.swift @@ -0,0 +1,785 @@ +import XCTest +@testable import DevScopeCore + +final class AutomationInventoryServiceTests: XCTestCase { + func testTimedOutSourceFailsIndependentlyAndPreservesHealthyRecords() async { + let hanging = CancellationAwareHangingAutomationSource(kind: .serviceManagement) + let healthy = FakeAutomationSource(snapshot: .healthy( + kind: .launchAgent, + records: [Fixtures.userAgent] + )) + let service = AutomationInventoryService( + sources: [hanging, healthy], + sourceTimeout: .milliseconds(20) + ) + + let snapshot = await service.refresh(force: true) + + XCTAssertEqual(snapshot.records.map(\.label), ["com.example.backup"]) + XCTAssertEqual(snapshot.health[.serviceManagement]?.state, .failed) + XCTAssertEqual( + snapshot.health[.serviceManagement]?.message, + "Automation source refresh exceeded its configured time limit." + ) + let observedCancellation = await hanging.waitForCancellation() + XCTAssertTrue(observedCancellation) + } + + func testTimedOutSourceReturnsEvenWhenSourceIgnoresCancellation() async { + let hanging = CancellationIgnoringAutomationSource(kind: .serviceManagement) + let healthy = FakeAutomationSource(snapshot: .healthy( + kind: .launchAgent, + records: [Fixtures.userAgent] + )) + let service = AutomationInventoryService( + sources: [hanging, healthy], + sourceTimeout: .milliseconds(20) + ) + let started = ContinuousClock.now + + let snapshot = await service.refresh(force: true) + let elapsed = ContinuousClock.now - started + await hanging.release() + + XCTAssertLessThan(elapsed, .seconds(1)) + XCTAssertEqual(snapshot.records.map(\.label), ["com.example.backup"]) + XCTAssertEqual(snapshot.health[.serviceManagement]?.state, .failed) + XCTAssertEqual( + snapshot.health[.serviceManagement]?.message, + "Automation source refresh exceeded its configured time limit." + ) + } + + func testForcedRefreshQuarantinesCancellationIgnoringSourceUntilItCompletes() async { + let hanging = CancellationIgnoringAutomationSource(kind: .serviceManagement) + let service = AutomationInventoryService( + sources: [hanging], + sourceTimeout: .milliseconds(20) + ) + + let timedOut = await service.refresh(force: true) + XCTAssertEqual( + timedOut.health[.serviceManagement]?.message, + "Automation source refresh exceeded its configured time limit." + ) + + for _ in 0..<4 { + let quarantined = await service.refresh(force: true) + XCTAssertEqual( + quarantined.health[.serviceManagement]?.message, + "A previous timed-out automation source refresh is still completing; a new call was not started." + ) + } + let blockedMetrics = await hanging.metrics() + XCTAssertEqual(blockedMetrics.invocations, 1) + XCTAssertEqual(blockedMetrics.active, 1) + XCTAssertEqual(blockedMetrics.maximumActive, 1) + + await hanging.release() + let becameIdle = await hanging.waitUntilIdle() + XCTAssertTrue(becameIdle) + + let recovered = await service.refresh(force: true) + let recoveredMetrics = await hanging.metrics() + XCTAssertEqual(recovered.health[.serviceManagement]?.state, .healthy) + XCTAssertEqual(recoveredMetrics.invocations, 2) + XCTAssertEqual(recoveredMetrics.maximumActive, 1) + } + + func testFailedBackgroundSourceDoesNotEraseHealthyLaunchdRecords() async { + let launchd = FakeAutomationSource(snapshot: .healthy( + kind: .launchAgent, + records: [Fixtures.userAgent] + )) + let background = FakeAutomationSource(snapshot: .failed( + kind: .serviceManagement, + message: "Background Task Management output unavailable" + )) + let service = AutomationInventoryService(sources: [launchd, background]) + + let snapshot = await service.refresh(force: true) + + XCTAssertEqual(snapshot.records.map(\.label), ["com.example.backup"]) + XCTAssertEqual(snapshot.health[.serviceManagement]?.state, .failed) + } + + func testExactPlistAndBackgroundIdentityDeduplicateToOneRecord() async { + let backgroundIdentity = copyRecord( + Fixtures.backgroundCopyOfUserAgent, + sourceURL: .some(nil) + ) + let service = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy( + kind: .launchAgent, + records: [Fixtures.userAgent] + )), + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [backgroundIdentity] + )), + ]) + + let snapshot = await service.refresh(force: true) + + XCTAssertEqual(snapshot.records.count, 1) + XCTAssertEqual(snapshot.records[0].evidence.count, 2) + } + + func testBoundedRefreshIntervalReturnsCachedGenerationUntilBoundary() async { + let clock = ControllableAutomationClock(Date(timeIntervalSince1970: 10_000)) + let source = CountingAutomationSource(snapshot: .healthy( + kind: .launchAgent, + records: [Fixtures.userAgent] + )) + let service = AutomationInventoryService( + sources: [source], + minimumRefreshInterval: 60, + now: clock.now + ) + + let first = await service.refresh() + clock.advance(by: 59) + let cached = await service.refresh() + clock.advance(by: 1) + let refreshed = await service.refresh() + let invocationCount = await source.invocationCount() + + XCTAssertEqual(first.generation, 1) + XCTAssertEqual(cached, first) + XCTAssertEqual(refreshed.generation, 2) + XCTAssertEqual(invocationCount, 2) + } + + func testConcurrentRefreshesShareOneRealRefreshAndGeneration() async { + let source = CountingAutomationSource( + snapshot: .healthy(kind: .launchAgent, records: [Fixtures.userAgent]), + delayNanoseconds: 50_000_000 + ) + let service = AutomationInventoryService(sources: [source]) + + async let first = service.refresh(force: true) + async let second = service.refresh(force: true) + let snapshots = await [first, second] + let invocationCount = await source.invocationCount() + + XCTAssertEqual(snapshots.map(\.generation), [1, 1]) + XCTAssertEqual(snapshots[0], snapshots[1]) + XCTAssertEqual(invocationCount, 1) + } + + func testRefreshAfterCurrentStartsOneNewGenerationAfterPreexistingScan() async { + let source = GatedAutomationSource(snapshot: .healthy( + kind: .launchAgent, + records: [Fixtures.userAgent] + )) + let service = AutomationInventoryService(sources: [source]) + + let preexisting = Task { await service.refresh(force: true) } + await source.waitUntilInvocation(1) + let afterCurrent = Task { await service.refreshAfterCurrent() } + await Task.yield() + let countWhileBlocked = await source.invocationCount() + XCTAssertEqual(countWhileBlocked, 1) + + await source.releaseInvocation(1) + await source.waitUntilInvocation(2) + let preexistingSnapshot = await preexisting.value + XCTAssertEqual(preexistingSnapshot.generation, 1) + await source.releaseInvocation(2) + + let afterCurrentSnapshot = await afterCurrent.value + let finalCount = await source.invocationCount() + XCTAssertEqual(afterCurrentSnapshot.generation, 2) + XCTAssertEqual(finalCount, 2) + } + + func testDuplicateSourceKindsPreserveWorstHealthDeterministically() async { + let failed = AutomationSourceSnapshot.failed( + kind: .serviceManagement, + message: "Generic failure", + refreshedAt: Date(timeIntervalSince1970: 100) + ) + let healthy = AutomationSourceSnapshot.healthy( + kind: .serviceManagement, + records: [], + refreshedAt: Date(timeIntervalSince1970: 200) + ) + let service = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: failed), + FakeAutomationSource(snapshot: healthy), + ]) + + let snapshot = await service.refresh(force: true) + + XCTAssertEqual(snapshot.health[.serviceManagement], failed.health) + } + + func testDisplayNameResemblanceNeverDeduplicatesDistinctRecords() async { + let lookalike = copyRecord( + Fixtures.backgroundCopyOfUserAgent, + label: "com.example.unrelated", + displayName: Fixtures.userAgent.displayName, + providerBundleIdentifier: "com.example.different-owner", + executable: "/bin/date", + sourceURL: URL(fileURLWithPath: "/tmp/devscope-fixtures/unrelated.plist") + ) + let service = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy( + kind: .launchAgent, + records: [Fixtures.userAgent] + )), + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [lookalike] + )), + ]) + + let snapshot = await service.refresh(force: true) + + XCTAssertEqual(snapshot.records.count, 2) + } + + func testEqualRankRecordsWithDifferentCapabilitySetsMergeIdenticallyWhenReversed() async { + let editPrimary = copyRecord( + Fixtures.backgroundCopyOfUserAgent, + capabilities: [.edit], + evidence: [AutomationEvidence( + strength: .strong, + source: "edit evidence", + detail: "Exact synthetic identity" + )] + ) + let disablePrimary = copyRecord( + Fixtures.backgroundCopyOfUserAgent, + capabilities: [.disable], + evidence: [AutomationEvidence( + strength: .strong, + source: "disable evidence", + detail: "Exact synthetic identity" + )] + ) + let forward = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [editPrimary] + )), + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [disablePrimary] + )), + ]) + let reverse = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [disablePrimary] + )), + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [editPrimary] + )), + ]) + + let forwardRecords = await forward.refresh(force: true).records + let reverseRecords = await reverse.refresh(force: true).records + + XCTAssertEqual(forwardRecords, reverseRecords) + XCTAssertEqual(forwardRecords.count, 1) + XCTAssertEqual(forwardRecords[0].capabilities, [.disable]) + XCTAssertEqual( + forwardRecords[0].evidence.map(\.source), + ["disable evidence", "edit evidence"] + ) + } + + func testEqualComparatorKeysAttachAmbiguousBridgeDeterministicallyWhenReversed() async { + let tiedID = AutomationRecord.ID(rawValue: "synthetic-component-tie") + let firstAnchor = copyRecord( + Fixtures.backgroundCopyOfUserAgent, + id: tiedID, + label: "com.example.component-tie", + executable: .some(nil), + sourceURL: URL(fileURLWithPath: "/tmp/devscope-fixtures/component-a"), + capabilities: [.exportRecord], + evidence: [AutomationEvidence( + strength: .strong, + source: "first anchor evidence", + detail: "Conflicting synthetic source identity" + )] + ) + let secondAnchor = copyRecord( + Fixtures.backgroundCopyOfUserAgent, + id: tiedID, + label: "com.example.component-tie", + providerBundleIdentifier: .some(nil), + executable: "/tmp/devscope-fixtures/component-bridge", + sourceURL: URL(fileURLWithPath: "/tmp/devscope-fixtures/component-b"), + capabilities: [.exportRecord], + evidence: [AutomationEvidence( + strength: .strong, + source: "second anchor evidence", + detail: "Conflicting synthetic source identity" + )] + ) + let bridge = copyRecord( + Fixtures.backgroundCopyOfUserAgent, + id: tiedID, + label: "com.example.component-tie", + executable: "/tmp/devscope-fixtures/component-bridge", + sourceURL: .some(nil), + capabilities: [.exportRecord], + evidence: [AutomationEvidence( + strength: .strong, + source: "bridge evidence", + detail: "Compatible with either synthetic anchor alone" + )] + ) + let forward = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [firstAnchor, secondAnchor] + )), + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [bridge] + )), + ]) + let reverse = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [bridge] + )), + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [secondAnchor, firstAnchor] + )), + ]) + + let forwardRecords = await forward.refresh(force: true).records + let reverseRecords = await reverse.refresh(force: true).records + + XCTAssertEqual(forwardRecords.count, 2) + XCTAssertEqual(forwardRecords, reverseRecords) + XCTAssertEqual(forwardRecords.filter { record in + record.evidence.contains { $0.source == "bridge evidence" } + }.count, 1) + XCTAssertFalse(forwardRecords.contains { record in + let sources = Set(record.evidence.map(\.source)) + return sources.contains("first anchor evidence") + && sources.contains("second anchor evidence") + }) + } + + func testTransitiveIdentityBridgeRejectsConflictingBundleComponentsInEitherOrder() async { + let bridge = copyRecord( + Fixtures.backgroundCopyOfUserAgent, + label: "com.example.bridge", + executable: "/tmp/devscope-fixtures/bridge", + sourceURL: .some(nil), + ownership: .thirdPartySystem, + capabilities: [.disable], + evidence: [AutomationEvidence( + strength: .strong, + source: "bridge bundle evidence", + detail: "Exact bundle identity" + )] + ) + let pathCopy = copyRecord( + Fixtures.backgroundCopyOfUserAgent, + label: "com.example.bridge", + providerBundleIdentifier: "com.example.other-owner", + executable: "/tmp/devscope-fixtures/folder/../bridge", + sourceURL: .some(nil), + capabilities: [.edit], + evidence: [AutomationEvidence( + strength: .strong, + source: "bridge path evidence", + detail: "Exact label and canonical executable" + )] + ) + let bundleAnchor = copyRecord( + Fixtures.userAgent, + label: "com.example.bridge", + executable: "/tmp/devscope-fixtures/bridge", + sourceURL: .some(nil), + capabilities: [.startNow] + ) + let forward = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy( + kind: .launchAgent, + records: [bundleAnchor] + )), + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [pathCopy, bridge] + )), + ]) + let reverse = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [bridge, pathCopy] + )), + FakeAutomationSource(snapshot: .healthy( + kind: .launchAgent, + records: [bundleAnchor] + )), + ]) + + let forwardRecords = await forward.refresh(force: true).records + let reverseRecords = await reverse.refresh(force: true).records + + XCTAssertEqual(forwardRecords.count, 2) + XCTAssertEqual(forwardRecords, reverseRecords) + let retainedAnchor = forwardRecords.first { + $0.providerBundleIdentifier == "com.example.devscope-fixture-owner" + } + let rejectedConflict = forwardRecords.first { + $0.providerBundleIdentifier == "com.example.other-owner" + } + XCTAssertEqual(retainedAnchor?.id, bundleAnchor.id) + XCTAssertEqual(retainedAnchor?.evidence.count, 2) + XCTAssertEqual(retainedAnchor?.capabilities, [.startNow]) + XCTAssertEqual(rejectedConflict?.id, pathCopy.id) + XCTAssertEqual(rejectedConflict?.evidence.count, 1) + XCTAssertEqual(rejectedConflict?.capabilities, [.edit]) + } + + func testExactBundleEdgeRejectsConflictingLabelExecutableIdentitiesInEitherOrder() async { + let first = copyRecord(Fixtures.userAgent, sourceURL: .some(nil)) + let second = copyRecord( + Fixtures.backgroundCopyOfUserAgent, + label: "com.example.conflicting-label", + executable: "/bin/date", + sourceURL: .some(nil) + ) + let forward = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [first, second] + )), + ]) + let reverse = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [second, first] + )), + ]) + + let forwardRecords = await forward.refresh(force: true).records + let reverseRecords = await reverse.refresh(force: true).records + + XCTAssertEqual(forwardRecords.count, 2) + XCTAssertEqual(forwardRecords, reverseRecords) + } + + func testExactBundleEdgeRejectsConflictingSourceReferencesInEitherOrder() async { + let first = Fixtures.userAgent + let second = Fixtures.backgroundCopyOfUserAgent + let forward = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [first, second] + )), + ]) + let reverse = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [second, first] + )), + ]) + + let forwardRecords = await forward.refresh(force: true).records + let reverseRecords = await reverse.refresh(force: true).records + + XCTAssertEqual(forwardRecords.count, 2) + XCTAssertEqual(forwardRecords, reverseRecords) + } + + func testCompatibleOrMissingStrongIdentitiesStillMergeTransitivelyInEitherOrder() async { + let bundleAnchor = copyRecord( + Fixtures.userAgent, + label: "com.example.bundle-anchor", + executable: .some(nil), + sourceURL: .some(nil), + capabilities: [.startNow] + ) + let bridge = copyRecord( + Fixtures.backgroundCopyOfUserAgent, + label: "com.example.compatible-bridge", + executable: "/tmp/devscope-fixtures/compatible-bridge", + sourceURL: .some(nil), + ownership: .thirdPartySystem, + capabilities: [.disable], + evidence: [AutomationEvidence( + strength: .strong, + source: "compatible bundle bridge", + detail: "Exact bundle identity" + )] + ) + let pathClosure = copyRecord( + Fixtures.backgroundCopyOfUserAgent, + label: "com.example.compatible-bridge", + providerBundleIdentifier: .some(nil), + executable: "/tmp/devscope-fixtures/folder/../compatible-bridge", + sourceURL: .some(nil), + capabilities: [.edit], + evidence: [AutomationEvidence( + strength: .strong, + source: "compatible path closure", + detail: "Exact label and canonical executable" + )] + ) + let forward = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy(kind: .launchAgent, records: [bundleAnchor])), + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [pathClosure, bridge] + )), + ]) + let reverse = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [bridge, pathClosure] + )), + FakeAutomationSource(snapshot: .healthy(kind: .launchAgent, records: [bundleAnchor])), + ]) + + let forwardRecords = await forward.refresh(force: true).records + let reverseRecords = await reverse.refresh(force: true).records + + XCTAssertEqual(forwardRecords.count, 1) + XCTAssertEqual(forwardRecords, reverseRecords) + XCTAssertEqual(forwardRecords[0].id, bundleAnchor.id) + XCTAssertEqual(forwardRecords[0].evidence.count, 3) + XCTAssertEqual(forwardRecords[0].capabilities, [.startNow]) + } + + func testExactCanonicalSourceReferenceDeduplicatesIndependentOfInputOrder() async { + let sharedURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/folder/../shared-source") + let first = copyRecord( + Fixtures.userAgent, + label: "com.example.first", + providerBundleIdentifier: .some(nil), + executable: .some(nil), + sourceURL: sharedURL + ) + let second = copyRecord( + Fixtures.backgroundCopyOfUserAgent, + label: "com.example.second", + providerBundleIdentifier: "com.example.second", + executable: .some(nil), + sourceURL: URL(fileURLWithPath: "/tmp/devscope-fixtures/shared-source") + ) + let forward = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [first, second] + )), + ]) + let reverse = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy( + kind: .serviceManagement, + records: [second, first] + )), + ]) + + let forwardRecords = await forward.refresh(force: true).records + let reverseRecords = await reverse.refresh(force: true).records + + XCTAssertEqual(forwardRecords.count, 1) + XCTAssertEqual(forwardRecords, reverseRecords) + XCTAssertEqual(forwardRecords[0].id, first.id) + } + + func testForcedRefreshBypassesThrottleAndThenBecomesCachedGeneration() async { + let clock = ControllableAutomationClock(Date(timeIntervalSince1970: 20_000)) + let source = CountingAutomationSource(snapshot: .healthy( + kind: .launchAgent, + records: [Fixtures.userAgent] + )) + let service = AutomationInventoryService( + sources: [source], + minimumRefreshInterval: 60, + now: clock.now + ) + + let first = await service.refresh() + let forced = await service.refresh(force: true) + let cached = await service.refresh() + let invocationCount = await source.invocationCount() + + XCTAssertEqual(first.generation, 1) + XCTAssertEqual(forced.generation, 2) + XCTAssertEqual(cached, forced) + XCTAssertEqual(invocationCount, 2) + } + + func testCommandSignatureParticipatesInDeterministicPrimaryOrdering() async { + let alpha = copyRecord(Fixtures.userAgent, commandSignature: "alpha") + let omega = copyRecord(Fixtures.userAgent, commandSignature: "omega") + let forward = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy(kind: .launchAgent, records: [omega, alpha])), + ]) + let reverse = AutomationInventoryService(sources: [ + FakeAutomationSource(snapshot: .healthy(kind: .launchAgent, records: [alpha, omega])), + ]) + + let forwardRecords = await forward.refresh(force: true).records + let reverseRecords = await reverse.refresh(force: true).records + + XCTAssertEqual(forwardRecords, reverseRecords) + XCTAssertEqual(forwardRecords.first?.commandSignature, "alpha") + } +} + +private actor CancellationAwareHangingAutomationSource: AutomationSource { + nonisolated let kind: AutomationSourceKind + private var wasCancelled = false + + init(kind: AutomationSourceKind) { + self.kind = kind + } + + func snapshot() async -> AutomationSourceSnapshot { + do { + try await Task.sleep(for: .seconds(30)) + } catch is CancellationError { + wasCancelled = true + } catch {} + return .healthy(kind: kind, records: []) + } + + func waitForCancellation() async -> Bool { + for _ in 0..<100 { + if wasCancelled { return true } + try? await Task.sleep(for: .milliseconds(5)) + } + return false + } +} + +private actor CancellationIgnoringAutomationSource: AutomationSource { + nonisolated let kind: AutomationSourceKind + private var isReleased = false + private var invocations = 0 + private var active = 0 + private var maximumActive = 0 + + init(kind: AutomationSourceKind) { + self.kind = kind + } + + func snapshot() async -> AutomationSourceSnapshot { + invocations += 1 + active += 1 + maximumActive = max(maximumActive, active) + defer { active -= 1 } + while !isReleased { + try? await Task.sleep(for: .milliseconds(5)) + await Task.yield() + } + return .healthy(kind: kind, records: []) + } + + func release() { + isReleased = true + } + + func metrics() -> (invocations: Int, active: Int, maximumActive: Int) { + (invocations, active, maximumActive) + } + + func waitUntilIdle() async -> Bool { + for _ in 0..<100 { + if active == 0 { return true } + try? await Task.sleep(for: .milliseconds(5)) + } + return false + } +} + +private actor GatedAutomationSource: AutomationSource { + nonisolated let kind: AutomationSourceKind + private let value: AutomationSourceSnapshot + private var count = 0 + private var invocationWaiters: [(Int, CheckedContinuation)] = [] + private var releaseWaiters: [Int: CheckedContinuation] = [:] + + init(snapshot: AutomationSourceSnapshot) { + kind = snapshot.health.kind + value = snapshot + } + + func snapshot() async -> AutomationSourceSnapshot { + count += 1 + let invocation = count + let ready = invocationWaiters.filter { $0.0 <= count } + invocationWaiters.removeAll { $0.0 <= count } + for (_, waiter) in ready { waiter.resume() } + await withCheckedContinuation { continuation in + releaseWaiters[invocation] = continuation + } + return value + } + + func waitUntilInvocation(_ target: Int) async { + guard count < target else { return } + await withCheckedContinuation { continuation in + invocationWaiters.append((target, continuation)) + } + } + + func releaseInvocation(_ invocation: Int) { + releaseWaiters.removeValue(forKey: invocation)?.resume() + } + + func invocationCount() -> Int { count } +} + +private func copyRecord( + _ record: AutomationRecord, + id: AutomationRecord.ID? = nil, + label: String? = nil, + displayName: String? = nil, + providerBundleIdentifier: String?? = nil, + executable: String?? = nil, + commandSignature: String?? = nil, + sourceURL: URL?? = nil, + ownership: AutomationOwnership? = nil, + capabilities: Set? = nil, + evidence: [AutomationEvidence]? = nil +) -> AutomationRecord { + let label = label ?? record.label + let bundleIdentifier = providerBundleIdentifier ?? record.providerBundleIdentifier + let executable = executable ?? record.executable + let sourceURL = sourceURL ?? record.sourceURL + return AutomationRecord( + id: id ?? AutomationRecord.ID( + source: record.sourceKind, + ownerUID: record.ownerUID ?? 0, + label: label, + sourcePath: sourceURL?.path ?? executable ?? label + ), + kind: record.kind, + sourceKind: record.sourceKind, + label: label, + displayName: displayName ?? record.displayName, + providerBundleIdentifier: bundleIdentifier, + ownerUID: record.ownerUID, + ownership: ownership ?? record.ownership, + executable: executable, + arguments: record.arguments, + commandSignature: commandSignature ?? record.commandSignature, + environment: record.environment, + workingDirectory: record.workingDirectory, + schedule: record.schedule, + sourceURL: sourceURL, + sourceChecksum: record.sourceChecksum, + enabledState: record.enabledState, + loadState: record.loadState, + approvalState: record.approvalState, + state: record.state, + evidence: evidence ?? record.evidence, + capabilities: capabilities ?? record.capabilities, + validationFindings: record.validationFindings + ) +} diff --git a/Tests/DevScopeCoreTests/AutomationManagerTests.swift b/Tests/DevScopeCoreTests/AutomationManagerTests.swift new file mode 100644 index 0000000..fd0de53 --- /dev/null +++ b/Tests/DevScopeCoreTests/AutomationManagerTests.swift @@ -0,0 +1,5092 @@ +import CryptoKit +import XCTest +@testable import DevScopeCore + +final class AutomationManagerTests: XCTestCase { + func testStartNowVerifiesAStrongFreshProcessLinkWithoutInventingInventoryRuntimeState() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let record = copyRecord( + Fixtures.userAgent, + sourceChecksum: checksum, + loadState: .loaded, + state: .idle + ) + let process = classifiedAutomationProcess( + pid: 62_001, + birth: ProcessBirthToken(seconds: 20_000, microseconds: 1) + ) + let snapshot = AutomationInventorySnapshot( + generation: 8, + records: [record], + health: [ + .launchAgent: AutomationSourceHealth( + kind: .launchAgent, + state: .healthy, + message: nil, + refreshedAt: Date(timeIntervalSince1970: 20_000) + ), + ], + refreshedAt: Date(timeIntervalSince1970: 20_000) + ) + let manager = makeManager( + fileSystem: InMemoryAutomationFileSystem(files: [sourceURL: sourceData]), + executor: RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.targetResolved, .currentRunStarted], + evidence: ["fixture started"] + )), + ]), + refresh: { snapshot }, + refreshProcesses: { [process.process] } + ) + + let result = await manager.perform( + .startNow, + record: record, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .succeeded) + XCTAssertTrue(result.verificationEvidence.contains( + "Fresh process truth confirms a strong link to the requested automation." + )) + } + + func testStaleChecksumBlocksBeforeBackupOrApply() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = Data("original source".utf8) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor() + let manager = makeManager(fileSystem: fileSystem, executor: executor) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: "stale-checksum", + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .rejected("The automation source changed since it was inspected.")) + XCTAssertEqual(fileSystem.recordedOperations, []) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testWrongOwnerBlocksBeforeBackupOrApply() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = Data("original source".utf8) + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + metadata: [ + sourceURL: AutomationFileMetadata( + canonicalURL: sourceURL, + ownerUID: 502, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 1_000), + resourceIdentifier: "wrong-owner-source" + ), + ] + ) + let executor = RecordingAutomationMutationExecutor() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 502) + ) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .rejected("The automation source is not owned by the current user.")) + XCTAssertEqual(fileSystem.recordedOperations, []) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testSuccessfulDisableCreatesOwnerOnlyBackupAndVerifiesRefreshedState() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let disabledRecord = copyRecord( + Fixtures.userAgent, + sourceChecksum: checksum, + enabledState: .disabled, + state: .disabled + ) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled], + evidence: ["launchctl confirmed disabled"] + )), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { snapshot(records: [disabledRecord]) } + ) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .succeeded) + XCTAssertNotNil(result.backup) + XCTAssertEqual(result.rollback, .notNeeded) + XCTAssertEqual(result.verificationEvidence, [ + "Executor evidence redacted.", + "Refreshed inventory confirms future launches are disabled.", + ]) + XCTAssertTrue(fileSystem.recordedOperations.contains { + if case .createDirectory(_, permissions: 0o700) = $0 { return true } + return false + }) + XCTAssertTrue(fileSystem.recordedOperations.contains { + if case .writeTemporary(_, permissions: 0o600) = $0 { return true } + return false + }) + } + + func testBackupPostWriteCanonicalMetadataMustVerifyBeforePublication() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let backupURL = URL(fileURLWithPath: + "/tmp/devscope-fixtures/recovery/11111111-2222-3333-4444-555555555555.backup" + ) + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + metadataAfterReplace: [backupURL: AutomationFileMetadata( + canonicalURL: URL(fileURLWithPath: "/tmp/outside-recovery/forged.backup"), + ownerUID: 501, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 1_000), + resourceIdentifier: "forged-backup", + permissions: 0o600 + )] + ) + let executor = RecordingAutomationMutationExecutor() + let manager = makeManager(fileSystem: fileSystem, executor: executor) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .failed("DevScope could not create owner-only recovery evidence.") + ) + XCTAssertTrue( + fileSystem.itemExists(at: backupURL), + "A post-write identity mismatch must not authorize deleting the replacement leaf." + ) + XCTAssertFalse(fileSystem.recordedOperations.contains { + if case .remove(let removedURL) = $0 { return removedURL == backupURL } + return false + }) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testExportReturnsRedactedArtifactWithoutSourceIOOrExecutorSideEffects() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let secret = "PRIVATE_TOKEN=do-not-export" + let sourceData = Data(secret.utf8) + let sourceChecksum = sha256(sourceData) + let record = AutomationRecord( + id: Fixtures.userAgent.id, + kind: Fixtures.userAgent.kind, + sourceKind: Fixtures.userAgent.sourceKind, + label: Fixtures.userAgent.label, + displayName: Fixtures.userAgent.displayName, + providerBundleIdentifier: nil, + ownerUID: 501, + ownership: .user, + executable: "/bin/sh", + arguments: ["-c", secret], + environment: ["TOKEN": secret], + workingDirectory: "/private/workspace", + schedule: Fixtures.userAgent.schedule, + sourceURL: sourceURL, + sourceChecksum: sourceChecksum, + enabledState: .enabled, + loadState: .loaded, + approvalState: .notApplicable, + state: .running, + evidence: [AutomationEvidence(strength: .strong, source: "test", detail: secret)], + capabilities: [.exportRecord], + validationFindings: [secret] + ) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor(results: [.failure(.applyFailed)]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + context: .fixture( + currentUID: 501, + canonicalPathIsApproved: true, + ownerUID: 501, + implementedCapabilities: [.exportRecord] + ) + ) + + let result = await manager.perform( + .exportRecord(redacted: true), + record: record, + expectedChecksum: record.sourceChecksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .succeeded) + XCTAssertEqual(result.rollback, .notNeeded) + XCTAssertNil(result.backup) + let artifact = try! XCTUnwrap(result.exportArtifact) + XCTAssertTrue(artifact.isRedacted) + XCTAssertEqual(artifact.mediaType, "application/json") + XCTAssertFalse(String(decoding: artifact.data, as: UTF8.self).contains(secret)) + XCTAssertFalse(fileSystem.recordedOperations.contains { + if case .createDirectory = $0 { return true } + return false + }) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testUnredactedUserExportReturnsExactChecksumVerifiedSourceBytes() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let record = copyRecord(Fixtures.userAgent, sourceChecksum: checksum) + let executor = RecordingAutomationMutationExecutor(results: [.failure(.applyFailed)]) + let manager = makeManager(fileSystem: InMemoryAutomationFileSystem(files: [ + sourceURL: sourceData, + ]), executor: executor) + + let result = await manager.perform( + .exportRecord(redacted: false), + record: record, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .succeeded) + let artifact = try! XCTUnwrap(result.exportArtifact) + XCTAssertFalse(artifact.isRedacted) + XCTAssertEqual(artifact.format, "source.plist") + XCTAssertEqual(artifact.mediaType, "application/x-plist") + XCTAssertEqual(artifact.data, sourceData) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testProtectedRecordExportForcesRedactionAndNeedsNoReadableSource() async { + let secret = "--private-system-argument" + let record = AutomationRecord( + id: AutomationRecord.ID( + source: .serviceManagement, + ownerUID: 0, + label: "com.example.protected", + sourcePath: "bundle:com.example.protected" + ), + kind: .backgroundItem, + sourceKind: .serviceManagement, + label: "com.example.protected", + displayName: "Protected Background Item", + providerBundleIdentifier: "com.example.protected", + ownerUID: nil, + ownership: .thirdPartySystem, + executable: "/private/protected/helper", + arguments: [secret], + environment: ["SECRET": secret], + workingDirectory: nil, + schedule: AutomationSchedule(triggers: [.demand], summary: "On demand"), + sourceURL: nil, + sourceChecksum: "diagnostic-generation", + enabledState: .unknown, + loadState: .unknown, + approvalState: .unknown, + state: .unresolved, + evidence: [AutomationEvidence(strength: .strong, source: "diagnostic", detail: secret)], + capabilities: [.exportRecord], + validationFindings: [] + ) + let executor = RecordingAutomationMutationExecutor(results: [.failure(.applyFailed)]) + let manager = makeManager( + fileSystem: InMemoryAutomationFileSystem(files: [:]), + executor: executor, + context: .fixture( + currentUID: 501, + canonicalPathIsApproved: false, + ownerUID: nil, + implementedCapabilities: [.exportRecord] + ) + ) + + let result = await manager.perform( + .exportRecord(redacted: false), + record: record, + expectedChecksum: record.sourceChecksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .succeeded) + let artifact = try! XCTUnwrap(result.exportArtifact) + XCTAssertTrue(artifact.isRedacted) + XCTAssertFalse(String(decoding: artifact.data, as: UTF8.self).contains(secret)) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testInvalidEditedSourceLeavesOriginalUnchangedAndNeverApplies() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor() + let manager = makeManager(fileSystem: fileSystem, executor: executor) + let payload = AutomationEditPayload( + label: Fixtures.userAgent.label, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: Data("not a property list".utf8) + ) + + let result = await manager.perform( + .edit(payload), + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .rejected("The launchd property list is not semantically valid.")) + XCTAssertEqual(fileSystem.storedData(at: sourceURL), sourceData) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testFailedApplyRestoresBackupAndPriorEnabledState() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor(results: [ + .failure(.applyFailed), + .success(AutomationExecutorResult( + postconditions: [.futureLaunchesEnabled], + evidence: ["prior state restored"] + )), + ]) + let manager = makeManager(fileSystem: fileSystem, executor: executor) + let editedData = validLaunchAgentData(arguments: ["7200"]) + let payload = AutomationEditPayload( + label: Fixtures.userAgent.label, + executable: "/bin/sleep", + arguments: ["7200"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + ) + + let result = await manager.perform( + .edit(payload), + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .failed("The automation operation failed; the prior source and loaded state were restored.")) + XCTAssertEqual(result.rollback, .restored(try! XCTUnwrap(result.backup?.id))) + XCTAssertEqual(fileSystem.storedData(at: sourceURL), sourceData) + let operations = await executor.recordedOperations() + XCTAssertEqual(operations, [.edit(payload)]) + } + + func testPostInstallIdentityFailureRestoresOriginalBeforeExecutorRuns() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let editedData = validLaunchAgentData(arguments: ["7200"]) + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + metadataFailuresAfterReplace: [sourceURL: 1] + ) + let executor = RecordingAutomationMutationExecutor() + let manager = makeManager(fileSystem: fileSystem, executor: executor) + let payload = AutomationEditPayload( + label: Fixtures.userAgent.label, + executable: "/bin/sleep", + arguments: ["7200"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + ) + + let result = await manager.perform( + .edit(payload), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .failed("The automation operation failed; the prior source and loaded state were restored.") + ) + XCTAssertEqual(result.rollback, .restored(try! XCTUnwrap(result.backup?.id))) + XCTAssertEqual(fileSystem.storedData(at: sourceURL), sourceData) + XCTAssertFalse(result.appliedSteps.contains { $0.localizedCaseInsensitiveContains("cron") }) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testPostInstallByteMismatchNeverReachesExecutor() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let editedData = validLaunchAgentData(arguments: ["7200"]) + let unreviewedData = validLaunchAgentData(arguments: ["attacker"]) + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + dataAfterReplace: [sourceURL: unreviewedData] + ) + let executor = RecordingAutomationMutationExecutor() + let manager = makeManager(fileSystem: fileSystem, executor: executor) + let payload = AutomationEditPayload( + label: Fixtures.userAgent.label, + executable: "/bin/sleep", + arguments: ["7200"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + ) + + let result = await manager.perform( + .edit(payload), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .partialFailure("HIGH SEVERITY: the operation failed and automatic rollback did not complete.") + ) + XCTAssertEqual(fileSystem.storedData(at: sourceURL), unreviewedData) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testPartialReplacePreservesRecoveryEvidenceAndNeverInvokesExecutor() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let editedData = validLaunchAgentData(arguments: ["7200"]) + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + partialReplaceAfterCommit: [sourceURL] + ) + let executor = RecordingAutomationMutationExecutor() + let manager = makeManager(fileSystem: fileSystem, executor: executor) + let payload = AutomationEditPayload( + label: Fixtures.userAgent.label, + executable: "/bin/sleep", + arguments: ["7200"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + ) + + let result = await manager.perform( + .edit(payload), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertNotNil(result.fileMutationEvidence) + XCTAssertNotNil(result.fileMutationEvidence?.recoveryHandle) + XCTAssertEqual(result.fileMutationEvidence?.commitState, .committed) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testPartialTrashMovePreservesRecoveryEvidenceAndNeverInvokesExecutor() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + partialTrashAfterCommit: [sourceURL] + ) + let executor = RecordingAutomationMutationExecutor() + let manager = makeManager(fileSystem: fileSystem, executor: executor) + + let result = await manager.perform( + .remove, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertNotNil(result.fileMutationEvidence) + XCTAssertNotNil(result.fileMutationEvidence?.recoveryHandle) + XCTAssertEqual(result.fileMutationEvidence?.kind, .trash) + XCTAssertEqual(fileSystem.storedData(at: sourceURL), sourceData) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testPartialBackupInstallIsReportedWithRecoveryHandleBeforeSourceMutation() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + partialReplaceAfterCommit: [deterministicFirstBackupURL()] + ) + let executor = RecordingAutomationMutationExecutor() + let refreshCount = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { + _ = refreshCount.incrementAndGet() + return Fixtures.inventoryGeneration7 + } + ) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .partialFailure("HIGH SEVERITY: recovery evidence installation was only partially completed.") + ) + XCTAssertNotNil(result.fileMutationEvidence?.recoveryHandle) + XCTAssertEqual(refreshCount.current(), 1) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testUnchangedBackupReplacementCleansExactStagedFileAndRemainsOrdinary() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let backupURL = deterministicFirstBackupURL() + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + unchangedReplaceOccurrences: [backupURL: [1]] + ) + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .failed("DevScope could not create owner-only recovery evidence.") + ) + XCTAssertNil(result.fileMutationEvidence) + XCTAssertNil(fileSystem.storedData(at: backupURL)) + let stagedURLs = fileSystem.recordedOperations.compactMap { operation -> URL? in + guard case .writeTemporary(let url, _) = operation else { return nil } + return url + } + XCTAssertEqual(stagedURLs.count, 1) + XCTAssertFalse(fileSystem.itemExists(at: try! XCTUnwrap(stagedURLs.first))) + } + + func testUnchangedReplacementCleanupFailureSurfacesExactStagedRecoveryHandle() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let backupURL = deterministicFirstBackupURL() + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + unchangedReplaceOccurrences: [backupURL: [1]], + unchangedReplaceRetainsStaged: true, + failStagedRemoves: true + ) + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + guard case .partialFailure = result.status else { + return XCTFail("Expected partial failure, got \(result.status)") + } + let handle = try! XCTUnwrap(result.fileMutationEvidence?.recoveryHandle) + XCTAssertTrue(handle.fileURL.lastPathComponent.hasPrefix(".devscope-fixture-")) + XCTAssertTrue(fileSystem.itemExists(at: handle.fileURL)) + XCTAssertEqual(fileSystem.storedData(at: handle.fileURL), sourceData) + } + + func testPartialManifestInstallIsReportedWithRecoveryHandleBeforeSourceMutation() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + partialReplaceAfterCommit: [deterministicFirstManifestURL()] + ) + let executor = RecordingAutomationMutationExecutor() + let refreshCount = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { + _ = refreshCount.incrementAndGet() + return Fixtures.inventoryGeneration7 + } + ) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .partialFailure("HIGH SEVERITY: recovery evidence installation was only partially completed.") + ) + XCTAssertEqual(result.fileMutationEvidence?.resultURL, deterministicFirstManifestURL()) + XCTAssertNil(fileSystem.storedData(at: deterministicFirstManifestURL())) + XCTAssertNil(fileSystem.storedData(at: deterministicFirstBackupURL())) + XCTAssertTrue(result.fileMutationEvidence?.recoveryHandles.isEmpty == true) + XCTAssertEqual(refreshCount.current(), 1) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testObservedNonRecoveryCollisionIsReportedButNeverDeletedOrPromoted() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let manifestURL = deterministicFirstManifestURL() + let collisionURL = manifestURL.deletingLastPathComponent() + .appendingPathComponent("attacker.keep") + let collisionData = Data("attacker-owned".utf8) + let fileSystem = InMemoryAutomationFileSystem( + files: [ + sourceURL: sourceData, + collisionURL: collisionData, + ], + partialReplaceAfterCommit: [manifestURL], + partialReplaceObservedOnly: [manifestURL: [collisionURL]] + ) + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + guard case .partialFailure = result.status else { + return XCTFail("Expected partial failure, got \(result.status)") + } + let evidence = try! XCTUnwrap(result.fileMutationEvidence) + XCTAssertEqual(fileSystem.storedData(at: collisionURL), collisionData) + XCTAssertTrue(evidence.observedFiles.contains { $0.fileURL == collisionURL }) + XCTAssertFalse(evidence.recoveryHandles.contains { $0.fileURL == collisionURL }) + XCTAssertNil(fileSystem.storedData(at: manifestURL)) + XCTAssertNil(fileSystem.storedData(at: deterministicFirstBackupURL())) + } + + func testManifestFailureCannotHidePartialBackupCleanup() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + dataAfterReplace: [deterministicFirstManifestURL(): Data("corrupt".utf8)], + partialRemoveAfterCommit: [deterministicFirstBackupURL()] + ) + let executor = RecordingAutomationMutationExecutor() + let refreshCount = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { + _ = refreshCount.incrementAndGet() + return Fixtures.inventoryGeneration7 + } + ) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + guard case .partialFailure = result.status else { + return XCTFail("Expected partial failure, got \(result.status)") + } + XCTAssertEqual(result.fileMutationEvidence?.kind, .remove) + XCTAssertNotNil(result.fileMutationEvidence?.recoveryHandle) + XCTAssertEqual(refreshCount.current(), 1) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testCorruptCommittedManifestIsRemovedWithItsBackupBeforeOrdinaryFailure() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let backupURL = deterministicFirstBackupURL() + let manifestURL = deterministicFirstManifestURL() + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + dataAfterReplace: [manifestURL: Data("corrupt".utf8)] + ) + let executor = RecordingAutomationMutationExecutor() + let manager = makeManager(fileSystem: fileSystem, executor: executor) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .failed("DevScope could not create owner-only recovery evidence.") + ) + XCTAssertNil(fileSystem.storedData(at: backupURL)) + XCTAssertNil(fileSystem.storedData(at: manifestURL)) + let restarted = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + let manifests = await restarted.restorationManifests() + XCTAssertTrue(manifests.isEmpty) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testVerifiedUnchangedBackupCleanupRemainsAnOrdinaryFailure() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let backupURL = deterministicFirstBackupURL() + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + dataAfterReplace: [backupURL: Data("corrupt".utf8)], + unchangedRemoves: [backupURL] + ) + let executor = RecordingAutomationMutationExecutor() + let manager = makeManager(fileSystem: fileSystem, executor: executor) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .failed("DevScope could not create owner-only recovery evidence.") + ) + XCTAssertNil(result.fileMutationEvidence) + XCTAssertEqual(fileSystem.storedData(at: backupURL), Data("corrupt".utf8)) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testRawBackupCleanupFailureBeforeMutationRemainsOrdinary() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let backupURL = deterministicFirstBackupURL() + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + dataAfterReplace: [backupURL: Data("corrupt".utf8)], + failingRemoves: [backupURL] + ) + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .failed("DevScope could not create owner-only recovery evidence.") + ) + XCTAssertNil(result.fileMutationEvidence) + XCTAssertEqual(fileSystem.storedData(at: backupURL), Data("corrupt".utf8)) + } + + func testVerifiedUnchangedCleanupIsPartialAfterAnotherArtifactWasDeleted() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let backupURL = deterministicFirstBackupURL() + let manifestURL = deterministicFirstManifestURL() + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + dataAfterReplace: [manifestURL: Data("corrupt".utf8)], + unchangedRemoves: [backupURL] + ) + let executor = RecordingAutomationMutationExecutor() + let manager = makeManager(fileSystem: fileSystem, executor: executor) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .partialFailure("HIGH SEVERITY: recovery evidence installation was only partially completed.") + ) + XCTAssertNil(fileSystem.storedData(at: manifestURL)) + XCTAssertNotNil(fileSystem.storedData(at: backupURL)) + XCTAssertTrue(result.fileMutationEvidence?.recoveryHandles.contains { + $0.fileURL == backupURL + } == true) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testFailedVerificationRestoresAndReappliesPriorState() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled], + evidence: ["disable command returned success"] + )), + .success(AutomationExecutorResult( + postconditions: [.futureLaunchesEnabled], + evidence: ["prior state restored"] + )), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { snapshot(records: [Fixtures.userAgent]) } + ) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .failed("The automation operation failed; the prior source and loaded state were restored.")) + XCTAssertEqual(result.rollback, .restored(try! XCTUnwrap(result.backup?.id))) + let operations = await executor.recordedOperations() + XCTAssertEqual(operations, [.disable]) + } + + func testFailedRollbackReportsHighSeverityPartialFailure() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor(results: [ + .failure(.applyFailed), + ], restorationFails: true) + let manager = makeManager(fileSystem: fileSystem, executor: executor) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .partialFailure("HIGH SEVERITY: the operation failed and automatic rollback did not complete.") + ) + XCTAssertEqual(result.rollback, .failed("Automatic rollback did not complete.")) + XCTAssertEqual( + result.manualRecovery, + "Use DevScope Restore with the recorded recovery identifier before retrying." + ) + XCTAssertNotEqual(result.status, .succeeded) + } + + func testConcurrentRequestsAreSerializedAcrossExecutorAwait() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let disabledRecord = copyRecord( + Fixtures.userAgent, + sourceChecksum: checksum, + enabledState: .disabled, + state: .disabled + ) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let success = AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled], + evidence: [] + ) + let executor = RecordingAutomationMutationExecutor( + results: [.success(success), .success(success)], + delayNanoseconds: 50_000_000 + ) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { snapshot(records: [disabledRecord]) } + ) + + async let first = manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + async let second = manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + let results = await [first, second] + + XCTAssertTrue(results.allSatisfy { $0.status == .succeeded }) + let maximumConcurrency = await executor.maximumConcurrency() + XCTAssertEqual(maximumConcurrency, 1) + } + + func testRemoveMovesSourceToTrashBeforeApplyingTypedRemoval() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceRemoved, .targetUnresolved], + evidence: ["service unloaded"] + )), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { snapshot(records: []) } + ) + + let result = await manager.perform( + .remove, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .succeeded) + XCTAssertNil(fileSystem.storedData(at: sourceURL)) + XCTAssertTrue(fileSystem.recordedOperations.contains { + if case .moveToTrash(let removedURL, _) = $0 { return removedURL == sourceURL } + return false + }) + } + + func testRemoveRejectsSourceRecreatedAtSameCanonicalPath() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let recreatedData = validLaunchAgentData(label: "com.example.recreated") + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor( + results: [.success(AutomationExecutorResult( + postconditions: [.sourceRemoved, .targetUnresolved], + evidence: [] + ))], + onApply: { fileSystem.setStoredData(recreatedData, at: sourceURL) } + ) + let recreated = copyRecord( + Fixtures.userAgent, + id: AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: "com.example.recreated", + sourcePath: sourceURL.path + ), + label: "com.example.recreated", + sourceChecksum: sha256(recreatedData) + ) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { snapshot(records: [recreated]) } + ) + + let result = await manager.perform( + .remove, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertNotEqual(result.status, .succeeded) + XCTAssertEqual(fileSystem.storedData(at: sourceURL), recreatedData) + XCTAssertTrue(result.verificationEvidence.contains { $0.contains("transaction identity") }) + } + + func testRestoreUsesBackupFirstWhenRemovedSourceNoLongerExists() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let refreshCount = LockedCallCounter() + let restoredRecord = copyRecord(Fixtures.userAgent, sourceChecksum: checksum) + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceRemoved, .targetUnresolved], + evidence: [] + )), + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(checksum)], + evidence: [] + )), + ]), + refresh: { + refreshCount.incrementAndGet() == 1 + ? snapshot(records: []) + : snapshot(records: [restoredRecord]) + } + ) + let removed = await manager.perform( + .remove, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + let backupID = try! XCTUnwrap(removed.backup?.id) + XCTAssertNil(fileSystem.storedData(at: sourceURL)) + + let restored = await manager.perform( + .restore(backupID), + record: Fixtures.userAgent, + expectedChecksum: nil, + linkedProcesses: [] + ) + + XCTAssertEqual(restored.status, .succeeded) + XCTAssertEqual(fileSystem.storedData(at: sourceURL), sourceData) + } + + func testRestoreRejectsExternallyEditedExistingDestinationWithoutMutation() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let externalData = validLaunchAgentData(arguments: ["external"]) + let checksum = sha256(sourceData) + let disabled = copyRecord( + Fixtures.userAgent, + sourceChecksum: checksum, + enabledState: .disabled, + state: .disabled + ) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult(postconditions: [.futureLaunchesDisabled], evidence: [])), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { snapshot(records: [disabled]) } + ) + let first = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + let backupID = try! XCTUnwrap(first.backup?.id) + fileSystem.setStoredData(externalData, at: sourceURL) + + let restored = await manager.perform( + .restore(backupID), + record: disabled, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual( + restored.status, + .rejected("The restore destination changed since it was inspected.") + ) + XCTAssertEqual(fileSystem.storedData(at: sourceURL), externalData) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 1) + } + + func testFailedRestoreIntoAbsentDestinationRollsBackToAbsenceNormally() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let refreshCount = LockedCallCounter() + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceRemoved, .targetUnresolved], + evidence: [] + )), + .failure(.applyFailed), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { + _ = refreshCount.incrementAndGet() + return snapshot(records: []) + } + ) + let removed = await manager.perform( + .remove, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + let backupID = try! XCTUnwrap(removed.backup?.id) + + let restored = await manager.perform( + .restore(backupID), + record: Fixtures.userAgent, + expectedChecksum: nil, + linkedProcesses: [] + ) + + XCTAssertEqual( + restored.status, + .failed("The automation operation failed; the prior absent source state was restored.") + ) + XCTAssertNotNil({ if case .restored = restored.rollback { return true }; return nil }()) + XCTAssertNil(fileSystem.storedData(at: sourceURL)) + let restorationStates = await executor.recordedRestorationStates() + XCTAssertTrue(restorationStates.isEmpty) + } + + func testManualRestoreOfAbsentBackupRemovesInstalledDestination() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let destination = sourceURL.deletingLastPathComponent() + .appendingPathComponent("duplicate.plist") + let sourceData = validLaunchAgentData() + let duplicateData = validLaunchAgentData(label: "com.example.duplicate") + let duplicateID = AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: "com.example.duplicate", + sourcePath: destination.path + ) + let duplicateRecord = copyRecord( + Fixtures.userAgent, + id: duplicateID, + label: "com.example.duplicate", + sourceURL: destination, + sourceChecksum: sha256(duplicateData), + enabledState: .disabled, + loadState: .unloaded + ) + let refreshCount = LockedCallCounter() + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [ + .sourceInstalled, + .sourceChecksum(sha256(duplicateData)), + .futureLaunchesDisabled, + ], + evidence: [] + )), + .success(AutomationExecutorResult( + postconditions: [.sourceRemoved, .targetUnresolved], + evidence: [] + )), + ]), + refresh: { + refreshCount.incrementAndGet() == 1 + ? snapshot(records: [duplicateRecord]) + : snapshot(records: []) + } + ) + let duplicate = await manager.perform( + .duplicate(AutomationEditPayload( + label: "com.example.duplicate", + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: duplicateData, + destination: destination + )), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + let backup = try! XCTUnwrap(duplicate.backup) + XCTAssertFalse(backup.sourceExisted) + XCTAssertEqual(fileSystem.storedData(at: destination), duplicateData) + + let restored = await manager.perform( + .restore(backup.id), + record: duplicateRecord, + expectedChecksum: sha256(duplicateData), + linkedProcesses: [] + ) + + XCTAssertEqual(restored.status, .succeeded) + XCTAssertNil(fileSystem.storedData(at: destination)) + } + + func testLaunchdDuplicateFailsAndRollsBackUnlessTheCopyIsDisabledAndUnloaded() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let destination = sourceURL.deletingLastPathComponent() + .appendingPathComponent("enabled-duplicate.plist") + let sourceData = validLaunchAgentData() + let duplicateData = validLaunchAgentData(label: "com.example.enabled-duplicate") + let duplicateRecord = copyRecord( + Fixtures.userAgent, + id: AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: "com.example.enabled-duplicate", + sourcePath: destination.path + ), + label: "com.example.enabled-duplicate", + sourceURL: destination, + sourceChecksum: sha256(duplicateData), + enabledState: .enabled, + loadState: .unloaded + ) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [ + .sourceInstalled, + .sourceChecksum(sha256(duplicateData)), + .futureLaunchesDisabled, + ], + evidence: [] + )), + ]), + refresh: { snapshot(records: [duplicateRecord]) } + ) + + let result = await manager.perform( + .duplicate(AutomationEditPayload( + label: duplicateRecord.label, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: duplicateData, + destination: destination + )), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertNotEqual(result.status, .succeeded) + XCTAssertNil(fileSystem.storedData(at: destination)) + } + + func testPartialRemovalWhileRestoringAbsenceIsReportedAndSkipsExecutor() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let destination = sourceURL.deletingLastPathComponent() + .appendingPathComponent("partial-duplicate.plist") + let sourceData = validLaunchAgentData() + let duplicateData = validLaunchAgentData(label: "com.example.partial-duplicate") + let duplicateRecord = copyRecord( + Fixtures.userAgent, + id: AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: "com.example.partial-duplicate", + sourcePath: destination.path + ), + label: "com.example.partial-duplicate", + sourceURL: destination, + sourceChecksum: sha256(duplicateData), + enabledState: .disabled, + loadState: .unloaded + ) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [ + .sourceInstalled, + .sourceChecksum(sha256(duplicateData)), + .futureLaunchesDisabled, + ], + evidence: [] + )), + ]) + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + partialRemoveAfterCommit: [destination] + ) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { snapshot(records: [duplicateRecord]) } + ) + let duplicate = await manager.perform( + .duplicate(AutomationEditPayload( + label: duplicateRecord.label, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: duplicateData, + destination: destination + )), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + let restored = await manager.perform( + .restore(try! XCTUnwrap(duplicate.backup?.id)), + record: duplicateRecord, + expectedChecksum: sha256(duplicateData), + linkedProcesses: [] + ) + + guard case .partialFailure = restored.status else { + return XCTFail("Expected partial failure, got \(restored.status)") + } + XCTAssertEqual(restored.fileMutationEvidence?.kind, .remove) + XCTAssertNotNil(restored.fileMutationEvidence?.recoveryHandle) + XCTAssertEqual(fileSystem.storedData(at: destination), duplicateData) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 1) + } + + func testRestoreAcceptsVerifiedRenamedSuccessorButRejectsUnrelatedRecord() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let originalData = validLaunchAgentData() + let renamedLabel = "com.example.renamed" + let renamedData = validLaunchAgentData(label: renamedLabel) + let originalChecksum = sha256(originalData) + let renamedChecksum = sha256(renamedData) + let renamedID = AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: renamedLabel, + sourcePath: sourceURL.path + ) + let renamedRecord = copyRecord( + Fixtures.userAgent, + id: renamedID, + label: renamedLabel, + sourceChecksum: renamedChecksum + ) + let restoredRecord = copyRecord(Fixtures.userAgent, sourceChecksum: originalChecksum) + let refreshCount = LockedCallCounter() + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: originalData]) + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(renamedChecksum)], evidence: [] + )), + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(originalChecksum)], evidence: [] + )), + ]), + refresh: { + refreshCount.incrementAndGet() == 1 + ? snapshot(records: [renamedRecord]) + : snapshot(records: [restoredRecord]) + } + ) + let renamed = await manager.perform( + .edit(AutomationEditPayload( + label: renamedLabel, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: renamedData + )), + record: Fixtures.userAgent, + expectedChecksum: originalChecksum, + linkedProcesses: [] + ) + let backupID = try! XCTUnwrap(renamed.backup?.id) + + let restored = await manager.perform( + .restore(backupID), + record: renamedRecord, + expectedChecksum: renamedChecksum, + linkedProcesses: [] + ) + XCTAssertEqual(restored.status, .succeeded) + XCTAssertEqual(fileSystem.storedData(at: sourceURL), originalData) + + let unrelated = copyRecord( + renamedRecord, + id: AutomationRecord.ID(rawValue: "unrelated-record") + ) + let rejected = await manager.perform( + .restore(backupID), record: unrelated, expectedChecksum: originalChecksum, linkedProcesses: [] + ) + XCTAssertEqual( + rejected.status, + .rejected("The selected recovery backup does not belong to this automation.") + ) + } + + func testImportRejectsExistingWrongOwnerDestinationBeforeBackup() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let destination = sourceURL.deletingLastPathComponent().appendingPathComponent("import.plist") + let sourceData = validLaunchAgentData() + let importedData = validLaunchAgentData(label: "com.example.imported") + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData, destination: importedData], + metadata: [ + destination: AutomationFileMetadata( + canonicalURL: destination, + ownerUID: 0, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 1_000), + resourceIdentifier: "root-owned-destination" + ), + ] + ) + let executor = RecordingAutomationMutationExecutor() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + destinationContext: { _, _ in + .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 0) + } + ) + let operation = AutomationOperation.importRecord(AutomationImportPayload( + destination: destination, + data: importedData, + expectedKind: .launchAgent, + expectedDestinationChecksum: sha256(importedData) + )) + + let result = await manager.perform( + operation, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .rejected("The intended destination is not owned by the current user.")) + XCTAssertEqual(fileSystem.recordedOperations, []) + } + + func testResultRedactsOperationPayloadAndExecutorEvidence() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let secret = "TOP-SECRET-TOKEN" + let editedData = validLaunchAgentData( + label: Fixtures.userAgent.label, + arguments: [secret], + environment: ["TOKEN": secret], + workingDirectory: "/tmp/\(secret)" + ) + let editedChecksum = sha256(editedData) + let editedRecord = copyRecord( + Fixtures.userAgent, + sourceChecksum: editedChecksum, + enabledState: .enabled, + state: .idle + ) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(editedChecksum)], + evidence: ["executor saw \(secret)"] + )), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { snapshot(records: [editedRecord]) } + ) + let operation = AutomationOperation.edit(AutomationEditPayload( + label: Fixtures.userAgent.label, + executable: "/bin/sleep", + arguments: [secret], + environment: ["TOKEN": secret], + workingDirectory: "/tmp/\(secret)", + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + )) + + let result = await manager.perform( + operation, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .succeeded) + XCTAssertFalse(String(describing: result).contains(secret)) + } + + func testImportCannotVerifyAgainstUnchangedOriginalRecord() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let destination = sourceURL.deletingLastPathComponent().appendingPathComponent("imported.plist") + let sourceData = validLaunchAgentData() + let importedData = validLaunchAgentData(label: "com.example.imported") + let importedChecksum = sha256(importedData) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(importedChecksum)], + evidence: [] + )), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { snapshot(records: [Fixtures.userAgent]) } + ) + + let result = await manager.perform( + .importRecord(AutomationImportPayload( + destination: destination, + data: importedData, + expectedKind: .launchAgent + )), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertNotEqual(result.status, .succeeded) + XCTAssertNil(fileSystem.storedData(at: destination)) + guard let backupID = result.backup?.id else { + return XCTFail("Expected recovery backup, got \(result.status)") + } + XCTAssertEqual(result.rollback, .restored(backupID)) + } + + func testLabelChangingEditVerifiesNewIdentityAtSameDestination() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let newLabel = "com.example.renamed" + let editedData = validLaunchAgentData(label: newLabel) + let editedChecksum = sha256(editedData) + let newID = AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: newLabel, + sourcePath: sourceURL.path + ) + let editedRecord = copyRecord( + Fixtures.userAgent, + id: newID, + label: newLabel, + sourceURL: sourceURL, + sourceChecksum: editedChecksum + ) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(editedChecksum)], + evidence: [] + )), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { snapshot(records: [editedRecord]) } + ) + let payload = AutomationEditPayload( + label: newLabel, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + ) + + let result = await manager.perform( + .edit(payload), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .succeeded) + } + + func testPartialSuccessorManifestReplacementPreservesEvidenceThroughSourceRollback() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let newLabel = "com.example.partial-successor" + let editedData = validLaunchAgentData(label: newLabel) + let editedChecksum = sha256(editedData) + let editedRecord = copyRecord( + Fixtures.userAgent, + id: AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: newLabel, + sourcePath: sourceURL.path + ), + label: newLabel, + sourceURL: sourceURL, + sourceChecksum: editedChecksum + ) + let disabledRecord = copyRecord( + Fixtures.userAgent, + sourceChecksum: sha256(sourceData), + enabledState: .disabled, + state: .disabled + ) + let manifestURL = deterministicFirstManifestURL() + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + partialReplaceAfterCommitOccurrences: [manifestURL: [2]] + ) + let preState = AutomationPreTransactionState( + sourceExisted: true, + enabledState: Fixtures.userAgent.enabledState, + loadState: Fixtures.userAgent.loadState, + linkedProcesses: [] + ) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(editedChecksum)], + evidence: [] + )), + ] + Array(repeating: .success(AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled], + evidence: [] + )), count: 20)) + let refreshCount = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { + switch refreshCount.incrementAndGet() { + case 1: snapshot(records: [editedRecord]) + case 2: snapshot(records: [Fixtures.userAgent]) + default: snapshot(records: [disabledRecord]) + } + } + ) + let result = await manager.perform( + .edit(AutomationEditPayload( + label: newLabel, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + )), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .partialFailure( + "HIGH SEVERITY: the filesystem reported a partial mutation; automatic rollback restored the prior state." + ) + ) + XCTAssertEqual(result.rollback, .restored(try! XCTUnwrap(result.backup?.id))) + XCTAssertEqual(result.fileMutationEvidence?.kind, .replace) + XCTAssertNotNil(result.fileMutationEvidence?.recoveryHandle) + XCTAssertNil(result.rollbackFileMutationEvidence) + XCTAssertEqual(refreshCount.current(), 2) + XCTAssertEqual(fileSystem.storedData(at: sourceURL), sourceData) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 1) + let restorationStates = await executor.recordedRestorationStates() + XCTAssertEqual(restorationStates, [preState]) + + let restoredBackupID = try! XCTUnwrap(result.backup?.id) + let restored = await manager.restorationManifests() + XCTAssertTrue(restored.contains { $0.id == restoredBackupID }) + await assertLiveRecoveryInventoryMatchesRestartAfterRetentionChurn( + manager: manager, + fileSystem: fileSystem, + expectedChecksum: sha256(sourceData) + ) + } + + func testSuccessorCompensationRetentionSurvivesLaterCleanupPartial() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let sourceChecksum = sha256(sourceData) + let newLabel = "com.example.successor-cleanup-partial" + let editedData = validLaunchAgentData(label: newLabel) + let editedChecksum = sha256(editedData) + let editedRecord = copyRecord( + Fixtures.userAgent, + id: AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: newLabel, + sourcePath: sourceURL.path + ), + label: newLabel, + sourceURL: sourceURL, + sourceChecksum: editedChecksum + ) + let disabledRecord = copyRecord( + Fixtures.userAgent, + sourceChecksum: sourceChecksum, + enabledState: .disabled, + state: .disabled + ) + let manifestURL = deterministicFirstManifestURL() + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + partialReplaceAfterCommitOccurrences: [manifestURL: [2]], + partialReplaceRetainsStagedOccurrences: [manifestURL: [2]], + failStagedRemoves: true + ) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(editedChecksum)], + evidence: [] + )), + ] + Array(repeating: .success(AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled], + evidence: [] + )), count: 20)) + let refreshCount = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { + switch refreshCount.incrementAndGet() { + case 1: snapshot(records: [editedRecord]) + case 2: snapshot(records: [Fixtures.userAgent]) + default: snapshot(records: [disabledRecord]) + } + } + ) + + let result = await manager.perform( + .edit(AutomationEditPayload( + label: newLabel, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + )), + record: Fixtures.userAgent, + expectedChecksum: sourceChecksum, + linkedProcesses: [] + ) + + guard case .partialFailure = result.status else { + return XCTFail("Expected partial failure, got \(result.status)") + } + let evidence = try! XCTUnwrap(result.fileMutationEvidence) + let stagedRecovery = try! XCTUnwrap(evidence.recoveryHandles.first { + $0.fileURL.lastPathComponent.hasSuffix(".tmp") + }) + XCTAssertTrue(fileSystem.itemExists(at: stagedRecovery.fileURL)) + let restoredBackupID = try! XCTUnwrap(result.backup?.id) + let restored = await manager.restorationManifests() + XCTAssertTrue(restored.contains { $0.id == restoredBackupID }) + + await assertLiveRecoveryInventoryMatchesRestartAfterRetentionChurn( + manager: manager, + fileSystem: fileSystem, + expectedChecksum: sourceChecksum + ) + } + + private func assertLiveRecoveryInventoryMatchesRestartAfterRetentionChurn( + manager: AutomationManager, + fileSystem: InMemoryAutomationFileSystem, + expectedChecksum: String + ) async { + for _ in 0..<20 { + let succeeded = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: expectedChecksum, + linkedProcesses: [] + ) + XCTAssertEqual(succeeded.status, .succeeded) + } + let live = await manager.restorationManifests() + let restarted = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + let reloaded = await restarted.restorationManifests() + XCTAssertEqual(live, reloaded) + XCTAssertEqual(live.count, 20) + } + + func testSuccessorRefusesFreshManifestOccupantAndReportsItAsObservationOnly() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let newLabel = "com.example.fresh-successor-collision" + let editedData = validLaunchAgentData(label: newLabel) + let editedChecksum = sha256(editedData) + let editedRecord = copyRecord( + Fixtures.userAgent, + id: AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: newLabel, + sourcePath: sourceURL.path + ), + label: newLabel, + sourceURL: sourceURL, + sourceChecksum: editedChecksum + ) + let manifestURL = deterministicFirstManifestURL() + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let preState = AutomationPreTransactionState( + sourceExisted: true, + enabledState: Fixtures.userAgent.enabledState, + loadState: Fixtures.userAgent.loadState, + linkedProcesses: [] + ) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(editedChecksum)], evidence: [] + )), + .success(AutomationExecutorResult( + postconditions: [.preTransactionStateRestored(preState)], evidence: [] + )), + ]) + let refreshCount = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { + let call = refreshCount.incrementAndGet() + if call == 1 { + let validPriorManifest = try! fileSystem.read(manifestURL) + fileSystem.setStoredData(validPriorManifest, at: manifestURL) + fileSystem.setMetadata(AutomationFileMetadata( + canonicalURL: manifestURL, + ownerUID: 501, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 9_999), + resourceIdentifier: "fixture:fresh-manifest-occupant", + permissions: 0o600 + ), for: manifestURL) + } + return call == 1 + ? snapshot(records: [editedRecord]) + : snapshot(records: [Fixtures.userAgent]) + } + ) + + let result = await manager.perform( + .edit(AutomationEditPayload( + label: newLabel, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + )), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + try! fileSystem.metadata(for: manifestURL).resourceIdentifier, + "fixture:fresh-manifest-occupant" + ) + let evidence = try! XCTUnwrap(result.fileMutationEvidence) + XCTAssertTrue(evidence.observedFiles.contains { $0.fileURL == manifestURL }) + XCTAssertFalse(evidence.recoveryHandles.contains { $0.fileURL == manifestURL }) + XCTAssertEqual(result.rollback, .restored(try! XCTUnwrap(result.backup?.id))) + let live = await manager.restorationManifests() + let restarted = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + let reloaded = await restarted.restorationManifests() + XCTAssertTrue(live.isEmpty) + XCTAssertEqual(reloaded.count, 1) + XCTAssertEqual(reloaded.first?.id, result.backup?.id) + XCTAssertEqual(fileSystem.recordedOperations.filter { operation in + guard case .replace(let destination, _) = operation else { return false } + return destination == manifestURL + }.count, 1) + } + + func testSuccessorRefusesMissingManifestWithoutRecreatingItFromAmbientState() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let newLabel = "com.example.missing-successor-manifest" + let editedData = validLaunchAgentData(label: newLabel) + let editedChecksum = sha256(editedData) + let editedRecord = copyRecord( + Fixtures.userAgent, + id: AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: newLabel, + sourcePath: sourceURL.path + ), + label: newLabel, + sourceURL: sourceURL, + sourceChecksum: editedChecksum + ) + let manifestURL = deterministicFirstManifestURL() + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let preState = AutomationPreTransactionState( + sourceExisted: true, + enabledState: Fixtures.userAgent.enabledState, + loadState: Fixtures.userAgent.loadState, + linkedProcesses: [] + ) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(editedChecksum)], evidence: [] + )), + .success(AutomationExecutorResult( + postconditions: [.preTransactionStateRestored(preState)], evidence: [] + )), + ]) + let refreshCount = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { + let call = refreshCount.incrementAndGet() + if call == 1 { + let rootURL = manifestURL.deletingLastPathComponent() + let root = try! XCTUnwrap(AutomationDirectoryAuthorization( + directoryURL: rootURL, + resourceIdentifier: try! fileSystem.metadata(for: rootURL).resourceIdentifier + )) + let manifest = try! XCTUnwrap(AutomationFileAuthorization( + fileURL: manifestURL, + directory: root, + expectation: .existing(resourceIdentifier: try! XCTUnwrap( + try! fileSystem.metadata(for: manifestURL).resourceIdentifier + )) + )) + _ = try! fileSystem.removeItem(manifest) + } + return call == 1 ? snapshot(records: [editedRecord]) : snapshot(records: [Fixtures.userAgent]) + } + ) + + let result = await manager.perform( + .edit(AutomationEditPayload( + label: newLabel, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + )), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertFalse(fileSystem.itemExists(at: manifestURL)) + let evidence = try! XCTUnwrap(result.fileMutationEvidence) + XCTAssertFalse(evidence.observedFiles.contains { $0.fileURL == manifestURL }) + XCTAssertFalse(evidence.recoveryHandles.contains { $0.fileURL == manifestURL }) + XCTAssertEqual(result.rollback, .restored(try! XCTUnwrap(result.backup?.id))) + let live = await manager.restorationManifests() + let restarted = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + let reloaded = await restarted.restorationManifests() + XCTAssertEqual(live, reloaded) + XCTAssertTrue(live.isEmpty) + } + + func testSuccessorDoesNotPromoteFreshValidManifestAndBackupPairDuringLiveReconcile() async { + await assertValidRecoveryAuthorityReplacementIsQuarantined(replaceRoot: false) + } + + func testSuccessorDoesNotPromoteRecoveryPairFromReplacedRootDuringLiveReconcile() async { + await assertValidRecoveryAuthorityReplacementIsQuarantined(replaceRoot: true) + } + + private func assertValidRecoveryAuthorityReplacementIsQuarantined( + replaceRoot: Bool + ) async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let newLabel = replaceRoot + ? "com.example.replaced-recovery-root" : "com.example.fresh-recovery-pair" + let editedData = validLaunchAgentData(label: newLabel) + let editedChecksum = sha256(editedData) + let editedRecord = copyRecord( + Fixtures.userAgent, + id: AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: newLabel, + sourcePath: sourceURL.path + ), + label: newLabel, + sourceURL: sourceURL, + sourceChecksum: editedChecksum + ) + let manifestURL = deterministicFirstManifestURL() + let backupURL = deterministicFirstBackupURL() + let recoveryRoot = manifestURL.deletingLastPathComponent() + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(editedChecksum)], evidence: [] + )), + ]) + let refreshCount = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { + let call = refreshCount.incrementAndGet() + if call == 1 { + if replaceRoot { + fileSystem.setMetadata(AutomationFileMetadata( + canonicalURL: recoveryRoot, + ownerUID: 501, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 9_999), + resourceIdentifier: "fixture:fresh-recovery-root", + permissions: 0o700 + ), for: recoveryRoot) + } else { + for (url, identity) in [ + (manifestURL, "fixture:fresh-valid-manifest"), + (backupURL, "fixture:fresh-valid-backup"), + ] { + let validBytes = try! fileSystem.read(url) + fileSystem.setStoredData(validBytes, at: url) + fileSystem.setMetadata(AutomationFileMetadata( + canonicalURL: url, + ownerUID: 501, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 9_999), + resourceIdentifier: identity, + permissions: 0o600 + ), for: url) + } + } + } + return call == 1 ? snapshot(records: [editedRecord]) : snapshot(records: [Fixtures.userAgent]) + } + ) + + let result = await manager.perform( + .edit(AutomationEditPayload( + label: newLabel, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + )), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + let evidence = try! XCTUnwrap(result.fileMutationEvidence) + XCTAssertTrue(evidence.observedFiles.contains { $0.fileURL == manifestURL }) + XCTAssertTrue(evidence.observedFiles.contains { $0.fileURL == backupURL }) + XCTAssertFalse(evidence.recoveryHandles.contains { $0.fileURL == manifestURL }) + XCTAssertFalse(evidence.recoveryHandles.contains { $0.fileURL == backupURL }) + XCTAssertEqual(fileSystem.storedData(at: backupURL), sourceData) + XCTAssertNotNil(try? PropertyListSerialization.propertyList( + from: try! XCTUnwrap(fileSystem.storedData(at: manifestURL)), + options: [], + format: nil + )) + XCTAssertEqual(fileSystem.recordedOperations.filter { operation in + guard case .replace(let destination, _) = operation else { return false } + return destination == manifestURL + }.count, 1) + XCTAssertEqual(fileSystem.recordedOperations.filter { operation in + guard case .replace(let destination, _) = operation else { return false } + return destination == backupURL + }.count, 1) + if replaceRoot { + XCTAssertEqual( + try! fileSystem.metadata(for: recoveryRoot).resourceIdentifier, + "fixture:fresh-recovery-root" + ) + } else { + XCTAssertEqual( + try! fileSystem.metadata(for: manifestURL).resourceIdentifier, + "fixture:fresh-valid-manifest" + ) + XCTAssertEqual( + try! fileSystem.metadata(for: backupURL).resourceIdentifier, + "fixture:fresh-valid-backup" + ) + } + let live = await manager.restorationManifests() + XCTAssertTrue(live.isEmpty) + let restarted = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + let reloaded = await restarted.restorationManifests() + XCTAssertEqual(reloaded.count, 1) + XCTAssertEqual(reloaded.first?.id, result.backup?.id) + } + + func testSuccessorRevalidatesBackupAfterManifestCommitBeforePublishingMaps() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let newLabel = "com.example.backup-toctou" + let editedData = validLaunchAgentData(label: newLabel) + let editedChecksum = sha256(editedData) + let editedRecord = copyRecord( + Fixtures.userAgent, + id: AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: newLabel, + sourcePath: sourceURL.path + ), + label: newLabel, + sourceURL: sourceURL, + sourceChecksum: editedChecksum + ) + let backupURL = deterministicFirstBackupURL() + let manifestURL = deterministicFirstManifestURL() + let attackerData = Data("attacker-backup-after-final-preflight".utf8) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let backupReads = LockedCallCounter() + fileSystem.setReadObserver { url, _ in + guard url == backupURL, backupReads.incrementAndGet() == 3 else { return } + fileSystem.setStoredData(attackerData, at: backupURL) + fileSystem.setMetadata(AutomationFileMetadata( + canonicalURL: backupURL, + ownerUID: 501, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 20_000), + resourceIdentifier: "fixture:attacker-backup-after-preflight", + permissions: 0o600 + ), for: backupURL) + } + let preState = AutomationPreTransactionState( + sourceExisted: true, + enabledState: Fixtures.userAgent.enabledState, + loadState: Fixtures.userAgent.loadState, + linkedProcesses: [] + ) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(editedChecksum)], evidence: [] + )), + .success(AutomationExecutorResult( + postconditions: [.preTransactionStateRestored(preState)], evidence: [] + )), + ]) + let refreshes = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { + refreshes.incrementAndGet() == 1 + ? snapshot(records: [editedRecord]) : snapshot(records: [Fixtures.userAgent]) + } + ) + + let result = await manager.perform( + .edit(AutomationEditPayload( + label: newLabel, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + )), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertNotEqual(result.status, .succeeded) + XCTAssertEqual(fileSystem.storedData(at: backupURL), attackerData) + XCTAssertFalse(fileSystem.recordedOperations.contains { operation in + guard case .remove(let removedURL) = operation else { return false } + return removedURL == backupURL + }) + let evidence = result.fileMutationEvidence + XCTAssertNotNil(evidence) + XCTAssertTrue(evidence?.observedFiles.contains { $0.fileURL == backupURL } == true) + XCTAssertFalse(evidence?.recoveryHandles.contains { $0.fileURL == backupURL } == true) + let restoredManifest = try! XCTUnwrap(fileSystem.storedData(at: manifestURL)) + let plist = try! XCTUnwrap( + PropertyListSerialization.propertyList(from: restoredManifest, format: nil) + as? [String: Any] + ) + XCTAssertEqual( + plist["authorizedRecordIDs"] as? [String], + [Fixtures.userAgent.id.rawValue] + ) + let live = await manager.restorationManifests() + let restarted = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + let reloaded = await restarted.restorationManifests() + XCTAssertEqual(live, reloaded) + XCTAssertTrue(live.isEmpty) + } + + func testSuccessorRechecksBackupIdentityAfterFinalContentVerification() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let newLabel = "com.example.backup-identity-toctou" + let editedData = validLaunchAgentData(label: newLabel) + let editedChecksum = sha256(editedData) + let editedRecord = copyRecord( + Fixtures.userAgent, + id: AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: newLabel, + sourcePath: sourceURL.path + ), + label: newLabel, + sourceURL: sourceURL, + sourceChecksum: editedChecksum + ) + let backupURL = deterministicFirstBackupURL() + let manifestURL = deterministicFirstManifestURL() + let replacementIdentity = "fixture:same-byte-backup-after-final-read" + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let backupReads = LockedCallCounter() + fileSystem.setReadObserver { url, _ in + guard url == backupURL, backupReads.incrementAndGet() == 4 else { return } + fileSystem.setStoredData(sourceData, at: backupURL) + fileSystem.setMetadata(AutomationFileMetadata( + canonicalURL: backupURL, + ownerUID: 501, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 30_000), + resourceIdentifier: replacementIdentity, + permissions: 0o600 + ), for: backupURL) + } + let preState = AutomationPreTransactionState( + sourceExisted: true, + enabledState: Fixtures.userAgent.enabledState, + loadState: Fixtures.userAgent.loadState, + linkedProcesses: [] + ) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(editedChecksum)], evidence: [] + )), + .success(AutomationExecutorResult( + postconditions: [.preTransactionStateRestored(preState)], evidence: [] + )), + ]) + let refreshes = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { + refreshes.incrementAndGet() == 1 + ? snapshot(records: [editedRecord]) : snapshot(records: [Fixtures.userAgent]) + } + ) + + let result = await manager.perform( + .edit(AutomationEditPayload( + label: newLabel, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + )), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertNotEqual(result.status, .succeeded) + XCTAssertEqual(fileSystem.storedData(at: backupURL), sourceData) + XCTAssertEqual( + try! fileSystem.metadata(for: backupURL).resourceIdentifier, + replacementIdentity + ) + XCTAssertFalse(fileSystem.recordedOperations.contains { operation in + guard case .remove(let removedURL) = operation else { return false } + return removedURL == backupURL + }) + let evidence = result.fileMutationEvidence + XCTAssertNotNil(evidence) + XCTAssertTrue(evidence?.observedFiles.contains { $0.fileURL == backupURL } == true) + XCTAssertFalse(evidence?.recoveryHandles.contains { $0.fileURL == backupURL } == true) + let restoredManifest = try! XCTUnwrap(fileSystem.storedData(at: manifestURL)) + let plist = try! XCTUnwrap( + PropertyListSerialization.propertyList(from: restoredManifest, format: nil) + as? [String: Any] + ) + XCTAssertEqual( + plist["authorizedRecordIDs"] as? [String], + [Fixtures.userAgent.id.rawValue] + ) + let live = await manager.restorationManifests() + XCTAssertTrue(live.isEmpty) + let restarted = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + let reloaded = await restarted.restorationManifests() + XCTAssertEqual(reloaded.count, 1) + XCTAssertEqual(reloaded.first?.id, result.backup?.id) + } + + func testSuccessorRechecksInstalledManifestIdentityAfterFinalRead() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let newLabel = "com.example.manifest-identity-toctou" + let editedData = validLaunchAgentData(label: newLabel) + let editedChecksum = sha256(editedData) + let editedRecord = copyRecord( + Fixtures.userAgent, + id: AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: newLabel, + sourcePath: sourceURL.path + ), + label: newLabel, + sourceURL: sourceURL, + sourceChecksum: editedChecksum + ) + let manifestURL = deterministicFirstManifestURL() + let replacementIdentity = "fixture:same-byte-manifest-after-final-read" + let replacementBytes = LockedDataCapture() + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let manifestReads = LockedCallCounter() + fileSystem.setReadObserver { url, data in + guard url == manifestURL, manifestReads.incrementAndGet() == 3 else { return } + replacementBytes.set(data) + fileSystem.setStoredData(data, at: manifestURL) + fileSystem.setMetadata(AutomationFileMetadata( + canonicalURL: manifestURL, + ownerUID: 501, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 40_000), + resourceIdentifier: replacementIdentity, + permissions: 0o600 + ), for: manifestURL) + } + let preState = AutomationPreTransactionState( + sourceExisted: true, + enabledState: Fixtures.userAgent.enabledState, + loadState: Fixtures.userAgent.loadState, + linkedProcesses: [] + ) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(editedChecksum)], evidence: [] + )), + .success(AutomationExecutorResult( + postconditions: [.preTransactionStateRestored(preState)], evidence: [] + )), + ]) + let refreshes = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { + refreshes.incrementAndGet() == 1 + ? snapshot(records: [editedRecord]) : snapshot(records: [Fixtures.userAgent]) + } + ) + + let result = await manager.perform( + .edit(AutomationEditPayload( + label: newLabel, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + )), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertNotEqual(result.status, .succeeded) + XCTAssertEqual(fileSystem.storedData(at: manifestURL), replacementBytes.current()) + XCTAssertEqual( + try! fileSystem.metadata(for: manifestURL).resourceIdentifier, + replacementIdentity + ) + XCTAssertFalse(fileSystem.recordedOperations.contains { operation in + guard case .remove(let removedURL) = operation else { return false } + return removedURL == manifestURL + }) + let evidence = result.fileMutationEvidence + XCTAssertNotNil(evidence) + XCTAssertTrue(evidence?.observedFiles.contains { $0.fileURL == manifestURL } == true) + XCTAssertFalse(evidence?.recoveryHandles.contains { $0.fileURL == manifestURL } == true) + let plist = try! XCTUnwrap( + PropertyListSerialization.propertyList( + from: try! XCTUnwrap(fileSystem.storedData(at: manifestURL)), + format: nil + ) as? [String: Any] + ) + XCTAssertEqual( + plist["authorizedRecordIDs"] as? [String], + [Fixtures.userAgent.id.rawValue, editedRecord.id.rawValue].sorted() + ) + let live = await manager.restorationManifests() + XCTAssertTrue(live.isEmpty) + let restarted = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + let reloaded = await restarted.restorationManifests() + XCTAssertEqual(reloaded.count, 1) + XCTAssertEqual(reloaded.first?.id, result.backup?.id) + } + + func testSuccessorManifestPostverificationFailureRestoresPriorManifest() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let newLabel = "com.example.corrupt-successor" + let editedData = validLaunchAgentData(label: newLabel) + let editedChecksum = sha256(editedData) + let editedRecord = copyRecord( + Fixtures.userAgent, + id: AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: newLabel, + sourcePath: sourceURL.path + ), + label: newLabel, + sourceURL: sourceURL, + sourceChecksum: editedChecksum + ) + let disabledRecord = copyRecord( + Fixtures.userAgent, + sourceChecksum: sha256(sourceData), + enabledState: .disabled, + state: .disabled + ) + let manifestURL = deterministicFirstManifestURL() + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + dataAfterReplaceOccurrences: [manifestURL: [2: Data("corrupt".utf8)]] + ) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(editedChecksum)], evidence: [] + )), + ] + Array(repeating: .success(AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled], + evidence: [] + )), count: 20)) + let refreshCount = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { + switch refreshCount.incrementAndGet() { + case 1: snapshot(records: [editedRecord]) + case 2: snapshot(records: [Fixtures.userAgent]) + default: snapshot(records: [disabledRecord]) + } + } + ) + let result = await manager.perform( + .edit(AutomationEditPayload( + label: newLabel, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + )), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + guard case .partialFailure = result.status else { + return XCTFail("Expected partial failure, got \(result.status)") + } + XCTAssertEqual(result.rollback, .restored(try! XCTUnwrap(result.backup?.id))) + XCTAssertEqual(result.fileMutationEvidence?.kind, .replace) + XCTAssertNil(result.rollbackFileMutationEvidence) + XCTAssertEqual(refreshCount.current(), 2) + XCTAssertEqual(fileSystem.storedData(at: sourceURL), sourceData) + let restoredBackupID = try! XCTUnwrap(result.backup?.id) + let restored = await manager.restorationManifests() + XCTAssertTrue(restored.contains { $0.id == restoredBackupID }) + await assertLiveRecoveryInventoryMatchesRestartAfterRetentionChurn( + manager: manager, + fileSystem: fileSystem, + expectedChecksum: sha256(sourceData) + ) + } + + func testSuccessorCompensationAndSourceRollbackPartialsRemainSeparatelyVisible() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let newLabel = "com.example.aggregate-successor" + let editedData = validLaunchAgentData(label: newLabel) + let editedChecksum = sha256(editedData) + let editedRecord = copyRecord( + Fixtures.userAgent, + id: AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: newLabel, + sourcePath: sourceURL.path + ), + label: newLabel, + sourceURL: sourceURL, + sourceChecksum: editedChecksum + ) + let manifestURL = deterministicFirstManifestURL() + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + dataAfterReplaceOccurrences: [manifestURL: [2: Data("corrupt".utf8)]], + partialReplaceAfterCommitOccurrences: [ + manifestURL: [3], + sourceURL: [2], + ] + ) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(editedChecksum)], evidence: [] + )), + ]) + let refreshCount = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { + _ = refreshCount.incrementAndGet() + return snapshot(records: [editedRecord]) + } + ) + let result = await manager.perform( + .edit(AutomationEditPayload( + label: newLabel, + executable: "/bin/sleep", + arguments: ["14400"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + )), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .partialFailure( + "HIGH SEVERITY: the operation failed and automatic rollback reached a partial filesystem state." + ) + ) + XCTAssertEqual(result.rollback, .failed("Automatic rollback did not complete.")) + let manifestEvidence = try! XCTUnwrap(result.fileMutationEvidence) + let rollbackEvidence = try! XCTUnwrap(result.rollbackFileMutationEvidence) + XCTAssertEqual(manifestEvidence.resultURL, manifestURL) + XCTAssertGreaterThanOrEqual(manifestEvidence.recoveryHandles.count, 1) + XCTAssertEqual(rollbackEvidence.resultURL, sourceURL) + XCTAssertGreaterThanOrEqual(rollbackEvidence.recoveryHandles.count, 1) + XCTAssertEqual(refreshCount.current(), 2) + for evidence in [manifestEvidence, rollbackEvidence] { + for recovery in evidence.recoveryHandles { + XCTAssertTrue(fileSystem.itemExists(at: recovery.fileURL)) + guard case .existing(let expectedIdentity) = recovery.expectation else { + return XCTFail("Recovery handle must bind an existing exact entry.") + } + XCTAssertEqual( + try! fileSystem.metadata(for: recovery.fileURL).resourceIdentifier, + expectedIdentity + ) + } + } + let restarted = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + let manifests = await restarted.restorationManifests() + XCTAssertEqual(manifests.first?.authorizedRecordIDs, [Fixtures.userAgent.id]) + } + + func testExternalEditDuringExecutorSuspensionIsNeverOverwrittenByRollback() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let editedData = validLaunchAgentData(arguments: ["7200"]) + let externalData = validLaunchAgentData(arguments: ["3600"]) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor( + results: [.failure(.applyFailed)], + onApply: { fileSystem.setStoredData(externalData, at: sourceURL) } + ) + let manager = makeManager(fileSystem: fileSystem, executor: executor) + let payload = AutomationEditPayload( + label: Fixtures.userAgent.label, + executable: "/bin/sleep", + arguments: ["7200"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + ) + + let result = await manager.perform( + .edit(payload), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .partialFailure("HIGH SEVERITY: the operation failed and automatic rollback did not complete.") + ) + XCTAssertEqual(fileSystem.storedData(at: sourceURL), externalData) + } + + func testRollbackRefusesWhenDestinationAuthorizationChangesDuringExecutorAwait() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let editedData = validLaunchAgentData(arguments: ["7200"]) + let authorizationRevoked = LockedFlag() + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor( + results: [.failure(.applyFailed)], + onApply: { authorizationRevoked.set() } + ) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + destinationContext: { _, _ in + .fixture( + currentUID: 501, + canonicalPathIsApproved: !authorizationRevoked.value, + ownerUID: 501 + ) + } + ) + + let result = await manager.perform( + .edit(AutomationEditPayload( + label: Fixtures.userAgent.label, + executable: "/bin/sleep", + arguments: ["7200"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + )), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .partialFailure("HIGH SEVERITY: the operation failed and automatic rollback did not complete.") + ) + XCTAssertEqual(fileSystem.storedData(at: sourceURL), editedData) + } + + func testStopAndCompoundStopRequireRefreshedBirthAndRelaunchProof() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let originalBirth = ProcessBirthToken(seconds: 1_000, microseconds: 1) + let relaunchedBirth = ProcessBirthToken(seconds: 2_000, microseconds: 2) + let controlled = classifiedAutomationProcess(pid: 700, birth: originalBirth) + let relaunched = classifiedAutomationProcess(pid: 701, birth: relaunchedBirth) + + for operation in [AutomationOperation.stopCurrentRun, .disableAndStop] { + let refreshedRecord = operation == .disableAndStop + ? copyRecord(Fixtures.userAgent, enabledState: .disabled, state: .disabled) + : Fixtures.userAgent + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: operation == .disableAndStop + ? [.futureLaunchesDisabled, .noLinkedProcess] + : [.noLinkedProcess], + evidence: [] + )), + ]), + refresh: { snapshot(records: [refreshedRecord]) }, + refreshProcesses: { [relaunched.process] } + ) + + let result = await manager.perform( + operation, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [controlled] + ) + XCTAssertNotEqual(result.status, .succeeded) + XCTAssertTrue(result.verificationEvidence.contains { + $0.contains("controlled or relaunched process") + }) + } + } + + func testStopRejectsStrongBirthlessRelaunchCandidate() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let controlled = classifiedAutomationProcess( + pid: 700, + birth: ProcessBirthToken(seconds: 1_000, microseconds: 1) + ) + let birthless = DevProcess( + pid: 701, + parentPID: 1, + executable: "/bin/sleep", + command: "/bin/sleep 14400", + argumentVector: ["/bin/sleep", "14400"], + birthToken: nil, + launchLabel: Fixtures.userAgent.label + ) + let manager = makeManager( + fileSystem: InMemoryAutomationFileSystem(files: [sourceURL: sourceData]), + executor: RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult(postconditions: [.noLinkedProcess], evidence: [])), + ]), + refreshProcesses: { [birthless] } + ) + + let result = await manager.perform( + .stopCurrentRun, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [controlled] + ) + + XCTAssertNotEqual(result.status, .succeeded) + XCTAssertTrue(result.verificationEvidence.contains { $0.contains("relaunched process") }) + } + + func testStopFailsClosedWhenLiveProcessVerificationIsUnavailable() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let controlled = classifiedAutomationProcess( + pid: 700, + birth: ProcessBirthToken(seconds: 1_000, microseconds: 1) + ) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.noLinkedProcess], + evidence: [] + )), + ]), + refreshProcesses: { + throw AutomationManagerConfigurationError.runtimeProcessVerificationUnavailable + } + ) + + let result = await manager.perform( + .stopCurrentRun, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [controlled] + ) + + XCTAssertNotEqual(result.status, .succeeded) + XCTAssertTrue(result.verificationEvidence.contains { + $0.contains("Live process verification was unavailable") + }) + } + + func testDurableManifestReloadsInANewManager() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let disabledRecord = copyRecord( + Fixtures.userAgent, + sourceChecksum: checksum, + enabledState: .disabled, + state: .disabled + ) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let success = AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled], + evidence: [] + ) + let first = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor(results: [.success(success)]), + refresh: { snapshot(records: [disabledRecord]) } + ) + let result = await first.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + let second = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + + let manifests = await second.restorationManifests() + XCTAssertEqual(manifests.map(\.id), [try! XCTUnwrap(result.backup?.id)]) + XCTAssertEqual(manifests.first?.backupURL.path, "/redacted") + } + + func testSourceLessCronCanUseRecoverableTransactionIdentity() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let data = Data("0 9 * * 1 /bin/sleep 1\n".utf8) + let checksum = sha256(data) + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: data]) + let metadata = try! fileSystem.metadata(for: transactionURL) + let cron = AutomationRecord( + id: AutomationRecord.ID( + source: .crontab, + ownerUID: 501, + label: "cron-entry", + sourcePath: "/.devscope/current-user-crontab/test" + ), + kind: .cron, + sourceKind: .crontab, + label: "Cron entry 1", + displayName: "Cron entry 1", + providerBundleIdentifier: nil, + ownerUID: 501, + ownership: .user, + executable: nil, + arguments: [], + commandSignature: "/bin/sleep 1", + environment: [:], + workingDirectory: nil, + schedule: AutomationSchedule(triggers: [.cron("0 9 * * 1")], summary: "Cron"), + sourceURL: nil, + sourceChecksum: checksum, + enabledState: .enabled, + loadState: .unknown, + approvalState: .notApplicable, + state: .idle, + evidence: [], + capabilities: [.exportRecord], + validationFindings: [] + ) + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult(postconditions: [.targetResolved], evidence: [])), + ]), + context: .fixture( + currentUID: 501, + canonicalPathIsApproved: true, + ownerUID: 501, + implementedCapabilities: [.exportRecord] + ), + recoverableSource: { _ in + AutomationRecoverableSource( + transactionURL: transactionURL, + data: data, + checksum: checksum, + metadata: metadata + ) + } + ) + + let result = await manager.perform( + .exportRecord(redacted: true), + record: cron, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .succeeded) + } + + func testPartialRecoverableTransactionSourceIsNeverDowngradedToPreflightRejection() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/partial-crontab") + let data = Data("0 9 * * 1 /bin/sleep 1\n".utf8) + let checksum = sha256(data) + let entry = try! XCTUnwrap(CronParser.parse(String(decoding: data, as: UTF8.self)).entries.first) + let cron = cronRecord(entry: entry, checksum: checksum) + let fileSystem = InMemoryAutomationFileSystem( + directories: [transactionURL.deletingLastPathComponent()] + ) + let executor = RecordingAutomationMutationExecutor() + let refreshCount = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501), + recoverableSource: { _ in + fileSystem.setStoredData(data, at: transactionURL) + let directoryMetadata = try fileSystem.metadata( + for: transactionURL.deletingLastPathComponent() + ) + let directory = try XCTUnwrap(AutomationDirectoryAuthorization( + directoryURL: transactionURL.deletingLastPathComponent(), + resourceIdentifier: directoryMetadata.resourceIdentifier + )) + let metadata = try fileSystem.metadata(for: transactionURL) + let recovery = try XCTUnwrap(AutomationFileAuthorization( + fileURL: transactionURL, + directory: directory, + expectation: .existing(resourceIdentifier: try XCTUnwrap(metadata.resourceIdentifier)) + )) + throw AutomationFilePartialMutation( + kind: .replace, + commitState: .committed, + observedFiles: [recovery], + recoveryHandle: recovery, + resultURL: transactionURL + ) + }, + refresh: { + _ = refreshCount.incrementAndGet() + return Fixtures.inventoryGeneration7 + } + ) + + let result = await manager.perform( + .disable, + record: cron, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .partialFailure("HIGH SEVERITY: recoverable source materialization reached a partial filesystem state.") + ) + XCTAssertEqual(result.fileMutationEvidence?.recoveryHandle?.fileURL, transactionURL) + XCTAssertEqual(result.fileMutationEvidence?.commitState, .committed) + XCTAssertEqual(refreshCount.current(), 1) + XCTAssertEqual(fileSystem.storedData(at: transactionURL), data) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testSecondRecoverableSourcePartialPreservesEvidenceRefreshesAndSkipsExecutor() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/second-partial-crontab") + let initial = Data("0 9 * * 1 /bin/sleep 1\n".utf8) + let materialized = Data("0 10 * * 1 /bin/sleep 2\n".utf8) + let checksum = sha256(initial) + let entry = try! XCTUnwrap( + CronParser.parse(String(decoding: initial, as: UTF8.self)).entries.first + ) + let cron = cronRecord(entry: entry, checksum: checksum) + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: initial]) + let executor = RecordingAutomationMutationExecutor() + let sourceCalls = LockedCallCounter() + let refreshCount = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501), + recoverableSource: { _ in + if sourceCalls.incrementAndGet() == 1 { + return AutomationRecoverableSource( + transactionURL: transactionURL, + data: initial, + checksum: checksum, + metadata: try fileSystem.metadata(for: transactionURL) + ) + } + fileSystem.setStoredData(materialized, at: transactionURL) + let directoryMetadata = try fileSystem.metadata( + for: transactionURL.deletingLastPathComponent() + ) + let directory = try XCTUnwrap(AutomationDirectoryAuthorization( + directoryURL: transactionURL.deletingLastPathComponent(), + resourceIdentifier: directoryMetadata.resourceIdentifier + )) + let metadata = try fileSystem.metadata(for: transactionURL) + let recovery = try XCTUnwrap(AutomationFileAuthorization( + fileURL: transactionURL, + directory: directory, + expectation: .existing(resourceIdentifier: try XCTUnwrap(metadata.resourceIdentifier)) + )) + throw AutomationFilePartialMutation( + kind: .replace, + commitState: .committed, + observedFiles: [recovery], + recoveryHandle: recovery, + resultURL: transactionURL + ) + }, + refresh: { + _ = refreshCount.incrementAndGet() + return Fixtures.inventoryGeneration7 + } + ) + + let result = await manager.perform( + .disable, + record: cron, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .partialFailure( + "HIGH SEVERITY: recoverable source materialization reached a partial filesystem state." + ) + ) + XCTAssertNotNil(result.backup) + XCTAssertEqual(result.fileMutationEvidence?.recoveryHandle?.fileURL, transactionURL) + XCTAssertEqual(result.fileMutationEvidence?.commitState, .committed) + XCTAssertEqual(sourceCalls.current(), 2) + XCTAssertEqual(refreshCount.current(), 1) + XCTAssertEqual(fileSystem.storedData(at: transactionURL), materialized) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testRecoverableSourceRevalidationRejectsAdapterSnapshotThatDiffersFromDisk() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let data = Data("0 9 * * 1 /bin/sleep 1\n".utf8) + let changed = Data("0 10 * * 1 /bin/sleep 2\n".utf8) + let checksum = sha256(data) + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: data]) + let metadata = try! fileSystem.metadata(for: transactionURL) + let calls = LockedCallCounter() + let cron = cronRecord(entry: try! XCTUnwrap(CronParser.parse(String(decoding: data, as: UTF8.self)).entries.first), checksum: checksum) + let executor = RecordingAutomationMutationExecutor() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501), + recoverableSource: { _ in + if calls.incrementAndGet() == 2 { fileSystem.setStoredData(changed, at: transactionURL) } + return AutomationRecoverableSource( + transactionURL: transactionURL, + data: data, + checksum: checksum, + metadata: metadata + ) + } + ) + + let result = await manager.perform( + .disable, + record: cron, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .failed("The recoverable source identity changed immediately before mutation.") + ) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testCronEditBindsRawDocumentToReviewedFieldsAndImportVerifiesStableEntryIdentity() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let data = Data("SHELL=/bin/sh\n0 9 * * 1 /usr/bin/echo hello\n".utf8) + let checksum = sha256(data) + let actualSnapshot = await CronAutomationSource( + commandRunner: RecordingAutomationCommandRunner(result: .success(AutomationCommandResult( + status: 0, + standardOutput: data, + standardError: Data() + ))), + currentUID: 501, + currentUsername: "test-user" + ).snapshot() + let cron = try! XCTUnwrap(actualSnapshot.records.first) + XCTAssertEqual(cron.sourceChecksum, checksum) + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: data]) + let captured: @Sendable (AutomationRecord) async throws -> AutomationRecoverableSource = { _ in + AutomationRecoverableSource( + transactionURL: transactionURL, + data: try fileSystem.read(transactionURL), + checksum: sha256(try fileSystem.read(transactionURL)), + metadata: try fileSystem.metadata(for: transactionURL) + ) + } + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(checksum)], evidence: [] + )), + ]), + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501), + recoverableSource: captured, + refresh: { snapshot(records: [cron]) } + ) + + let mismatched = await manager.perform( + .edit(AutomationEditPayload( + label: cron.label, + executable: "/usr/bin/echo", + arguments: ["reviewed-other-command"], + environment: ["SHELL": "/bin/sh"], + workingDirectory: nil, + schedule: cron.schedule, + rawRepresentation: data + )), + record: cron, + expectedChecksum: checksum, + linkedProcesses: [] + ) + XCTAssertEqual( + mismatched.status, + .rejected("The rendered crontab does not match the reviewed command, schedule, and environment.") + ) + + let imported = await manager.perform( + .importRecord(AutomationImportPayload( + destination: transactionURL, + data: data, + expectedKind: .cron, + expectedDestinationChecksum: checksum + )), + record: cron, + expectedChecksum: checksum, + linkedProcesses: [] + ) + XCTAssertEqual(imported.status, .succeeded) + } + + func testCronEditRejectsAmbiguousIntendedEntryAndStaleRefreshedContent() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let original = Data("0 9 * * 1 /usr/bin/echo original\n".utf8) + let entry = try! XCTUnwrap(CronParser.parse(String(decoding: original, as: UTF8.self)).entries.first) + let record = cronRecord(entry: entry, checksum: sha256(original)) + let ambiguous = Data("0 9 * * 1 /usr/bin/echo changed\n0 9 * * 1 /usr/bin/echo changed\n".utf8) + let changed = Data("0 9 * * 1 /usr/bin/echo changed\n".utf8) + let staleRecord = cronRecord(entry: entry, checksum: sha256(changed)) + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: original]) + let captured: @Sendable (AutomationRecord) async throws -> AutomationRecoverableSource = { _ in + let data = try fileSystem.read(transactionURL) + return AutomationRecoverableSource( + transactionURL: transactionURL, + data: data, + checksum: sha256(data), + metadata: try fileSystem.metadata(for: transactionURL) + ) + } + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(sha256(changed))], + evidence: [] + )), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501), + recoverableSource: captured, + refresh: { snapshot(records: [staleRecord]) } + ) + let payload: (Data) -> AutomationEditPayload = { data in + AutomationEditPayload( + label: record.label, + executable: "/usr/bin/echo", + arguments: ["changed"], + environment: [:], + workingDirectory: nil, + schedule: record.schedule, + rawRepresentation: data + ) + } + + let rejected = await manager.perform( + .edit(payload(ambiguous)), + record: record, + expectedChecksum: sha256(original), + linkedProcesses: [] + ) + XCTAssertEqual( + rejected.status, + .rejected("The intended cron entry is missing or ambiguous in the rendered document.") + ) + + let stale = await manager.perform( + .edit(payload(changed)), + record: record, + expectedChecksum: sha256(original), + linkedProcesses: [] + ) + XCTAssertNotEqual(stale.status, .succeeded) + } + + func testCronEditRejectsAddOnlyDocumentThatKeepsSelectedEntry() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let original = Data("0 9 * * 1 /usr/bin/echo original\n".utf8) + let addOnly = Data("0 9 * * 1 /usr/bin/echo original\n0 10 * * 1 /usr/bin/echo changed\n".utf8) + let originalRecords = await realCronRecords(original) + let addOnlyRecords = await realCronRecords(addOnly) + let selected = try! XCTUnwrap(originalRecords.first) + let intended = try! XCTUnwrap(addOnlyRecords.last) + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: original]) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(sha256(addOnly))], + evidence: [] + )), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501), + recoverableSource: recoverableCronSource(transactionURL, fileSystem: fileSystem), + refresh: { snapshot(records: [intended]) } + ) + + let result = await manager.perform( + .edit(AutomationEditPayload( + label: selected.label, + executable: "/usr/bin/echo", + arguments: ["changed"], + environment: [:], + workingDirectory: nil, + schedule: AutomationSchedule(triggers: [.cron("0 10 * * 1")], summary: "reviewed"), + rawRepresentation: addOnly + )), + record: selected, + expectedChecksum: sha256(original), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .rejected("A cron edit must replace exactly the selected entry and preserve unrelated entries.") + ) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testCronEditRejectsChangingUnselectedIdenticalOccurrence() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let original = Data("0 9 * * 1 /usr/bin/echo same\n0 9 * * 1 /usr/bin/echo same\n".utf8) + let wrongOccurrence = Data("0 9 * * 1 /usr/bin/echo same\n0 9 * * 1 /usr/bin/echo changed\n".utf8) + let originalRecords = await realCronRecords(original) + let intendedRecords = await realCronRecords(wrongOccurrence) + let selected = try! XCTUnwrap(originalRecords.first) + let intended = try! XCTUnwrap(intendedRecords.last) + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: original]) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(sha256(wrongOccurrence))], + evidence: [] + )), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501), + recoverableSource: recoverableCronSource(transactionURL, fileSystem: fileSystem), + refresh: { snapshot(records: [intended]) } + ) + + let result = await manager.perform( + .edit(AutomationEditPayload( + label: selected.label, + executable: "/usr/bin/echo", + arguments: ["changed"], + environment: [:], + workingDirectory: nil, + schedule: selected.schedule, + rawRepresentation: wrongOccurrence + )), + record: selected, + expectedChecksum: sha256(original), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .rejected("The intended cron entry does not replace the selected occurrence.") + ) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testRealCronSourceDuplicateSucceedsAndFailedDuplicateRollsBack() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let original = Data("0 9 * * 1 /usr/bin/echo original\n".utf8) + let duplicated = Data("0 9 * * 1 /usr/bin/echo original\n0 10 * * 1 /usr/bin/echo copy\n".utf8) + let originalRecords = await realCronRecords(original) + let duplicatedRecords = await realCronRecords(duplicated) + let selected = try! XCTUnwrap(originalRecords.first) + let duplicatedRecord = try! XCTUnwrap(duplicatedRecords.last) + let payload = AutomationEditPayload( + label: selected.label, + executable: "/usr/bin/echo", + arguments: ["copy"], + environment: [:], + workingDirectory: nil, + schedule: AutomationSchedule(triggers: [.cron("0 10 * * 1")], summary: "reviewed"), + rawRepresentation: duplicated, + destination: transactionURL, + expectedDestinationChecksum: sha256(original) + ) + + let successFS = InMemoryAutomationFileSystem(files: [transactionURL: original]) + let success = makeManager( + fileSystem: successFS, + executor: RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(sha256(duplicated))], + evidence: [] + )), + ]), + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501), + recoverableSource: recoverableCronSource(transactionURL, fileSystem: successFS), + refresh: { snapshot(records: [duplicatedRecord]) } + ) + let succeeded = await success.perform( + .duplicate(payload), + record: selected, + expectedChecksum: sha256(original), + linkedProcesses: [] + ) + XCTAssertEqual(succeeded.status, .succeeded) + + let rollbackFS = InMemoryAutomationFileSystem(files: [transactionURL: original]) + let rollback = makeManager( + fileSystem: rollbackFS, + executor: RecordingAutomationMutationExecutor(results: [.failure(.applyFailed)]), + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501), + recoverableSource: recoverableCronSource(transactionURL, fileSystem: rollbackFS), + refresh: { snapshot(records: [selected]) } + ) + let failed = await rollback.perform( + .duplicate(payload), + record: selected, + expectedChecksum: sha256(original), + linkedProcesses: [] + ) + XCTAssertEqual(failed.rollback, .restored(try! XCTUnwrap(failed.backup?.id))) + XCTAssertEqual(rollbackFS.storedData(at: transactionURL), original) + } + + func testCronDuplicateRejectsSameSchedulePrependThatReusesSelectedID() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let original = Data("0 9 * * 1 /usr/bin/echo original\n".utf8) + let prepended = Data("0 9 * * 1 /usr/bin/echo copy\n0 9 * * 1 /usr/bin/echo original\n".utf8) + let originalRecords = await realCronRecords(original) + let selected = try! XCTUnwrap(originalRecords.first) + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: original]) + let executor = RecordingAutomationMutationExecutor() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501), + recoverableSource: recoverableCronSource(transactionURL, fileSystem: fileSystem) + ) + + let result = await manager.perform( + .duplicate(AutomationEditPayload( + label: selected.label, + executable: "/usr/bin/echo", + arguments: ["copy"], + environment: [:], + workingDirectory: nil, + schedule: selected.schedule, + rawRepresentation: prepended, + destination: transactionURL, + expectedDestinationChecksum: sha256(original) + )), + record: selected, + expectedChecksum: sha256(original), + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .rejected("The added cron entry identity is not distinct.")) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testRealCronSourcePresentRestoreSucceedsAndFailedRestoreRollsBack() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let original = Data("0 9 * * 1 /usr/bin/echo original\n".utf8) + let edited = Data("0 10 * * 1 /usr/bin/echo changed\n".utf8) + let originalRecords = await realCronRecords(original) + let editedRecords = await realCronRecords(edited) + let originalRecord = try! XCTUnwrap(originalRecords.first) + let editedRecord = try! XCTUnwrap(editedRecords.first) + let editPayload = AutomationEditPayload( + label: originalRecord.label, + executable: "/usr/bin/echo", + arguments: ["changed"], + environment: [:], + workingDirectory: nil, + schedule: editedRecord.schedule, + rawRepresentation: edited + ) + + for restoreSucceeds in [true, false] { + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: original]) + let refreshCount = LockedCallCounter() + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(sha256(edited))], + evidence: [] + )), + restoreSucceeds + ? .success(AutomationExecutorResult( + postconditions: [.sourceInstalled, .sourceChecksum(sha256(original))], + evidence: [] + )) + : .failure(.applyFailed), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + context: .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501), + recoverableSource: recoverableCronSource(transactionURL, fileSystem: fileSystem), + refresh: { + let call = refreshCount.incrementAndGet() + return restoreSucceeds && call > 1 + ? snapshot(records: [originalRecord]) + : snapshot(records: [editedRecord]) + } + ) + let edit = await manager.perform( + .edit(editPayload), + record: originalRecord, + expectedChecksum: sha256(original), + linkedProcesses: [] + ) + let backupID = try! XCTUnwrap(edit.backup?.id) + let restored = await manager.perform( + .restore(backupID), + record: editedRecord, + expectedChecksum: sha256(edited), + linkedProcesses: [] + ) + + if restoreSucceeds { + XCTAssertEqual(restored.status, .succeeded) + XCTAssertEqual(fileSystem.storedData(at: transactionURL), original) + } else { + XCTAssertEqual(restored.rollback, .restored(try! XCTUnwrap(restored.backup?.id))) + XCTAssertEqual(fileSystem.storedData(at: transactionURL), edited) + } + } + } + + func testDestinationResourceIdentityChangeBlocksBeforeReplacement() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData(arguments: ["14400"]) + let editedData = validLaunchAgentData(arguments: ["7200"]) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let calls = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor(), + destinationContext: { _, _ in + if calls.incrementAndGet() == 2 { + fileSystem.setMetadata( + AutomationFileMetadata( + canonicalURL: sourceURL, + ownerUID: 501, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 2_000), + resourceIdentifier: "replacement-identity" + ), + for: sourceURL + ) + } + return .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501) + } + ) + let payload = AutomationEditPayload( + label: Fixtures.userAgent.label, + executable: "/bin/sleep", + arguments: ["7200"], + environment: [:], + workingDirectory: nil, + schedule: Fixtures.userAgent.schedule, + rawRepresentation: editedData + ) + + let result = await manager.perform( + .edit(payload), + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .failed("The intended destination identity changed immediately before mutation.") + ) + XCTAssertEqual(fileSystem.storedData(at: sourceURL), sourceData) + } + + func testImportRejectsSymlinkAndPathEscapeDestinations() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let destination = sourceURL.deletingLastPathComponent().appendingPathComponent("linked.plist") + let sourceData = validLaunchAgentData() + let importedData = validLaunchAgentData(label: "com.example.linked") + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData, destination: importedData], + metadata: [ + destination: AutomationFileMetadata( + canonicalURL: destination, + ownerUID: 501, + isSymbolicLink: true, + modificationDate: Date(timeIntervalSince1970: 1_000), + resourceIdentifier: "symlink-destination" + ), + ] + ) + let operation = AutomationOperation.importRecord(AutomationImportPayload( + destination: destination, + data: importedData, + expectedKind: .launchAgent, + expectedDestinationChecksum: sha256(importedData) + )) + let symlinkManager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor(), + destinationContext: { _, _ in + .fixture( + currentUID: 501, + canonicalPathIsApproved: true, + ownerUID: 501, + isSymlink: true + ) + } + ) + let symlinkResult = await symlinkManager.perform( + operation, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + let escapeManager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor(), + destinationContext: { _, _ in + .fixture(currentUID: 501, canonicalPathIsApproved: false, ownerUID: 501) + } + ) + let escapeResult = await escapeManager.perform( + operation, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual(symlinkResult.status, .rejected("The intended destination is a symbolic link.")) + XCTAssertEqual( + escapeResult.status, + .rejected("The intended destination resolves outside the approved automation folder.") + ) + } + + func testRollbackRestoresDisabledButLoadedPreStateExactly() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let preStateRecord = copyRecord( + Fixtures.userAgent, + sourceChecksum: sha256(sourceData), + enabledState: .disabled, + loadState: .loaded, + state: .running + ) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor(results: [.failure(.applyFailed)]) + let originalBirth = ProcessBirthToken(seconds: 1_000, microseconds: 1) + let controlled = classifiedAutomationProcess(pid: 700, birth: originalBirth) + let relaunched = classifiedAutomationProcess( + pid: 701, + birth: ProcessBirthToken(seconds: 2_000, microseconds: 2) + ) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { snapshot(records: [preStateRecord]) }, + refreshProcesses: { [relaunched.process] } + ) + + let result = await manager.perform( + .disable, + record: preStateRecord, + expectedChecksum: sha256(sourceData), + linkedProcesses: [controlled] + ) + + XCTAssertEqual(result.rollback, .restored(try! XCTUnwrap(result.backup?.id))) + let states = await executor.recordedRestorationStates() + XCTAssertEqual(states, [AutomationPreTransactionState( + enabledState: .disabled, + loadState: .loaded, + linkedProcesses: [AutomationLinkedProcessIdentity( + processID: controlled.process.pid, + birthToken: originalBirth + )] + )]) + } + + func testRecoveryRetentionKeepsTwentyNewestIncludingCurrentRollbackEvidence() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let disabledRecord = copyRecord( + Fixtures.userAgent, + sourceChecksum: checksum, + enabledState: .disabled, + state: .disabled + ) + let success = AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled], + evidence: [] + ) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor( + results: Array(repeating: .success(success), count: 21) + ) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { snapshot(records: [disabledRecord]) } + ) + var latestID: AutomationBackup.ID? + for _ in 0..<21 { + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + XCTAssertEqual(result.status, .succeeded) + latestID = result.backup?.id + } + + let manifests = await manager.restorationManifests() + XCTAssertEqual(manifests.count, 20) + XCTAssertTrue(manifests.contains { $0.id == latestID }) + } + + func testPruneRestoresManifestWhenBackupDeletionFailsAndRestartSeesConsistentPair() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let disabledRecord = copyRecord( + Fixtures.userAgent, + sourceChecksum: checksum, + enabledState: .disabled, + state: .disabled + ) + let success = AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled], + evidence: [] + ) + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + failingRemoves: [deterministicFirstBackupURL()] + ) + let executor = RecordingAutomationMutationExecutor( + results: Array(repeating: .success(success), count: 21) + ) + let refreshCount = LockedCallCounter() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { + _ = refreshCount.incrementAndGet() + return snapshot(records: [disabledRecord]) + } + ) + for _ in 0..<20 { + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + XCTAssertEqual(result.status, .succeeded) + } + + let failedPrune = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual( + failedPrune.status, + .partialFailure("HIGH SEVERITY: recovery evidence installation was only partially completed.") + ) + XCTAssertEqual(failedPrune.fileMutationEvidence?.kind, .remove) + XCTAssertTrue(fileSystem.itemExists(at: deterministicFirstManifestURL())) + XCTAssertTrue(fileSystem.itemExists(at: deterministicFirstBackupURL())) + XCTAssertEqual(refreshCount.current(), 21) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 20) + + let live = await manager.restorationManifests() + let restarted = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + let restartedManifests = await restarted.restorationManifests() + XCTAssertEqual(live, restartedManifests) + XCTAssertEqual(restartedManifests.count, 21) + XCTAssertTrue(restartedManifests.contains { + $0.backupURL.path == "/redacted" && $0.id.rawValue.uuidString.hasSuffix("555555555555") + }) + } + + func testPruneReportsAttackerManifestAsObservedButNeverAsRecoveryHandle() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let disabledRecord = copyRecord( + Fixtures.userAgent, + sourceChecksum: checksum, + enabledState: .disabled, + state: .disabled + ) + let manifestURL = deterministicFirstManifestURL() + let backupURL = deterministicFirstBackupURL() + let attackerData = Data("attacker-manifest".utf8) + let success = AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled], + evidence: [] + ) + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + failingRemoves: [backupURL], + filesInstalledBeforeRemoveFailure: [backupURL: [manifestURL: attackerData]] + ) + let executor = RecordingAutomationMutationExecutor( + results: Array(repeating: .success(success), count: 21) + ) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { snapshot(records: [disabledRecord]) } + ) + for _ in 0..<20 { + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + XCTAssertEqual(result.status, .succeeded) + } + + let failedPrune = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + guard case .partialFailure = failedPrune.status else { + return XCTFail("Expected partial failure, got \(failedPrune.status)") + } + let evidence = try! XCTUnwrap(failedPrune.fileMutationEvidence) + XCTAssertEqual(fileSystem.storedData(at: manifestURL), attackerData) + XCTAssertTrue(evidence.observedFiles.contains { $0.fileURL == manifestURL }) + XCTAssertFalse(evidence.recoveryHandles.contains { $0.fileURL == manifestURL }) + XCTAssertTrue(evidence.recoveryHandles.contains { $0.fileURL == backupURL }) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 20) + + let currentManifests = await manager.restorationManifests() + let restarted = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + let restartedManifests = await restarted.restorationManifests() + XCTAssertEqual(currentManifests, restartedManifests) + XCTAssertEqual(currentManifests.count, 20) + } + + func testPrePruneManifestAndBackupReplacementsAreNeverMutatedAndMapsMatchRestart() async { + for replacesManifest in [true, false] { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let disabledRecord = copyRecord( + Fixtures.userAgent, + sourceChecksum: checksum, + enabledState: .disabled, + state: .disabled + ) + let success = AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled], + evidence: [] + ) + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor( + results: Array(repeating: .success(success), count: 21) + ) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { snapshot(records: [disabledRecord]) } + ) + for _ in 0..<20 { + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + XCTAssertEqual(result.status, .succeeded) + } + + let targetURL = replacesManifest + ? deterministicFirstManifestURL() : deterministicFirstBackupURL() + let pairedURL = replacesManifest + ? deterministicFirstBackupURL() : deterministicFirstManifestURL() + let pairedData = fileSystem.storedData(at: pairedURL) + let attackerData = Data( + (replacesManifest ? "fresh-manifest-before-prune" : "fresh-backup-before-prune").utf8 + ) + fileSystem.setStoredData(attackerData, at: targetURL) + fileSystem.setMetadata(AutomationFileMetadata( + canonicalURL: targetURL, + ownerUID: 501, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 9_999), + resourceIdentifier: replacesManifest + ? "fixture:fresh-manifest-before-prune" : "fixture:fresh-backup-before-prune", + permissions: 0o600 + ), for: targetURL) + + let result = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .succeeded) + XCTAssertEqual(fileSystem.storedData(at: targetURL), attackerData) + XCTAssertEqual(fileSystem.storedData(at: pairedURL), pairedData) + XCTAssertFalse(fileSystem.recordedOperations.contains { operation in + guard case .remove(let removedURL) = operation else { return false } + return removedURL == targetURL || removedURL == pairedURL + }) + let live = await manager.restorationManifests() + let restarted = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + let reloaded = await restarted.restorationManifests() + XCTAssertEqual(live, reloaded) + XCTAssertEqual(live.count, 20) + } + } + + func testRawAndUnchangedFirstPruneArtifactFailuresRemainOrdinary() async { + for returnsUnchanged in [false, true] { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let checksum = sha256(sourceData) + let disabledRecord = copyRecord( + Fixtures.userAgent, + sourceChecksum: checksum, + enabledState: .disabled, + state: .disabled + ) + let manifestURL = deterministicFirstManifestURL() + let fileSystem = InMemoryAutomationFileSystem( + files: [sourceURL: sourceData], + failingRemoves: returnsUnchanged ? [] : [manifestURL], + unchangedRemoves: returnsUnchanged ? [manifestURL] : [] + ) + let executor = RecordingAutomationMutationExecutor(results: Array( + repeating: .success(AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled], evidence: [] + )), + count: 21 + )) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + refresh: { snapshot(records: [disabledRecord]) } + ) + for _ in 0..<20 { + let succeeded = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + XCTAssertEqual( + succeeded.status, + .succeeded + ) + } + + let failed = await manager.perform( + .disable, + record: Fixtures.userAgent, + expectedChecksum: checksum, + linkedProcesses: [] + ) + + XCTAssertEqual( + failed.status, + .failed("DevScope could not create owner-only recovery evidence.") + ) + XCTAssertNil(failed.fileMutationEvidence) + XCTAssertTrue(fileSystem.itemExists(at: manifestURL)) + XCTAssertTrue(fileSystem.itemExists(at: deterministicFirstBackupURL())) + let live = await manager.restorationManifests() + let restarted = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + let reloaded = await restarted.restorationManifests() + XCTAssertEqual(live, reloaded) + XCTAssertEqual(live.count, 21) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 20) + } + } + + func testForgedManifestCannotLoadOrPruneBackupOutsideRecoveryRoot() async { + let recoveryRoot = URL(fileURLWithPath: "/tmp/devscope-fixtures/recovery") + let externalBackup = URL(fileURLWithPath: "/tmp/devscope-fixtures/do-not-delete.backup") + let uuid = UUID(uuidString: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee")! + let manifestURL = recoveryRoot.appendingPathComponent( + "\(uuid.uuidString.lowercased()).manifest.plist" + ) + let externalData = Data("external-owner-data".utf8) + let manifest = try! PropertyListSerialization.data( + fromPropertyList: [ + "version": 2, + "id": uuid.uuidString.lowercased(), + "recordID": Fixtures.userAgent.id.rawValue, + "sourcePath": try! XCTUnwrap(Fixtures.userAgent.sourceURL).path, + "backupPath": externalBackup.path, + "checksum": sha256(externalData), + "createdAt": Date(timeIntervalSince1970: 1).timeIntervalSince1970, + "sourceExisted": true, + "ownerUID": 501, + "kind": AutomationKind.launchAgent.rawValue, + "sourceKind": AutomationSourceKind.launchAgent.rawValue, + "authorizedRecordIDs": [Fixtures.userAgent.id.rawValue], + "parentResourceIdentifier": "fixture:/tmp/devscope-fixtures", + ], + format: .binary, + options: 0 + ) + let fileSystem = InMemoryAutomationFileSystem( + files: [manifestURL: manifest, externalBackup: externalData], + metadata: [ + recoveryRoot: AutomationFileMetadata( + canonicalURL: recoveryRoot, + ownerUID: 501, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 1), + resourceIdentifier: "recovery-root", + permissions: 0o700 + ), + manifestURL: AutomationFileMetadata( + canonicalURL: manifestURL, + ownerUID: 501, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 1), + resourceIdentifier: "forged-manifest", + permissions: 0o600 + ), + externalBackup: AutomationFileMetadata( + canonicalURL: externalBackup, + ownerUID: 501, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 1), + resourceIdentifier: "external-backup", + permissions: 0o600 + ), + ], + directories: [recoveryRoot] + ) + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor() + ) + + let manifests = await manager.restorationManifests() + XCTAssertEqual(manifests, []) + XCTAssertEqual(fileSystem.storedData(at: externalBackup), externalData) + } + + func testCronDisableStagesTheCompleteLosslessDocumentBeforeTheExecutor() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let original = Data("SHELL=/bin/zsh\n# preserve me\n0 * * * * /usr/bin/true\n".utf8) + let intended = Data("SHELL=/bin/zsh\n# preserve me\n# devscope-disabled: 0 * * * * /usr/bin/true\n".utf8) + let originalRecords = await realCronRecords(original) + let intendedRecords = await realCronRecords(intended) + let record = try! XCTUnwrap(originalRecords.first) + let disabled = try! XCTUnwrap(intendedRecords.first) + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: original]) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.futureLaunchesDisabled, .sourceChecksum(sha256(intended))], + evidence: [] + )), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + recoverableSource: recoverableCronSource(transactionURL, fileSystem: fileSystem), + refresh: { snapshot(records: [disabled]) } + ) + + let result = await manager.perform( + .disable, + record: record, + expectedChecksum: sha256(original), + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .succeeded) + XCTAssertEqual(fileSystem.storedData(at: transactionURL), intended) + } + + func testCronRemoveInstallsTheRemainingDocumentWithoutTrashingTheTransactionSource() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let original = Data("# preserve me\n0 * * * * /usr/bin/true\n30 * * * * /usr/bin/false\n".utf8) + let intended = Data("# preserve me\n30 * * * * /usr/bin/false\n".utf8) + let records = await realCronRecords(original) + let record = try! XCTUnwrap(records.first) + let remaining = await realCronRecords(intended) + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: original]) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.sourceRemoved, .sourceChecksum(sha256(intended))], + evidence: [] + )), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + recoverableSource: recoverableCronSource(transactionURL, fileSystem: fileSystem), + refresh: { snapshot(records: remaining) } + ) + + let result = await manager.perform( + .remove, + record: record, + expectedChecksum: sha256(original), + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .succeeded) + XCTAssertEqual(fileSystem.storedData(at: transactionURL), intended) + XCTAssertFalse(fileSystem.recordedOperations.contains { operation in + if case .moveToTrash = operation { return true } + return false + }) + } + + func testConfirmedCronRunNowCanFinishImmediatelyAndRedactsTheExactCommandFromResult() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let secretCommand = "/usr/bin/curl -H 'Authorization: secret-value' https://example.invalid" + let data = Data("0 * * * * \(secretCommand)\n".utf8) + let records = await realCronRecords(data) + let record = try! XCTUnwrap(records.first) + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: data]) + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult( + postconditions: [.targetResolved, .runCompleted], + evidence: [] + )), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + recoverableSource: recoverableCronSource(transactionURL, fileSystem: fileSystem), + refresh: { snapshot(records: [record]) } + ) + let confirmation = AutomationRunToCompletionConfirmation( + recordID: record.id, + sourceChecksum: record.sourceChecksum, + exactCommand: secretCommand + ) + + let result = await manager.perform( + .confirmedRunToCompletion(confirmation), + record: record, + expectedChecksum: record.sourceChecksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .succeeded) + guard case .confirmedRunToCompletion(let redacted) = result.operation else { + return XCTFail("Expected the typed confirmed operation") + } + XCTAssertEqual(redacted.recordID, record.id) + XCTAssertEqual(redacted.exactCommand, "") + XCTAssertFalse(String(describing: result).contains(secretCommand)) + } + + func testConfirmedCronRunRejectsAStaleRecordGenerationBeforeExecutorInvocation() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let staleData = Data("0 * * * * /usr/bin/old-command\n".utf8) + let currentData = Data("0 * * * * /usr/bin/current-command\n".utf8) + let staleRecords = await realCronRecords(staleData) + let staleRecord = try! XCTUnwrap(staleRecords.first) + let currentChecksum = try! XCTUnwrap(CronDocumentChecksum.checksum(currentData)) + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: currentData]) + let executor = RecordingAutomationMutationExecutor() + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + recoverableSource: recoverableCronSource(transactionURL, fileSystem: fileSystem) + ) + let confirmation = AutomationRunToCompletionConfirmation( + recordID: staleRecord.id, + sourceChecksum: staleRecord.sourceChecksum, + exactCommand: staleRecord.commandSignature! + ) + + let result = await manager.perform( + .confirmedRunToCompletion(confirmation), + record: staleRecord, + expectedChecksum: currentChecksum, + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .rejected("The confirmed cron command belongs to a stale source generation.") + ) + let invocationCount = await executor.invocationCount() + XCTAssertEqual(invocationCount, 0) + } + + func testCronRollbackDoesNotOverwriteAnExternalEditAfterManagerVerificationFails() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let original = Data("0 * * * * /usr/bin/true\n".utf8) + let intended = Data("# devscope-disabled: 0 * * * * /usr/bin/true\n".utf8) + let external = Data("15 * * * * /usr/bin/external\n".utf8) + let records = await realCronRecords(original) + let record = try! XCTUnwrap(records.first) + let runner = ScriptedManagerCommandRunner(results: [ + AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()), + AutomationCommandResult(status: 0, standardOutput: intended, standardError: Data()), + AutomationCommandResult(status: 0, standardOutput: external, standardError: Data()), + ]) + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: original]) + let manager = makeManager( + fileSystem: fileSystem, + executor: CronAutomationExecutor( + runner: runner, + fileSystem: fileSystem, + currentUID: 501 + ), + recoverableSource: recoverableCronSource(transactionURL, fileSystem: fileSystem), + refresh: { snapshot(records: await realCronRecords(original)) } + ) + + let result = await manager.perform( + .disable, + record: record, + expectedChecksum: record.sourceChecksum, + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .partialFailure("HIGH SEVERITY: the operation failed and automatic rollback did not complete.") + ) + XCTAssertEqual(fileSystem.storedData(at: transactionURL), original) + XCTAssertEqual(runner.invocations.count, 3) + XCTAssertEqual( + runner.invocations.filter { $0.executable == "/usr/bin/crontab" && $0.arguments != ["-l"] }.count, + 1, + "Rollback must not install the backup over an externally changed live crontab." + ) + } + + func testLegacyDisableUsesSelectedPathAbsenceAndPreservesRecoveryDescriptor() async { + let descriptorURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/legacy-login-items.json") + let selected = LegacyLoginItemDescriptor( + name: "Backup", + path: "/Applications/Backup.app", + isHidden: false + ) + let records = await realLegacyLoginItemRecords([selected]) + let record = try! XCTUnwrap(records.first) + let descriptorData = legacyRecoveryDescriptorData(record: record, isHidden: false) + XCTAssertEqual(record.sourceChecksum, sha256(descriptorData)) + let fileSystem = InMemoryAutomationFileSystem(files: [descriptorURL: descriptorData]) + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult(postconditions: [.futureLaunchesDisabled], evidence: [])), + ]), + recoverableSource: recoverableLegacySource(descriptorURL, fileSystem: fileSystem), + refresh: { snapshot(records: [], health: .legacyLoginItem, state: .healthy) } + ) + + let result = await manager.perform( + .disable, + record: record, + expectedChecksum: record.sourceChecksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .succeeded) + XCTAssertEqual(fileSystem.storedData(at: descriptorURL), descriptorData) + XCTAssertFalse(fileSystem.recordedOperations.contains { if case .moveToTrash = $0 { true } else { false } }) + } + + func testLegacyRemoveAllowsUnrelatedItemsAndPreservesRecoveryDescriptor() async { + let descriptorURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/legacy-login-items.json") + let selected = LegacyLoginItemDescriptor( + name: "Backup", + path: "/Applications/Backup.app", + isHidden: false + ) + let unrelated = LegacyLoginItemDescriptor( + name: "Keep", + path: "/Applications/Keep.app", + isHidden: false + ) + let initialRecords = await realLegacyLoginItemRecords([selected, unrelated]) + let record = try! XCTUnwrap(initialRecords.first { $0.executable == selected.path }) + let descriptorData = legacyRecoveryDescriptorData(record: record, isHidden: false) + XCTAssertEqual(record.sourceChecksum, sha256(descriptorData)) + let remaining = await realLegacyLoginItemRecords([unrelated]) + let fileSystem = InMemoryAutomationFileSystem(files: [descriptorURL: descriptorData]) + let manager = makeManager( + fileSystem: fileSystem, + executor: RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult(postconditions: [.sourceRemoved], evidence: [])), + ]), + recoverableSource: recoverableLegacySource(descriptorURL, fileSystem: fileSystem), + refresh: { snapshot(records: remaining, health: .legacyLoginItem, state: .healthy) } + ) + + let result = await manager.perform( + .remove, + record: record, + expectedChecksum: record.sourceChecksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .succeeded) + XCTAssertEqual(fileSystem.storedData(at: descriptorURL), descriptorData) + XCTAssertTrue(remaining.contains { $0.executable == unrelated.path }) + XCTAssertFalse(fileSystem.recordedOperations.contains { if case .moveToTrash = $0 { true } else { false } }) + } + + func testLegacyAbsenceRequiresHealthyAuthoritativeSourceForEveryUnhealthyState() async { + let descriptorURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/legacy-login-items.json") + let selected = LegacyLoginItemDescriptor( + name: "Backup", + path: "/Applications/Backup.app", + isHidden: true + ) + let records = await realLegacyLoginItemRecords([selected]) + let record = try! XCTUnwrap(records.first) + let descriptorData = legacyRecoveryDescriptorData(record: record, isHidden: true) + + for operation in [AutomationOperation.disable, .remove] { + for healthState in [ + AutomationSourceHealthState.failed, + .partial, + .permissionRequired, + ] { + let refreshCount = LockedCallCounter() + let fileSystem = InMemoryAutomationFileSystem(files: [descriptorURL: descriptorData]) + let postcondition: AutomationPostcondition = operation == .disable + ? .futureLaunchesDisabled + : .sourceRemoved + let executor = RecordingAutomationMutationExecutor(results: [ + .success(AutomationExecutorResult(postconditions: [postcondition], evidence: [])), + ]) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + recoverableSource: recoverableLegacySource(descriptorURL, fileSystem: fileSystem), + refresh: { + refreshCount.incrementAndGet() == 1 + ? snapshot(records: [], health: .legacyLoginItem, state: healthState) + : snapshot(records: [record], health: .legacyLoginItem, state: .healthy) + } + ) + + let result = await manager.perform( + operation, + record: record, + expectedChecksum: sha256(descriptorData), + linkedProcesses: [] + ) + let message = "\(operation) \(healthState.rawValue)" + XCTAssertNotEqual(result.status, .succeeded, message) + XCTAssertEqual(result.rollback, .restored(try! XCTUnwrap(result.backup?.id)), message) + XCTAssertTrue( + result.verificationEvidence.contains { $0.lowercased().contains("authoritative") }, + message + ) + } + } + } + + func testCronDisableAndStopUsesOwnedGenerationForSnapshotAndPollFailures() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let original = Data("* * * * * /usr/bin/true\n".utf8) + let intended = Data("# devscope-disabled: * * * * * /usr/bin/true\n".utf8) + let records = await realCronRecords(original) + let record = try! XCTUnwrap(records.first) + let (classified, processes) = managerCronProcessFixture() + + for postInstallFailure in ["snapshot", "poll"] { + let runner = ScriptedManagerCommandRunner(results: [ + AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()), + AutomationCommandResult(status: 0, standardOutput: intended, standardError: Data()), + AutomationCommandResult(status: 0, standardOutput: intended, standardError: Data()), + AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()), + AutomationCommandResult(status: 0, standardOutput: original, standardError: Data()), + ]) + let signalCount = LockedCallCounter() + let resolver: ProcessKiller.IdentityResolver = { _ in + .identity(ProcessIdentity(process: classified.process)) + } + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: original]) + let executor = CronAutomationExecutor( + runner: runner, + fileSystem: fileSystem, + currentUID: 501, + processKiller: ProcessKiller( + identityResolver: resolver, + signalSender: { _, _ in _ = signalCount.incrementAndGet() } + ), + currentProcessID: 99_999, + processIdentityResolver: resolver, + processSnapshot: { + if postInstallFailure == "snapshot" { + throw AutomationExecutorError.processControlUnavailable + } + return processes + }, + now: { Date(timeIntervalSince1970: 3_600) }, + terminationVerificationPolicy: .init(maximumAttempts: 1, interval: .zero), + verificationSleep: { _ in } + ) + let manager = makeManager( + fileSystem: fileSystem, + executor: executor, + recoverableSource: recoverableCronSource(transactionURL, fileSystem: fileSystem), + refresh: { snapshot(records: records) }, + refreshProcesses: { processes } + ) + + let result = await manager.perform( + .disableAndStop, + record: record, + expectedChecksum: record.sourceChecksum, + linkedProcesses: [classified] + ) + + XCTAssertEqual( + result.status, + .partialFailure("HIGH SEVERITY: the operation failed and automatic rollback did not complete."), + postInstallFailure + ) + XCTAssertEqual(fileSystem.storedData(at: transactionURL), original, postInstallFailure) + XCTAssertEqual(runner.invocations.count, 5, postInstallFailure) + XCTAssertEqual(runner.invocations[2].arguments, ["-l"], postInstallFailure) + XCTAssertNotEqual(runner.invocations[3].arguments, ["-l"], postInstallFailure) + XCTAssertEqual(signalCount.current(), postInstallFailure == "poll" ? 1 : 0) + } + } + + func testManagerPassesImmutableVerifiedCronBytesAfterBackupPathMutation() async { + let transactionURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/current-crontab") + let original = Data("0 * * * * /usr/bin/true\n5 * * * * /usr/bin/keep\n".utf8) + let intended = Data("# devscope-disabled: 0 * * * * /usr/bin/true\n5 * * * * /usr/bin/keep\n".utf8) + let altered = Data("0 * * * * /usr/bin/true\n10 * * * * /usr/bin/unrelated-change\n".utf8) + let records = await realCronRecords(original) + let record = try! XCTUnwrap(records.first) + let fileSystem = InMemoryAutomationFileSystem(files: [transactionURL: original]) + let callIndex = LockedCallCounter() + let runner = HookedManagerCommandRunner { command in + switch callIndex.incrementAndGet() { + case 1: + return AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()) + case 2: + fileSystem.setReadObserver { url, _ in + guard url.pathExtension == "backup" else { return } + fileSystem.setReadObserver(nil) + fileSystem.setStoredData(altered, at: url) + } + return AutomationCommandResult(status: 0, standardOutput: intended, standardError: Data()) + case 3: + return AutomationCommandResult(status: 0, standardOutput: intended, standardError: Data()) + case 4: + XCTAssertEqual(fileSystem.storedData(at: URL(fileURLWithPath: command.arguments[0])), original) + return AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()) + default: + return AutomationCommandResult(status: 0, standardOutput: original, standardError: Data()) + } + } + let manager = makeManager( + fileSystem: fileSystem, + executor: CronAutomationExecutor(runner: runner, fileSystem: fileSystem, currentUID: 501), + recoverableSource: recoverableCronSource(transactionURL, fileSystem: fileSystem), + refresh: { snapshot(records: records) } + ) + + let result = await manager.perform( + .disable, + record: record, + expectedChecksum: record.sourceChecksum, + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .failed("The automation operation failed; the prior source and loaded state were restored.")) + XCTAssertEqual(runner.invocations.count, 5) + XCTAssertEqual(fileSystem.storedData(at: deterministicFirstBackupURL()), altered) + } + + func testManagerPassesImmutableVerifiedLegacyHiddenValueAfterBackupPathMutation() async { + let descriptorURL = URL(fileURLWithPath: "/tmp/devscope-fixtures/legacy-login-items.json") + let selected = LegacyLoginItemDescriptor( + name: "Backup", + path: "/Applications/Backup.app", + isHidden: true + ) + let records = await realLegacyLoginItemRecords([selected]) + let record = try! XCTUnwrap(records.first) + let original = legacyRecoveryDescriptorData(record: record, isHidden: true) + let altered = legacyRecoveryDescriptorData(record: record, isHidden: false) + let fileSystem = InMemoryAutomationFileSystem(files: [descriptorURL: original]) + let commandIndex = LockedCallCounter() + let runner = HookedManagerCommandRunner { command in + if commandIndex.incrementAndGet() == 1 { + fileSystem.setReadObserver { url, _ in + guard url.pathExtension == "backup" else { return } + fileSystem.setReadObserver(nil) + fileSystem.setStoredData(altered, at: url) + } + } else { + XCTAssertEqual(Array(command.arguments.suffix(4)), ["--", "add", selected.path, "true"]) + } + return AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()) + } + let listing = SequencedManagerLegacyLoginItemListing(values: [[], [selected]]) + let manager = makeManager( + fileSystem: fileSystem, + executor: LegacyLoginItemAutomationExecutor( + runner: runner, + listing: listing, + currentUID: 501 + ), + recoverableSource: recoverableLegacySource(descriptorURL, fileSystem: fileSystem), + refresh: { snapshot(records: records, health: .legacyLoginItem, state: .healthy) } + ) + + let result = await manager.perform( + .disable, + record: record, + expectedChecksum: sha256(original), + linkedProcesses: [] + ) + + XCTAssertEqual(result.status, .failed("The automation operation failed; the prior source and loaded state were restored.")) + XCTAssertEqual(runner.invocations.count, 2) + XCTAssertEqual(fileSystem.storedData(at: deterministicFirstBackupURL()), altered) + } + + func testBackupCorruptionBeforeManagerAuthenticationNeverReachesRecoveryExecutor() async { + let sourceURL = try! XCTUnwrap(Fixtures.userAgent.sourceURL) + let sourceData = validLaunchAgentData() + let fileSystem = InMemoryAutomationFileSystem(files: [sourceURL: sourceData]) + let executor = RecordingAutomationMutationExecutor( + results: [.failure(.applyFailed)], + onApply: { + fileSystem.setStoredData(Data("corrupted".utf8), at: deterministicFirstBackupURL()) + } + ) + let manager = makeManager(fileSystem: fileSystem, executor: executor) + + let result = await manager.perform( + .startNow, + record: Fixtures.userAgent, + expectedChecksum: sha256(sourceData), + linkedProcesses: [] + ) + + XCTAssertEqual( + result.status, + .partialFailure("HIGH SEVERITY: the operation failed and automatic rollback did not complete.") + ) + let restorationStates = await executor.recordedRestorationStates() + XCTAssertTrue(restorationStates.isEmpty) + } +} + +private extension AutomationManagerTests { + func makeManager( + fileSystem: InMemoryAutomationFileSystem, + executor: any AutomationMutationApplying, + context: AutomationCapabilityContext = .fixture( + currentUID: 501, + canonicalPathIsApproved: true, + ownerUID: 501 + ), + destinationContext: @escaping @Sendable ( + AutomationRecord, + URL + ) throws -> AutomationCapabilityContext = { _, _ in + .fixture(currentUID: 501, canonicalPathIsApproved: true, ownerUID: 501) + }, + recoverableSource: @escaping @Sendable ( + AutomationRecord + ) async throws -> AutomationRecoverableSource = { _ in + throw AutomationManagerConfigurationError.recoverableSourceUnavailable + }, + refresh: @escaping @Sendable () async -> AutomationInventorySnapshot = { + Fixtures.inventoryGeneration7 + }, + refreshProcesses: @escaping @Sendable () async throws -> [DevProcess] = { [] } + ) -> AutomationManager { + let uuidSequence = DeterministicAutomationUUIDSequence() + return AutomationManager( + fileSystem: fileSystem, + executor: executor, + capabilityContext: { _ in context }, + destinationContext: destinationContext, + recoverableSource: recoverableSource, + refresh: refresh, + refreshProcesses: refreshProcesses, + backupDirectory: URL(fileURLWithPath: "/tmp/devscope-fixtures/recovery"), + currentUID: 501, + now: { Date(timeIntervalSince1970: 20_000) }, + makeUUID: { uuidSequence.next() } + ) + } +} + +private final class DeterministicAutomationUUIDSequence: @unchecked Sendable { + private let lock = NSLock() + private var sequence: UInt8 = 0x55 + + func next() -> UUID { + lock.withLock { + defer { sequence &+= 1 } + return UUID(uuid: ( + 0x11, 0x11, 0x11, 0x11, + 0x22, 0x22, + 0x33, 0x33, + 0x44, 0x44, + 0x55, 0x55, 0x55, 0x55, 0x55, sequence + )) + } + } +} + +private final class LockedCallCounter: @unchecked Sendable { + private let lock = NSLock() + private var value = 0 + + func incrementAndGet() -> Int { + lock.withLock { + value += 1 + return value + } + } + + func current() -> Int { lock.withLock { value } } +} + +private final class LockedDataCapture: @unchecked Sendable { + private let lock = NSLock() + private var value: Data? + + func set(_ data: Data) { lock.withLock { value = data } } + + func current() -> Data? { lock.withLock { value } } +} + +private final class LockedFlag: @unchecked Sendable { + private let lock = NSLock() + private var isSet = false + + var value: Bool { lock.withLock { isSet } } + + func set() { + lock.withLock { isSet = true } + } +} + +private final class ScriptedManagerCommandRunner: AutomationCommandRunning, @unchecked Sendable { + private let lock = NSLock() + private var results: [AutomationCommandResult] + private var commands: [AutomationCommand] = [] + + init(results: [AutomationCommandResult]) { + self.results = results + } + + var invocations: [AutomationCommand] { + lock.withLock { commands } + } + + func run(_ command: AutomationCommand) async throws -> AutomationCommandResult { + lock.withLock { + commands.append(command) + guard !results.isEmpty else { + return AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()) + } + return results.removeFirst() + } + } +} + +private final class HookedManagerCommandRunner: AutomationCommandRunning, @unchecked Sendable { + private let lock = NSLock() + private let handler: @Sendable (AutomationCommand) -> AutomationCommandResult + private var commands: [AutomationCommand] = [] + + init(handler: @escaping @Sendable (AutomationCommand) -> AutomationCommandResult) { + self.handler = handler + } + + var invocations: [AutomationCommand] { lock.withLock { commands } } + + func run(_ command: AutomationCommand) async throws -> AutomationCommandResult { + lock.withLock { commands.append(command) } + return handler(command) + } +} + +private final class SequencedManagerLegacyLoginItemListing: LegacyLoginItemListing, @unchecked Sendable { + private let lock = NSLock() + private var values: [[LegacyLoginItemDescriptor]] + + init(values: [[LegacyLoginItemDescriptor]]) { self.values = values } + + func currentUserLoginItems() async throws -> [LegacyLoginItemDescriptor] { + lock.withLock { values.isEmpty ? [] : values.removeFirst() } + } +} + +private enum AutomationManagerFixtureError: Error { + case applyFailed + case rollbackFailed +} + +private actor RecordingAutomationMutationExecutor: AutomationMutationApplying { + private var invocations = 0 + private var operations: [AutomationOperation] = [] + private var results: [Result] + private let delayNanoseconds: UInt64 + private let restorationFails: Bool + private let onApply: @Sendable () -> Void + private var activeInvocations = 0 + private var maximumActiveInvocations = 0 + private var restorationStates: [AutomationPreTransactionState] = [] + + init( + results: [Result] = [ + .success(AutomationExecutorResult(postconditions: [], evidence: [])), + ], + delayNanoseconds: UInt64 = 0, + restorationFails: Bool = false, + onApply: @escaping @Sendable () -> Void = {} + ) { + self.results = results + self.delayNanoseconds = delayNanoseconds + self.restorationFails = restorationFails + self.onApply = onApply + } + + func apply( + _ operation: AutomationOperation, + to record: AutomationRecord, + linkedProcesses: [ClassifiedDevProcess], + proposedSourceURL: URL? + ) async throws -> AutomationExecutorResult { + invocations += 1 + onApply() + operations.append(operation) + activeInvocations += 1 + maximumActiveInvocations = max(maximumActiveInvocations, activeInvocations) + defer { activeInvocations -= 1 } + guard !results.isEmpty else { + return AutomationExecutorResult(postconditions: [], evidence: []) + } + let result = results.removeFirst() + if delayNanoseconds > 0 { + try await Task.sleep(nanoseconds: delayNanoseconds) + } + return try result.get() + } + + func invocationCount() -> Int { + invocations + } + + func recordedOperations() -> [AutomationOperation] { + operations + } + + func maximumConcurrency() -> Int { + maximumActiveInvocations + } + + func recordedRestorationStates() -> [AutomationPreTransactionState] { + restorationStates + } + + func restorePreTransactionState( + _ state: AutomationPreTransactionState, + for record: AutomationRecord, + recovery: AutomationVerifiedRecoveryInput, + linkedProcesses: [ClassifiedDevProcess], + expectedAppliedState: AutomationExecutorAppliedState? + ) async throws -> AutomationExecutorResult { + if restorationFails { throw AutomationManagerFixtureError.rollbackFailed } + restorationStates.append(state) + return AutomationExecutorResult( + postconditions: [.preTransactionStateRestored(state)], + evidence: [] + ) + } +} + +private func validLaunchAgentData( + label: String = "com.example.backup", + arguments: [String] = ["14400"], + environment: [String: String] = [:], + workingDirectory: String? = nil +) -> Data { + var propertyList: [String: Any] = [ + "Label": label, + "ProgramArguments": ["/bin/sleep"] + arguments, + "RunAtLoad": true, + "StartInterval": 14_400, + ] + if !environment.isEmpty { + propertyList["EnvironmentVariables"] = environment + } + if let workingDirectory { + propertyList["WorkingDirectory"] = workingDirectory + } + return try! PropertyListSerialization.data( + fromPropertyList: propertyList, + format: .xml, + options: 0 + ) +} + +private func snapshot( + records: [AutomationRecord], + health sourceKind: AutomationSourceKind? = nil, + state: AutomationSourceHealthState = .healthy +) -> AutomationInventorySnapshot { + var health = Fixtures.inventoryGeneration7.health + if let sourceKind { + health[sourceKind] = AutomationSourceHealth( + kind: sourceKind, + state: state, + message: state == .healthy ? nil : "fixture \(state.rawValue)", + refreshedAt: Date(timeIntervalSince1970: 20_001) + ) + } + return AutomationInventorySnapshot( + generation: 8, + records: records, + health: health, + refreshedAt: Date(timeIntervalSince1970: 20_001) + ) +} + +private func copyRecord( + _ record: AutomationRecord, + id: AutomationRecord.ID? = nil, + label: String? = nil, + sourceURL: URL? = nil, + sourceChecksum: String? = nil, + enabledState: AutomationEnabledState? = nil, + loadState: AutomationLoadState? = nil, + state: AutomationState? = nil +) -> AutomationRecord { + AutomationRecord( + id: id ?? record.id, + kind: record.kind, + sourceKind: record.sourceKind, + label: label ?? record.label, + displayName: record.displayName, + providerBundleIdentifier: record.providerBundleIdentifier, + ownerUID: record.ownerUID, + ownership: record.ownership, + executable: record.executable, + arguments: record.arguments, + commandSignature: record.commandSignature, + environment: record.environment, + workingDirectory: record.workingDirectory, + schedule: record.schedule, + sourceURL: sourceURL ?? record.sourceURL, + sourceChecksum: sourceChecksum ?? record.sourceChecksum, + enabledState: enabledState ?? record.enabledState, + loadState: loadState ?? record.loadState, + approvalState: record.approvalState, + state: state ?? record.state, + evidence: record.evidence, + capabilities: record.capabilities, + validationFindings: record.validationFindings + ) +} + +private func sha256(_ data: Data) -> String { + SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined() +} + +private func deterministicFirstBackupURL() -> URL { + URL(fileURLWithPath: "/tmp/devscope-fixtures/recovery") + .appendingPathComponent("11111111-2222-3333-4444-555555555555.backup") +} + +private func deterministicFirstManifestURL() -> URL { + URL(fileURLWithPath: "/tmp/devscope-fixtures/recovery") + .appendingPathComponent("11111111-2222-3333-4444-555555555555.manifest.plist") +} + +private func classifiedAutomationProcess( + pid: Int32, + birth: ProcessBirthToken +) -> ClassifiedDevProcess { + ClassifiedDevProcess( + process: DevProcess( + pid: pid, + parentPID: 1, + executable: "/bin/sleep", + command: "/bin/sleep 14400", + argumentVector: ["/bin/sleep", "14400"], + birthToken: birth, + launchLabel: Fixtures.userAgent.label + ), + classification: DevProcessClassification( + kind: .backgroundAgent, + displayName: "Automation", + projectHint: nil + ) + ) +} + +private func cronRecord(entry: CronEntry, checksum: String) -> AutomationRecord { + AutomationRecord( + id: CronParser.recordID(for: entry, ownerUID: 501), + kind: .cron, + sourceKind: .crontab, + label: "Cron entry 1", + displayName: "Cron entry 1", + providerBundleIdentifier: nil, + ownerUID: 501, + ownership: .user, + executable: nil, + arguments: [], + commandSignature: entry.command, + environment: entry.environment, + workingDirectory: nil, + schedule: entry.schedule, + sourceURL: nil, + sourceChecksum: checksum, + enabledState: .enabled, + loadState: .unknown, + approvalState: .notApplicable, + state: .idle, + evidence: [], + capabilities: [], + validationFindings: [] + ) +} + +private func realCronRecords(_ data: Data) async -> [AutomationRecord] { + await CronAutomationSource( + commandRunner: RecordingAutomationCommandRunner(result: .success(AutomationCommandResult( + status: 0, + standardOutput: data, + standardError: Data() + ))), + currentUID: 501, + currentUsername: "test-user" + ).snapshot().records +} + +private func realLegacyLoginItemRecords( + _ items: [LegacyLoginItemDescriptor] +) async -> [AutomationRecord] { + await LegacyLoginItemAutomationSource( + adapter: StaticManagerLegacyLoginItemListing(items: items), + currentUID: 501 + ).snapshot().records +} + +private func managerCronProcessFixture() -> ( + classified: ClassifiedDevProcess, + processes: [DevProcess] +) { + let carrier = DevProcess( + pid: 66_101, + parentPID: 66_100, + executable: "/bin/sh", + command: "/bin/sh -c /usr/bin/true", + argumentVector: ["/bin/sh", "-c", "/usr/bin/true"], + resourceUsage: DevProcessResourceUsage( + cpuPercent: 0, + residentMemoryBytes: 1, + elapsedTime: "00:00:00" + ), + birthToken: ProcessBirthToken(seconds: 3_600, microseconds: 1) + ) + let daemon = DevProcess( + pid: 66_100, + parentPID: 1, + executable: "/usr/sbin/cron", + command: "/usr/sbin/cron", + argumentVector: ["/usr/sbin/cron"], + birthToken: ProcessBirthToken(seconds: 1, microseconds: 0) + ) + return ( + ClassifiedDevProcess( + process: carrier, + classification: DevProcessClassification(kind: .shell, displayName: "cron", projectHint: nil) + ), + [daemon, carrier] + ) +} + +private func legacyRecoveryDescriptorData( + record: AutomationRecord, + isHidden: Bool +) -> Data { + try! LegacyLoginItemRecoveryDocument.encode( + selectedRecord: record, + descriptor: LegacyLoginItemDescriptor( + name: record.label, + path: record.executable!, + isHidden: isHidden + ), + currentUID: 501 + ) +} + +private struct StaticManagerLegacyLoginItemListing: LegacyLoginItemListing { + let items: [LegacyLoginItemDescriptor] + + func currentUserLoginItems() async throws -> [LegacyLoginItemDescriptor] { items } +} + +private func recoverableCronSource( + _ transactionURL: URL, + fileSystem: InMemoryAutomationFileSystem +) -> @Sendable (AutomationRecord) async throws -> AutomationRecoverableSource { + { _ in + let data = try fileSystem.read(transactionURL) + return AutomationRecoverableSource( + transactionURL: transactionURL, + data: data, + checksum: sha256(data), + metadata: try fileSystem.metadata(for: transactionURL) + ) + } +} + +private func recoverableLegacySource( + _ transactionURL: URL, + fileSystem: InMemoryAutomationFileSystem +) -> @Sendable (AutomationRecord) async throws -> AutomationRecoverableSource { + { _ in + let data = try fileSystem.read(transactionURL) + return AutomationRecoverableSource( + transactionURL: transactionURL, + data: data, + checksum: sha256(data), + metadata: try fileSystem.metadata(for: transactionURL) + ) + } +} diff --git a/Tests/DevScopeCoreTests/AutomationModelsTests.swift b/Tests/DevScopeCoreTests/AutomationModelsTests.swift new file mode 100644 index 0000000..c2bdf3f --- /dev/null +++ b/Tests/DevScopeCoreTests/AutomationModelsTests.swift @@ -0,0 +1,127 @@ +import XCTest +@testable import DevScopeCore + +final class AutomationModelsTests: XCTestCase { + func testRecordIdentityDoesNotContainCommandSecrets() { + let id = AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: "com.example.backup", + sourcePath: "/Users/example/Library/LaunchAgents/com.example.backup.plist" + ) + + XCTAssertEqual(id.rawValue.count, 64) + XCTAssertFalse(id.rawValue.contains("example")) + } + + func testRecordIdentityUsesEveryCanonicalSourceComponent() { + let base = AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: "com.example.fixture", + sourcePath: "/tmp/devscope-fixtures/folder/../fixture.plist" + ) + + XCTAssertEqual(base, AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: "com.example.fixture", + sourcePath: "/tmp/devscope-fixtures/fixture.plist" + )) + XCTAssertNotEqual(base, AutomationRecord.ID( + source: .launchDaemon, + ownerUID: 501, + label: "com.example.fixture", + sourcePath: "/tmp/devscope-fixtures/fixture.plist" + )) + XCTAssertNotEqual(base, AutomationRecord.ID( + source: .launchAgent, + ownerUID: 502, + label: "com.example.fixture", + sourcePath: "/tmp/devscope-fixtures/fixture.plist" + )) + XCTAssertNotEqual(base, AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: "com.example.other-fixture", + sourcePath: "/tmp/devscope-fixtures/fixture.plist" + )) + XCTAssertNotEqual(base, AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: "com.example.fixture", + sourcePath: "/tmp/devscope-fixtures/other-fixture.plist" + )) + } + + func testRecordIdentityDistinguishesDelimiterAmbiguousFieldTuples() { + let delimiterInPath = AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: "com.example.fixture", + sourcePath: "/tmp/first\u{1F}/tmp/second.plist" + ) + let delimiterInLabel = AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: "com.example.fixture\u{1F}/tmp/first", + sourcePath: "/tmp/second.plist" + ) + + XCTAssertNotEqual(delimiterInPath, delimiterInLabel) + } + + func testSnapshotFactoriesPreserveRecordsAndSourceFailure() { + let refreshedAt = Date(timeIntervalSince1970: 1_000) + let record = AutomationRecord( + id: AutomationRecord.ID(rawValue: "record-id"), + kind: .launchAgent, + sourceKind: .launchAgent, + label: "com.example.synthetic-backup", + displayName: "Synthetic Backup", + providerBundleIdentifier: nil, + ownerUID: 501, + ownership: .user, + executable: "/bin/sleep", + arguments: ["60"], + environment: [:], + workingDirectory: nil, + schedule: AutomationSchedule(triggers: [.runAtLoad], summary: "At load"), + sourceURL: URL(fileURLWithPath: "/tmp/com.example.synthetic-backup.plist"), + sourceChecksum: nil, + enabledState: .enabled, + loadState: .loaded, + approvalState: .notApplicable, + state: .running, + evidence: [AutomationEvidence( + strength: .strong, + source: "synthetic fixture", + detail: "Exact label and executable" + )], + capabilities: [.disableAndStop], + validationFindings: [] + ) + + let healthy = AutomationSourceSnapshot.healthy( + kind: .launchAgent, + records: [record], + refreshedAt: refreshedAt + ) + let failed = AutomationSourceSnapshot.failed( + kind: .serviceManagement, + message: "Synthetic source failure", + refreshedAt: refreshedAt + ) + + XCTAssertEqual(healthy.records, [record]) + XCTAssertEqual(healthy.health, AutomationSourceHealth( + kind: .launchAgent, + state: .healthy, + message: nil, + refreshedAt: refreshedAt + )) + XCTAssertTrue(failed.records.isEmpty) + XCTAssertEqual(failed.health.state, .failed) + XCTAssertEqual(failed.health.message, "Synthetic source failure") + } +} diff --git a/Tests/DevScopeCoreTests/AutomationNotificationPolicyTests.swift b/Tests/DevScopeCoreTests/AutomationNotificationPolicyTests.swift new file mode 100644 index 0000000..f0432ed --- /dev/null +++ b/Tests/DevScopeCoreTests/AutomationNotificationPolicyTests.swift @@ -0,0 +1,80 @@ +import XCTest +@testable import DevScopeCore + +final class AutomationNotificationPolicyTests: XCTestCase { + func testDefaultPreferencesStayOffAndOnlyDirectOptInRequestsAuthorization() { + var policy = AutomationNotificationPolicy(maximumRetainedEventIdentities: 8) + + XCTAssertEqual(policy.preferences, AutomationNotificationPreferences()) + XCTAssertEqual( + policy.setPreference(.unexpectedExit, isEnabled: false), + .none + ) + XCTAssertEqual( + policy.setPreference(.unexpectedExit, isEnabled: true), + .requestAuthorization + ) + } + + func testVerifiedEventsUseSafeContentAndBoundedIdentityDeduplication() { + var policy = AutomationNotificationPolicy(maximumRetainedEventIdentities: 2) + XCTAssertEqual(policy.setPreference(.unexpectedExit, isEnabled: true), .requestAuthorization) + policy.recordAuthorizationResult(granted: true) + + let first = unexpectedExit(label: "first", pid: 41) + let second = unexpectedExit(label: "second", pid: 42) + let third = unexpectedExit(label: "third", pid: 43) + + let content = policy.notification(for: first) + XCTAssertEqual(content?.title, "Automation stopped unexpectedly") + XCTAssertEqual(content?.body, "A verified user automation exited unexpectedly.") + XCTAssertNil(policy.notification(for: first), "The same verified event must be deduplicated.") + XCTAssertNotNil(policy.notification(for: second)) + XCTAssertNotNil(policy.notification(for: third)) + XCTAssertNotNil( + policy.notification(for: first), + "The oldest event identity should be eligible again after bounded eviction." + ) + + XCTAssertEqual( + policy.setPreference(.repeatedFailure, isEnabled: true), + .none, + "An already authorized notifier must not prompt for each preference." + ) + } + + func testAuthorizationRequestIsSingleFlightButCanRetryAfterDenial() { + var policy = AutomationNotificationPolicy(maximumRetainedEventIdentities: 8) + + XCTAssertEqual( + policy.setPreference(.unexpectedExit, isEnabled: true), + .requestAuthorization + ) + XCTAssertEqual( + policy.setPreference(.repeatedFailure, isEnabled: true), + .none, + "A second opt-in while the first system prompt is pending must not request again." + ) + policy.recordAuthorizationResult(granted: false) + XCTAssertEqual( + policy.setPreference(.crossedLongRunningThreshold, isEnabled: true), + .requestAuthorization, + "A later direct opt-in may retry after denial." + ) + } + + private func unexpectedExit(label: String, pid: Int32) -> AutomationEvent { + .unexpectedExit( + recordID: AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: label, + sourcePath: "/Users/test/Library/LaunchAgents/\(label).plist" + ), + process: ProcessIdentity( + pid: pid, + birthToken: ProcessBirthToken(seconds: UInt64(pid), microseconds: 0) + ) + ) + } +} diff --git a/Tests/DevScopeCoreTests/AutomationPresentationTests.swift b/Tests/DevScopeCoreTests/AutomationPresentationTests.swift new file mode 100644 index 0000000..cf1f149 --- /dev/null +++ b/Tests/DevScopeCoreTests/AutomationPresentationTests.swift @@ -0,0 +1,988 @@ +import XCTest +@testable import DevScopeCore + +final class AutomationPresentationTests: XCTestCase { + func testNewestContextProjectsOneCoherentInventoryAndThresholdGeneration() { + let firstContext = AutomationPresentationContext( + inventory: Fixtures.inventoryGeneration7, + longRunningThreshold: 14_400 + ) + let nextInventory = AutomationInventorySnapshot( + generation: 8, + records: [], + health: [:], + refreshedAt: Date(timeIntervalSince1970: 20_000) + ) + let newestContext = AutomationPresentationContext( + inventory: nextInventory, + longRunningThreshold: 18_000 + ) + + let first = firstContext.build( + processes: [Fixtures.runningBackup], + now: Date(timeIntervalSince1970: 10_000) + ) + let newest = newestContext.build( + processes: [Fixtures.runningBackup], + now: Date(timeIntervalSince1970: 10_002) + ) + + XCTAssertEqual(first.inventory.generation, 7) + XCTAssertEqual(first.longRunningProcessIDs, [Fixtures.runningBackup.pid]) + XCTAssertEqual(newest.inventory.generation, 8) + XCTAssertTrue(newest.linksByProcessID.isEmpty) + XCTAssertTrue(newest.longRunningProcessIDs.isEmpty) + } + + func testPresentationContextSanitizesInvalidThresholdsAtTheBoundary() { + XCTAssertEqual( + AutomationPresentationContext( + inventory: Fixtures.inventoryGeneration7, + longRunningThreshold: -.infinity + ).longRunningThreshold, + AutomationPresentationContext.defaultLongRunningThreshold + ) + XCTAssertEqual( + AutomationPresentationContext( + inventory: Fixtures.inventoryGeneration7, + longRunningThreshold: -1 + ).longRunningThreshold, + 0 + ) + } + + + func testBuildKeepsInventoryLinksAndIndependentLongRunningStateCoherent() { + let unautomated = DevProcess( + pid: 77, parentPID: 1, executable: "/bin/other", command: "/bin/other", + resourceUsage: DevProcessResourceUsage( + cpuPercent: 0, residentMemoryBytes: 0, elapsedTime: "04:00:00" + ) + ) + + let snapshot = AutomationPresentationSnapshot.build( + inventory: Fixtures.inventoryGeneration7, + processes: [Fixtures.runningBackup, unautomated], + longRunningThreshold: 14_400, + now: Date(timeIntervalSince1970: 10_000) + ) + + XCTAssertEqual(snapshot.inventory.generation, 7) + XCTAssertEqual(snapshot.inventory.records.first?.state, .running) + XCTAssertEqual(snapshot.linksByProcessID[Fixtures.runningBackup.pid]?.recordID, Fixtures.userAgent.id) + XCTAssertEqual( + AutomationPresentation.uniquelyStrongLinkedProcessIdentities( + for: Fixtures.userAgent.id, + linksByProcessID: snapshot.allLinksByProcessID + ), + [ProcessIdentity(process: Fixtures.runningBackup)] + ) + XCTAssertEqual(snapshot.longRunningProcessIDs, [Fixtures.runningBackup.pid, unautomated.pid]) + XCTAssertNil(snapshot.processIdentitiesByID[unautomated.pid]?.birthToken) + XCTAssertTrue(snapshot.isProcessSnapshotComplete) + } + + func testPresentationPreservesEveryStrongRecordLinkForOneProcess() { + let launchAgent = copiedRecord( + Fixtures.userAgent, + label: Fixtures.userAgent.label, + ownership: .user, + state: .idle, + sourceKind: .launchAgent + ) + let launchDaemon = copiedRecord( + Fixtures.userAgent, + label: Fixtures.userAgent.label, + ownership: .user, + state: .idle, + sourceKind: .launchDaemon + ) + let snapshot = AutomationPresentationSnapshot.build( + inventory: AutomationInventorySnapshot( + generation: 9, + records: [launchAgent, launchDaemon], + health: [:], + refreshedAt: Date(timeIntervalSince1970: 9) + ), + processes: [Fixtures.runningBackup], + longRunningThreshold: 14_400, + now: Date(timeIntervalSince1970: 10_000) + ) + + XCTAssertEqual( + Set(snapshot.allLinksByProcessID[Fixtures.runningBackup.pid]?.map(\.recordID) ?? []), + [launchAgent.id, launchDaemon.id] + ) + XCTAssertNotNil(snapshot.linksByProcessID[Fixtures.runningBackup.pid]) + XCTAssertEqual( + AutomationPresentation.uniquelyStrongLinkedProcessIdentities( + for: launchAgent.id, + linksByProcessID: snapshot.allLinksByProcessID + ), + Set() + ) + } + + func testAmbiguousNilLabelProvenancePublishesNoControllablePresentationLink() { + let first = copiedRecord( + Fixtures.userAgent, + label: "com.example.first", + ownership: .user, + state: .idle, + sourceKind: .launchAgent + ) + let second = copiedRecord( + Fixtures.userAgent, + label: "com.example.second", + ownership: .user, + state: .idle, + sourceKind: .launchAgent + ) + let inventory = AutomationInventorySnapshot( + generation: 1, + records: [first, second], + health: [:], + refreshedAt: Date(timeIntervalSince1970: 10_000) + ) + + let snapshot = AutomationPresentationSnapshot.build( + inventory: inventory, + processes: [Fixtures.runningBackup], + longRunningThreshold: 14_400, + now: Date(timeIntervalSince1970: 10_000) + ) + + XCTAssertTrue(snapshot.linksByProcessID.isEmpty) + XCTAssertEqual(snapshot.inventory.records.map(\.state), [.idle, .idle]) + } + + func testThresholdOnlyChangeRecomputesDurationWithoutChangingLinksOrInventory() { + let first = AutomationPresentationSnapshot.build( + inventory: Fixtures.inventoryGeneration7, processes: [Fixtures.runningBackup], + longRunningThreshold: 14_400, now: Date(timeIntervalSince1970: 10_000) + ) + let second = AutomationPresentationSnapshot.build( + inventory: Fixtures.inventoryGeneration7, processes: [Fixtures.runningBackup], + longRunningThreshold: 18_000, now: Date(timeIntervalSince1970: 10_000) + ) + + XCTAssertEqual(first.inventory, second.inventory) + XCTAssertEqual(first.linksByProcessID, second.linksByProcessID) + XCTAssertEqual(first.longRunningProcessIDs, [Fixtures.runningBackup.pid]) + XCTAssertTrue(second.longRunningProcessIDs.isEmpty) + } + + func testRecycledPIDCannotRetainOldPresentationLinkIdentity() { + let old = AutomationPresentationSnapshot.build( + inventory: Fixtures.inventoryGeneration7, processes: [Fixtures.runningBackup], + longRunningThreshold: 14_400, now: Date(timeIntervalSince1970: 10_000) + ) + let replacement = DevProcess( + pid: Fixtures.runningBackup.pid, parentPID: 1, executable: "/bin/other", + command: "/bin/other", birthToken: ProcessBirthToken(seconds: 20_000, microseconds: 9) + ) + let current = AutomationPresentationSnapshot.build( + inventory: Fixtures.inventoryGeneration7, processes: [replacement], + longRunningThreshold: 14_400, now: Date(timeIntervalSince1970: 20_001) + ) + + XCTAssertNotNil(old.linksByProcessID[replacement.pid]) + XCTAssertNil(current.linksByProcessID[replacement.pid]) + XCTAssertEqual(current.processIdentitiesByID[replacement.pid]?.birthToken, replacement.birthToken) + } + + func testDuplicatePIDRowsAlwaysChooseNewestBirthRegardlessOfInputOrder() { + let old = Fixtures.runningBackup + let newest = DevProcess( + pid: old.pid, parentPID: 1, executable: "/bin/other", command: "/bin/other", + argumentVector: ["/bin/other"], + resourceUsage: DevProcessResourceUsage( + cpuPercent: 0, residentMemoryBytes: 0, elapsedTime: "04:00:01" + ), + birthToken: ProcessBirthToken(seconds: 40_000, microseconds: 1) + ) + + for processes in [[old, newest], [newest, old]] { + let snapshot = AutomationPresentationSnapshot.build( + inventory: Fixtures.inventoryGeneration7, processes: processes, + longRunningThreshold: 14_400, now: Date(timeIntervalSince1970: 40_001) + ) + XCTAssertNil(snapshot.linksByProcessID[old.pid]) + XCTAssertEqual(snapshot.processIdentitiesByID[old.pid]?.birthToken, newest.birthToken) + XCTAssertEqual(snapshot.longRunningProcessIdentities, [ProcessIdentity(process: newest)]) + } + } + + func testDuplicatePIDRowsWithSameBirthButConflictingMetadataFailClosed() { + let first = Fixtures.runningBackup + let conflicting = DevProcess( + pid: first.pid, parentPID: first.parentPID, executable: first.executable, + command: first.command, argumentVector: [first.executable, "different"], + resourceUsage: first.resourceUsage, birthToken: first.birthToken + ) + + let snapshot = AutomationPresentationSnapshot.build( + inventory: Fixtures.inventoryGeneration7, processes: [first, conflicting], + longRunningThreshold: 14_400, now: Date(timeIntervalSince1970: 10_001) + ) + + XCTAssertTrue(snapshot.linksByProcessID.isEmpty) + XCTAssertTrue(snapshot.processIdentitiesByID.isEmpty) + XCTAssertTrue(snapshot.longRunningProcessIDs.isEmpty) + } + + func testFiltersExcludeAppleByDefaultAndCombineExactFacetsDeterministically() { + let apple = copiedRecord( + Fixtures.userAgent, label: "com.apple.synthetic", ownership: .appleSystem, + state: .idle, sourceKind: .launchDaemon + ) + let user = copiedRecord( + Fixtures.userAgent, label: "com.example.weekly-report", ownership: .user, + state: .idle, sourceKind: .launchAgent + ) + + XCTAssertEqual(AutomationPresentation.filtered( + [apple, user], source: .launchd, state: .idle, ownership: .all, + searchText: "weekly", includeAppleSystemServices: false + ).map(\.label), ["com.example.weekly-report"]) + XCTAssertEqual(AutomationPresentation.filtered( + [apple, user], source: .launchd, state: .idle, ownership: .all, + searchText: "", includeAppleSystemServices: true + ).map(\.label), ["com.apple.synthetic", "com.example.weekly-report"]) + } + + func testInventoryCountExplainsAppleServicesHiddenByPolicy() { + let count = AutomationPresentation.inventoryCount( + visibleCount: 69, + eligibleCount: 69, + totalCount: 983 + ) + + XCTAssertEqual(count.primaryText, "69 automations") + XCTAssertEqual(count.contextText, "914 Apple system services hidden") + XCTAssertEqual( + count.accessibilityLabel, + "69 automations. 914 Apple system services hidden. " + + "In Settings, open Automations and enable Include Apple System Services to review them." + ) + } + + func testInventoryCountSeparatesActiveFiltersFromPolicyHiddenServices() { + let count = AutomationPresentation.inventoryCount( + visibleCount: 5, + eligibleCount: 69, + totalCount: 983 + ) + + XCTAssertEqual(count.primaryText, "5 of 69 automations") + XCTAssertEqual(count.contextText, "914 Apple system services hidden") + } + + func testInventoryCountOmitsHiddenContextWhenAppleServicesAreIncluded() { + let count = AutomationPresentation.inventoryCount( + visibleCount: 983, + eligibleCount: 983, + totalCount: 983 + ) + + XCTAssertEqual(count.primaryText, "983 automations") + XCTAssertNil(count.contextText) + XCTAssertEqual(count.accessibilityLabel, "983 automations") + } + + func testInventoryCountUsesSingularGrammarAndClampsInconsistentInputs() { + XCTAssertEqual( + AutomationPresentation.inventoryCount( + visibleCount: 1, + eligibleCount: 1, + totalCount: 1 + ).primaryText, + "1 automation" + ) + XCTAssertEqual( + AutomationPresentation.inventoryCount( + visibleCount: 20, + eligibleCount: 10, + totalCount: 5 + ).primaryText, + "5 automations" + ) + XCTAssertEqual( + AutomationPresentation.inventoryCount( + visibleCount: 1, + eligibleCount: 1, + totalCount: 2 + ).contextText, + "1 Apple system service hidden" + ) + } + + func testThirdPartyOwnershipFilterIncludesManagedInspectionOnlyDefinitions() { + let thirdParty = copiedRecord( + Fixtures.userAgent, label: "third", ownership: .thirdPartySystem, + state: .idle, sourceKind: .launchAgent + ) + let managed = copiedRecord( + Fixtures.userAgent, label: "managed", ownership: .managed, + state: .idle, sourceKind: .launchAgent + ) + let user = copiedRecord( + Fixtures.userAgent, label: "user", ownership: .user, + state: .idle, sourceKind: .launchAgent + ) + + XCTAssertEqual( + AutomationPresentation.filtered( + [thirdParty, managed, user], source: .all, state: nil, ownership: .thirdParty, + searchText: "", includeAppleSystemServices: false + ).map(\.label), + ["managed", "third"] + ) + } + + func testAutomatedAndLongRunningBadgesRemainIndependent() { + XCTAssertEqual( + AutomationPresentation.badges(isAutomated: true, isLongRunning: false, elapsed: "00:20:00"), + [.automated] + ) + XCTAssertEqual( + AutomationPresentation.badges(isAutomated: false, isLongRunning: true, elapsed: "05:00:00"), + [.longRunning("5h")] + ) + XCTAssertEqual( + AutomationPresentation.badges(isAutomated: true, isLongRunning: true, elapsed: "04:00:00"), + [.automated, .longRunning("4h")] + ) + } + + func testActivityTypeFiltersKeepAutomatedAndLongRunningIndependentAndCombinable() { + XCTAssertTrue(AutomationActivityTypeFilter.all.matches(isAutomated: false, isLongRunning: false)) + XCTAssertTrue(AutomationActivityTypeFilter.automated.matches(isAutomated: true, isLongRunning: false)) + XCTAssertFalse(AutomationActivityTypeFilter.automated.matches(isAutomated: false, isLongRunning: true)) + XCTAssertTrue(AutomationActivityTypeFilter.longRunning.matches(isAutomated: false, isLongRunning: true)) + XCTAssertFalse(AutomationActivityTypeFilter.both.matches(isAutomated: true, isLongRunning: false)) + XCTAssertTrue(AutomationActivityTypeFilter.both.matches(isAutomated: true, isLongRunning: true)) + } + + func testAutomationSearchCoversArgumentsSourceScheduleAndOwningApplication() { + let record = copiedRecord( + Fixtures.userAgent, + label: "com.example.report", + ownership: .user, + state: .idle, + sourceKind: .launchAgent, + displayName: "Weekly Report", + providerBundleIdentifier: "com.example.owner", + arguments: ["--workspace", "North Star"], + sourceURL: URL(fileURLWithPath: "/Users/test/Library/LaunchAgents/report.plist"), + schedule: AutomationSchedule(triggers: [.calendar("Monday")], summary: "Every Monday") + ) + + for query in ["weekly", "north star", "report.plist", "every monday", "example.owner"] { + XCTAssertEqual( + AutomationPresentation.filtered( + [record], source: .all, state: nil, ownership: .all, + searchText: query, includeAppleSystemServices: false + ).map(\.id), + [record.id], + "Expected \(query) to match the complete automation search surface." + ) + } + } + + func testManagementActionsFollowExactCapabilityDecisionAndConcreteBackups() { + let backup = AutomationBackup( + id: .init(rawValue: UUID(uuidString: "11111111-1111-1111-1111-111111111111")!), + recordID: Fixtures.userAgent.id, + sourceURL: URL(fileURLWithPath: "/redacted"), + backupURL: URL(fileURLWithPath: "/redacted"), + checksum: "checksum", + createdAt: Date(timeIntervalSince1970: 1) + ) + let decision = AutomationCapabilityDecision( + capabilities: [.startNow, .disable, .exportRecord, .remove, .restore], + reason: nil + ) + + XCTAssertEqual( + AutomationManagementPresentation.actions( + decision: decision, + backups: [backup], + record: Fixtures.userAgent + ), + [.startNow, .disable, .exportRedacted, .exportUnredacted, .remove, .restore(backup.id)] + ) + XCTAssertEqual( + AutomationManagementPresentation.actions( + decision: AutomationCapabilityDecision( + capabilities: [.exportRecord], + reason: "Modern background items must be managed in System Settings." + ), + backups: [backup], + record: Fixtures.userAgent + ), + [.exportRedacted, .exportUnredacted] + ) + + let protected = copiedRecord( + Fixtures.userAgent, + label: "background", + ownership: .thirdPartySystem, + state: .idle, + sourceKind: .serviceManagement, + kind: .backgroundItem + ) + XCTAssertEqual( + AutomationManagementPresentation.actions( + decision: AutomationCapabilityDecision(capabilities: [.exportRecord], reason: "Inspection only."), + backups: [], + record: protected + ), + [.exportRedacted] + ) + } + + func testManagementActionChromeKeepsLifecycleActionsPinnedWithRedundantSemanticCues() { + let expectations: [( + action: AutomationManagementAction, + placement: AutomationManagementActionPlacement, + emphasis: AutomationManagementActionEmphasis, + symbol: String + )] = [ + (.startNow, .pinned, .positive, "play.fill"), + (.stopCurrentRun, .pinned, .destructive, "stop.fill"), + (.enable, .pinned, .positive, "checkmark.circle"), + (.disable, .pinned, .caution, "pause.circle"), + (.disableAndStop, .pinned, .destructive, "stop.circle.fill"), + (.edit, .overflow, .neutral, "square.and.pencil"), + (.duplicate, .overflow, .neutral, "plus.square.on.square"), + (.importRecord, .overflow, .neutral, "square.and.arrow.down"), + (.exportRedacted, .overflow, .neutral, "square.and.arrow.up"), + (.exportUnredacted, .overflow, .caution, "lock.open.fill"), + (.remove, .overflow, .destructive, "trash"), + (.restore(.init(rawValue: UUID())), .history, .neutral, "clock.arrow.circlepath"), + ] + + for expectation in expectations { + XCTAssertEqual(expectation.action.placement, expectation.placement) + XCTAssertEqual(expectation.action.emphasis, expectation.emphasis) + XCTAssertEqual(expectation.action.systemImage, expectation.symbol) + } + } + + func testConfirmationPoliciesStateExactLifecycleConsequences() throws { + let stop = try XCTUnwrap(AutomationManagementPresentation.confirmation( + for: .stopCurrentRun, + record: Fixtures.userAgent + )) + XCTAssertTrue(stop.consequence.contains("exact launchd service")) + XCTAssertTrue(stop.consequence.contains("does not disable")) + XCTAssertFalse(stop.consequence.contains("KeepAlive")) + + let cron = copiedRecord( + Fixtures.userAgent, + label: "nightly", + ownership: .user, + state: .running, + sourceKind: .crontab, + kind: .cron + ) + XCTAssertTrue(try XCTUnwrap(AutomationManagementPresentation.confirmation( + for: .stopCurrentRun, + record: cron + )).consequence.contains("strongly linked")) + + let start = try XCTUnwrap(AutomationManagementPresentation.confirmation( + for: .startNow, + record: Fixtures.userAgent + )) + XCTAssertTrue(start.consequence.contains("immediate launch")) + + XCTAssertTrue(try XCTUnwrap(AutomationManagementPresentation.confirmation( + for: .enable, + record: Fixtures.userAgent + )).consequence.contains("does not start")) + XCTAssertTrue(try XCTUnwrap(AutomationManagementPresentation.confirmation( + for: .disable, + record: Fixtures.userAgent + )).consequence.contains("does not stop")) + XCTAssertTrue(try XCTUnwrap(AutomationManagementPresentation.confirmation( + for: .disableAndStop, + record: Fixtures.userAgent + )).consequence.contains("exact launchd service")) + XCTAssertTrue( + AutomationManagementPresentation.helpText( + for: .stopCurrentRun, + record: Fixtures.userAgent + ).contains("exact launchd service") + ) + XCTAssertTrue( + AutomationManagementPresentation.helpText( + for: .disableAndStop, + record: Fixtures.userAgent + ).contains("exact launchd service") + ) + XCTAssertTrue( + AutomationManagementPresentation.helpText( + for: .stopCurrentRun, + record: cron + ).contains("strongly linked") + ) + + let remove = try XCTUnwrap(AutomationManagementPresentation.confirmation( + for: .remove, + record: Fixtures.userAgent + )) + XCTAssertEqual(remove.requiredLabel, Fixtures.userAgent.label) + XCTAssertTrue(remove.consequence.contains("restoration manifest")) + XCTAssertFalse(remove.isSatisfiedByLabel("\(Fixtures.userAgent.label) ")) + XCTAssertTrue(remove.isSatisfiedByLabel(Fixtures.userAgent.label)) + } + + func testUnredactedExportAndConcreteRestoreRequireExplicitConfirmation() throws { + let backup = AutomationBackup( + id: .init(rawValue: UUID(uuidString: "22222222-2222-2222-2222-222222222222")!), + recordID: Fixtures.userAgent.id, + sourceURL: URL(fileURLWithPath: "/redacted"), + backupURL: URL(fileURLWithPath: "/redacted"), + checksum: "checksum", + createdAt: Date(timeIntervalSince1970: 10) + ) + let export = try XCTUnwrap(AutomationManagementPresentation.confirmation( + for: .exportUnredacted, + record: Fixtures.userAgent + )) + XCTAssertTrue(export.consequence.contains("secrets")) + + let restore = try XCTUnwrap(AutomationManagementPresentation.confirmation( + for: .restore(backup.id), + record: Fixtures.userAgent, + backups: [backup] + )) + XCTAssertTrue(restore.consequence.contains("verified backup")) + XCTAssertTrue(restore.consequence.contains(backup.createdAt.formatted(date: .abbreviated, time: .shortened))) + } + + func testOperationResultPresentationRedactsSecretsAndKeepsPartialRecoveryVisible() { + let result = AutomationOperationResult( + operation: .disable, + status: .partialFailure("launchctl failed --token=super-secret"), + appliedSteps: ["raw output --password=hunter2"], + verificationEvidence: ["API_KEY=hidden"], + rollback: .failed("rollback --secret=also-hidden"), + manualRecovery: "Retry with --access-token=last-secret" + ) + + let presentation = AutomationManagementPresentation.result(result) + XCTAssertEqual(presentation.title, "Partially completed") + XCTAssertTrue(presentation.detail.contains("")) + XCTAssertFalse(presentation.detail.contains("super-secret")) + XCTAssertFalse(presentation.recoveryGuidance?.contains("last-secret") ?? true) + XCTAssertFalse(presentation.detail.contains("hunter2")) + XCTAssertFalse(presentation.detail.contains("hidden")) + let completePresentation = String(describing: presentation) + for secret in ["super-secret", "hunter2", "hidden", "also-hidden", "last-secret"] { + XCTAssertFalse(completePresentation.contains(secret)) + } + } + + func testOperationResultPresentationKeepsRedactedAppliedVerificationAndRecoveryEvidence() { + let backup = AutomationBackup( + id: AutomationBackup.ID(rawValue: UUID(uuidString: "AAAA0000-0000-0000-0000-000000000001")!), + recordID: Fixtures.userAgent.id, + sourceURL: URL(fileURLWithPath: "/private/source.plist"), + backupURL: URL(fileURLWithPath: "/private/backup.plist"), + checksum: String(repeating: "a", count: 64), + createdAt: Date(timeIntervalSince1970: 20_000) + ) + let result = AutomationOperationResult( + operation: .edit(AutomationEditPayload( + label: "fixture", executable: "/bin/true", arguments: [], environment: [:], + workingDirectory: nil, schedule: Fixtures.userAgent.schedule, + rawRepresentation: nil + )), + status: .succeeded, + appliedSteps: ["Applied --token=private-value"], + verificationEvidence: ["Verified API_KEY=private-value"], + rollback: .restored(backup.id), + manualRecovery: nil, + backup: backup, + fileMutationEvidence: AutomationFilePartialMutation( + kind: .replace, + commitState: .unknown, + observedFiles: [], + recoveryHandle: nil, + recoveryHandles: [], + resultURL: nil + ) + ) + + let presentation = AutomationManagementPresentation.result(result) + XCTAssertEqual(presentation.appliedEvidence, ["Applied --token="]) + XCTAssertEqual(presentation.verificationEvidence, ["Verified API_KEY="]) + XCTAssertTrue(presentation.rollbackEvidence.contains("restored")) + XCTAssertTrue(presentation.rollbackEvidence.contains("AAAA0000")) + XCTAssertTrue(presentation.backupEvidence?.contains(String(repeating: "a", count: 12)) == true) + XCTAssertEqual( + presentation.mutationEvidence, + ["Source replacement outcome is unknown; 0 observed files and 0 recovery handles were retained."] + ) + XCTAssertFalse(String(describing: presentation).contains("private-value")) + XCTAssertFalse(String(describing: presentation).contains("/private/")) + } + + func testOperationResultPresentationExposesEveryExactRecoveryLocation() throws { + let directoryURL = URL(fileURLWithPath: "/tmp/devscope-recovery", isDirectory: true) + let directory = try XCTUnwrap(AutomationDirectoryAuthorization( + directoryURL: directoryURL, + resourceIdentifier: "directory-id" + )) + let firstURL = directoryURL.appendingPathComponent(".devscope-recovery-first") + let secondURL = directoryURL.appendingPathComponent(".devscope-recovery-second") + let first = try XCTUnwrap(AutomationFileAuthorization( + fileURL: firstURL, + directory: directory, + expectation: .existing(resourceIdentifier: "first-id") + )) + let second = try XCTUnwrap(AutomationFileAuthorization( + fileURL: secondURL, + directory: directory, + expectation: .existing(resourceIdentifier: "second-id") + )) + let result = AutomationOperationResult( + operation: .remove, + status: .partialFailure("Automatic recovery did not complete."), + appliedSteps: [], + verificationEvidence: [], + rollback: .notNeeded, + manualRecovery: nil, + fileMutationEvidence: AutomationFilePartialMutation( + kind: .remove, + commitState: .unknown, + observedFiles: [], + recoveryHandle: first, + recoveryHandles: [second], + resultURL: nil + ) + ) + + let guidance = try XCTUnwrap(AutomationManagementPresentation.result(result).recoveryGuidance) + + XCTAssertTrue(guidance.contains(firstURL.path), guidance) + XCTAssertTrue(guidance.contains(secondURL.path), guidance) + } + + func testCronConfirmationDisplaysRedactedCommandButBuildsExactTypedOperation() throws { + let cron = copiedRecord( + Fixtures.userAgent, + label: "nightly", + ownership: .user, + state: .idle, + sourceKind: .crontab, + kind: .cron, + commandSignature: "/usr/bin/curl --token=super-secret https://example.test" + ) + + let policy = try XCTUnwrap(AutomationManagementPresentation.confirmation( + for: .startNow, + record: cron + )) + XCTAssertEqual( + policy.displayedCommand, + "/usr/bin/curl --token= https://example.test" + ) + XCTAssertEqual( + AutomationManagementPresentation.confirmedOperation(for: .startNow, record: cron), + .confirmedRunToCompletion(AutomationRunToCompletionConfirmation( + recordID: cron.id, + sourceChecksum: cron.sourceChecksum, + exactCommand: "/usr/bin/curl --token=super-secret https://example.test" + )) + ) + } + + func testEditorValidationRequiresApprovedDuplicateDestinationAndValidRawPlist() { + XCTAssertEqual( + AutomationEditorPresentation.validationMessage( + record: Fixtures.userAgent, + purposeIsDuplicate: true, + label: "copy", + executable: "/bin/true", + arguments: [], + environment: [:], + schedule: Fixtures.userAgent.schedule, + usesRawRepresentation: false, + rawData: nil, + duplicateDestination: nil + ), + "Choose a label that resolves to a distinct approved destination." + ) + XCTAssertEqual( + AutomationEditorPresentation.validationMessage( + record: Fixtures.userAgent, + purposeIsDuplicate: false, + label: "valid", + executable: "/bin/true", + arguments: [], + environment: [:], + schedule: Fixtures.userAgent.schedule, + usesRawRepresentation: true, + rawData: Data("not a plist".utf8), + duplicateDestination: nil + ), + "The raw property list is not valid." + ) + } + + func testEditorRawLaunchdValidationRejectsMismatchedLabelAndCommand() throws { + let destination = URL(fileURLWithPath: "/Users/test/Library/LaunchAgents/copy.plist") + func plist(_ label: String, _ program: String) throws -> Data { + try PropertyListSerialization.data( + fromPropertyList: ["Label": label, "Program": program], + format: .xml, + options: 0 + ) + } + + XCTAssertEqual( + AutomationEditorPresentation.validationMessage( + record: Fixtures.userAgent, purposeIsDuplicate: true, + label: "expected", executable: "/bin/true", arguments: [], environment: [:], + schedule: Fixtures.userAgent.schedule, usesRawRepresentation: true, + rawData: try plist("different", "/bin/true"), duplicateDestination: destination + ), + "The raw property-list label does not match the reviewed label." + ) + XCTAssertEqual( + AutomationEditorPresentation.validationMessage( + record: Fixtures.userAgent, purposeIsDuplicate: true, + label: "expected", executable: "/bin/true", arguments: [], environment: [:], + schedule: Fixtures.userAgent.schedule, usesRawRepresentation: true, + rawData: try plist("expected", "/bin/false"), duplicateDestination: destination + ), + "The raw property-list command does not match the reviewed executable and arguments." + ) + } + + func testEditorRawCronValidationRequiresReviewedEntryAndRetainedOriginalForDuplicate() { + let schedule = AutomationSchedule(triggers: [.cron("* * * * *")], summary: "Every minute") + let cron = copiedRecord( + Fixtures.userAgent, label: "cron", ownership: .user, state: .idle, + sourceKind: .crontab, kind: .cron, schedule: schedule, + commandSignature: "/bin/true --old" + ) + let destination = URL(fileURLWithPath: "/tmp/cron-source") + + XCTAssertEqual( + AutomationEditorPresentation.validationMessage( + record: cron, purposeIsDuplicate: true, label: "cron-copy", + executable: "/bin/true", arguments: ["--new"], environment: cron.environment, schedule: schedule, + usesRawRepresentation: true, + rawData: Data("DEVSCOPE_FIXTURE=synthetic\n* * * * * /bin/true --new\n".utf8), + duplicateDestination: destination + ), + "The duplicate crontab must retain the selected entry and add one reviewed entry." + ) + XCTAssertNil(AutomationEditorPresentation.validationMessage( + record: cron, purposeIsDuplicate: true, label: "cron-copy", + executable: "/bin/true", arguments: ["--new"], environment: cron.environment, schedule: schedule, + usesRawRepresentation: true, + rawData: Data("DEVSCOPE_FIXTURE=synthetic\n* * * * * /bin/true --old\n* * * * * /bin/true --new\n".utf8), + duplicateDestination: destination + )) + } + + func testEditorParsesEditableEnvironmentAndEverySupportedScheduleToken() throws { + let environment = try XCTUnwrap(AutomationEditorPresentation.environment( + from: "API_URL=https://example.test?a=b\nTOKEN=value\n" + )) + XCTAssertEqual(environment, ["API_URL": "https://example.test?a=b", "TOKEN": "value"]) + XCTAssertEqual( + AutomationEditorPresentation.environmentText(for: environment), + "API_URL=https://example.test?a=b\nTOKEN=value" + ) + + let schedule = try XCTUnwrap(AutomationEditorPresentation.schedule( + from: "run-at-load\ninterval 900\nkeep-alive\ncalendar Mondays at 08:30" + )) + XCTAssertEqual(schedule.triggers, [ + .runAtLoad, + .keepAlive, + .interval(seconds: 900), + .calendar("Mondays at 08:30"), + ]) + XCTAssertEqual( + AutomationEditorPresentation.scheduleText(for: schedule), + "run-at-load\nkeep-alive\ninterval 900\ncalendar Mondays at 08:30" + ) + XCTAssertNil(AutomationEditorPresentation.environment(from: "INVALID")) + XCTAssertNil(AutomationEditorPresentation.schedule(from: "interval 0")) + XCTAssertNil(AutomationEditorPresentation.schedule(from: "unknown trigger")) + } + + func testEditorScheduleNormalizesManagerRoundTripAndRejectsContradictoryCombinations() throws { + XCTAssertEqual( + try XCTUnwrap(AutomationEditorPresentation.schedule(from: "keep-alive")).triggers, + [.runAtLoad, .keepAlive] + ) + XCTAssertEqual( + try XCTUnwrap(AutomationEditorPresentation.schedule(from: "at-login")).triggers, + [.runAtLoad] + ) + XCTAssertNil(AutomationEditorPresentation.schedule(from: "on-demand\ninterval 60")) + XCTAssertNil(AutomationEditorPresentation.schedule(from: "interval 60\ninterval 120")) + XCTAssertNil(AutomationEditorPresentation.schedule(from: "cron * * * * *\nrun-at-load")) + XCTAssertEqual( + try XCTUnwrap(AutomationEditorPresentation.schedule( + from: "calendar First\ninterval 60\ncalendar Second" + )).triggers, + [.interval(seconds: 60), .calendar("First"), .calendar("Second")] + ) + XCTAssertEqual( + AutomationEditorPresentation.scheduleText(for: AutomationSchedule( + triggers: [.atLogin], + summary: "At login" + )), + "run-at-load" + ) + } + + func testRawLaunchdValidationAcceptsChangedReviewedEnvironmentAndSchedule() throws { + let changedSchedule = AutomationSchedule( + triggers: [.runAtLoad, .interval(seconds: 900)], + summary: "At load, every 15 minutes" + ) + let changedEnvironment = ["MODE": "audit"] + let raw = try PropertyListSerialization.data( + fromPropertyList: [ + "Label": "edited", + "ProgramArguments": ["/bin/true", "--edited"], + "EnvironmentVariables": changedEnvironment, + "RunAtLoad": true, + "StartInterval": 900, + ], + format: .xml, + options: 0 + ) + + XCTAssertNil(AutomationEditorPresentation.validationMessage( + record: Fixtures.userAgent, + purposeIsDuplicate: false, + label: "edited", + executable: "/bin/true", + arguments: ["--edited"], + environment: changedEnvironment, + schedule: changedSchedule, + usesRawRepresentation: true, + rawData: raw, + duplicateDestination: Fixtures.userAgent.sourceURL + )) + XCTAssertNotNil(AutomationEditorPresentation.validationMessage( + record: Fixtures.userAgent, + purposeIsDuplicate: false, + label: "edited", + executable: "/bin/true", + arguments: ["--edited"], + environment: [:], + schedule: changedSchedule, + usesRawRepresentation: true, + rawData: raw, + duplicateDestination: Fixtures.userAgent.sourceURL + )) + } + + func testImportPreviewValidatesKindAndExactDestinationBeforeApply() throws { + let data = try PropertyListSerialization.data( + fromPropertyList: ["Label": "com.example.imported", "Program": "/bin/true"], + format: .xml, + options: 0 + ) + let destination = URL(fileURLWithPath: "/Users/test/Library/LaunchAgents/imported.plist") + let valid = AutomationImportPresentation( + data: data, + expectedRecord: Fixtures.userAgent, + destination: destination + ) + XCTAssertTrue(valid.canApply) + XCTAssertEqual(valid.destination, destination) + XCTAssertTrue(valid.summary.contains("com.example.imported")) + + let invalid = AutomationImportPresentation( + data: Data("* * * * * /bin/true".utf8), + expectedRecord: Fixtures.userAgent, + destination: destination + ) + XCTAssertFalse(invalid.canApply) + XCTAssertEqual(invalid.validationMessage, "The selected file is not a labeled launchd property list.") + } + + func testImportPreviewNamesReplacementTargetOwnershipAndConsequence() throws { + let data = try PropertyListSerialization.data( + fromPropertyList: ["Label": "com.example.imported", "Program": "/bin/true"], + format: .xml, + options: 0 + ) + let presentation = AutomationImportPresentation( + data: data, + expectedRecord: Fixtures.userAgent, + destination: URL(fileURLWithPath: "/Users/test/Library/LaunchAgents/imported.plist") + ) + + XCTAssertEqual(presentation.targetLabel, Fixtures.userAgent.label) + XCTAssertEqual(presentation.targetOwnership, "User owned") + XCTAssertTrue(presentation.consequence.contains("replace")) + XCTAssertTrue(presentation.consequence.contains(Fixtures.userAgent.label)) + XCTAssertTrue(presentation.consequence.contains("checksum")) + } + + func testVisibleSelectionDropsARecordExcludedByFilters() { + let visible = [Fixtures.userAgent.id] + XCTAssertEqual( + AutomationPresentation.resolvedSelection(current: nil, visibleIDs: visible), + Fixtures.userAgent.id + ) + XCTAssertEqual( + AutomationPresentation.resolvedSelection( + current: AutomationRecord.ID(rawValue: "hidden"), + visibleIDs: visible + ), + Fixtures.userAgent.id + ) + XCTAssertEqual( + AutomationPresentation.resolvedSelection(current: Fixtures.userAgent.id, visibleIDs: visible), + Fixtures.userAgent.id + ) + XCTAssertNil(AutomationPresentation.resolvedSelection( + current: Fixtures.userAgent.id, + visibleIDs: [] + )) + } +} + +private func copiedRecord( + _ source: AutomationRecord, + label: String, + ownership: AutomationOwnership, + state: AutomationState, + sourceKind: AutomationSourceKind, + kind: AutomationKind? = nil, + displayName: String? = nil, + providerBundleIdentifier: String? = nil, + arguments: [String]? = nil, + sourceURL: URL? = nil, + schedule: AutomationSchedule? = nil, + commandSignature: String? = nil +) -> AutomationRecord { + AutomationRecord( + id: AutomationRecord.ID(source: sourceKind, ownerUID: 501, label: label, sourcePath: "/\(label)"), + kind: kind ?? source.kind, sourceKind: sourceKind, label: label, displayName: displayName ?? label, + providerBundleIdentifier: providerBundleIdentifier ?? source.providerBundleIdentifier, ownerUID: source.ownerUID, + ownership: ownership, executable: source.executable, arguments: arguments ?? source.arguments, + commandSignature: commandSignature ?? source.commandSignature, environment: source.environment, + workingDirectory: source.workingDirectory, schedule: schedule ?? source.schedule, + sourceURL: sourceURL ?? source.sourceURL, sourceChecksum: source.sourceChecksum, + enabledState: source.enabledState, loadState: source.loadState, + approvalState: source.approvalState, state: state, evidence: source.evidence, + capabilities: source.capabilities, validationFindings: source.validationFindings + ) +} diff --git a/Tests/DevScopeCoreTests/AutomationProcessCorrelatorTests.swift b/Tests/DevScopeCoreTests/AutomationProcessCorrelatorTests.swift new file mode 100644 index 0000000..dc09cc0 --- /dev/null +++ b/Tests/DevScopeCoreTests/AutomationProcessCorrelatorTests.swift @@ -0,0 +1,601 @@ +import XCTest +@testable import DevScopeCore + +final class AutomationProcessCorrelatorTests: XCTestCase { + func testExactExecutableAndArgumentsCreateVerifiedLink() { + let links = AutomationProcessCorrelator.links( + records: [Fixtures.userAgent], + processes: [Fixtures.runningBackup], + now: Date(timeIntervalSince1970: 10_000) + ) + + XCTAssertEqual(links.count, 1) + XCTAssertEqual(links[0].strength, .strong) + XCTAssertEqual(links[0].processIdentity.birthToken, Fixtures.runningBackup.birthToken) + XCTAssertEqual( + links[0].evidence.map(\.detail), + ["Exact executable and arguments with direct PID 1 compatibility"] + ) + } + + func testWeakResemblancePartialArgumentsAndNilBirthFailClosed() { + let cases = [ + DevProcess( + pid: 44, parentPID: 1, executable: "/tmp/backup-helper", + command: "/tmp/backup-helper 14400", + birthToken: ProcessBirthToken(seconds: 4, microseconds: 4) + ), + DevProcess( + pid: 45, parentPID: 1, executable: "/bin/sleep", + command: "/bin/sleep 144000", + birthToken: ProcessBirthToken(seconds: 4, microseconds: 5) + ), + DevProcess( + pid: 46, parentPID: 1, executable: "/bin/sleep", + command: "/bin/sleep 14400" + ), + ] + + XCTAssertTrue(AutomationProcessCorrelator.links( + records: [Fixtures.userAgent], processes: cases, + now: Date(timeIntervalSince1970: 10_000) + ).isEmpty) + } + + func testExactExecutableFailsClosedWithoutExactArgumentVector() { + let process = DevProcess( + pid: 48, parentPID: 1, executable: "/bin/sleep", + command: "/bin/sleep 14400", argumentVector: nil, + birthToken: ProcessBirthToken(seconds: 4, microseconds: 8) + ) + + XCTAssertTrue(AutomationProcessCorrelator.links( + records: [Fixtures.userAgent], processes: [process], + now: Date(timeIntervalSince1970: 10_000) + ).isEmpty) + } + + func testExactExecutableAndArgumentsRejectManualNonLaunchAncestry() { + let manuallyInvoked = DevProcess( + pid: 54, parentPID: 9_001, executable: "/bin/sleep", + command: "/bin/sleep 14400", argumentVector: ["/bin/sleep", "14400"], + birthToken: ProcessBirthToken(seconds: 5, microseconds: 4) + ) + + XCTAssertTrue(AutomationProcessCorrelator.links( + records: [Fixtures.userAgent], processes: [manuallyInvoked], + now: Date(timeIntervalSince1970: 10_000) + ).isEmpty) + } + + func testExactLaunchLabelIsStrongerProvenanceThanDirectPIDOneParentage() { + let exactLabel = DevProcess( + pid: 55, parentPID: 9_002, executable: "/bin/sleep", + command: "/bin/sleep 14400", argumentVector: ["/bin/sleep", "14400"], + birthToken: ProcessBirthToken(seconds: 5, microseconds: 5), + launchLabel: Fixtures.userAgent.label + ) + + let links = AutomationProcessCorrelator.links( + records: [Fixtures.userAgent], processes: [exactLabel], + now: Date(timeIntervalSince1970: 10_000) + ) + + XCTAssertEqual(links.map(\.processIdentity.pid), [55]) + XCTAssertEqual(links[0].evidence.map(\.detail), ["Exact executable, arguments, and launch label"]) + } + + func testMismatchedLaunchLabelRejectsEvenWithDirectPIDOneParentage() { + let wrongLabel = DevProcess( + pid: 56, parentPID: 1, executable: "/bin/sleep", + command: "/bin/sleep 14400", argumentVector: ["/bin/sleep", "14400"], + birthToken: ProcessBirthToken(seconds: 5, microseconds: 6), + launchLabel: "com.example.different" + ) + + XCTAssertTrue(AutomationProcessCorrelator.links( + records: [Fixtures.userAgent], processes: [wrongLabel], + now: Date(timeIntervalSince1970: 10_000) + ).isEmpty) + } + + func testNilLaunchLabelDoesNotChooseBetweenIdenticalDefinitions() { + let first = automationRecord(Fixtures.userAgent, label: "com.example.first") + let second = automationRecord(Fixtures.userAgent, label: "com.example.second") + + let links = AutomationProcessCorrelator.links( + records: [first, second], + processes: [Fixtures.runningBackup], + now: Date(timeIntervalSince1970: 10_000) + ) + + XCTAssertTrue(links.isEmpty) + } + + func testExactLaunchLabelSelectsTheMatchingIdenticalDefinition() { + let first = automationRecord(Fixtures.userAgent, label: "com.example.first") + let second = automationRecord(Fixtures.userAgent, label: "com.example.second") + let labeledProcess = DevProcess( + pid: Fixtures.runningBackup.pid, + parentPID: Fixtures.runningBackup.parentPID, + executable: Fixtures.runningBackup.executable, + command: Fixtures.runningBackup.command, + argumentVector: Fixtures.runningBackup.argumentVector, + birthToken: Fixtures.runningBackup.birthToken, + launchLabel: second.label + ) + + let links = AutomationProcessCorrelator.links( + records: [first, second], + processes: [labeledProcess], + now: Date(timeIntervalSince1970: 10_000) + ) + + XCTAssertEqual(links.map(\.recordID), [second.id]) + } + + func testEmptyRecordArgumentsRejectAnExactExecutableWithExtraArguments() { + let definition = automationRecord(Fixtures.userAgent, arguments: []) + let process = DevProcess( + pid: 49, parentPID: 1, executable: "/bin/sleep", + command: "/bin/sleep --unexpected", + argumentVector: ["/bin/sleep", "--unexpected"], + birthToken: ProcessBirthToken(seconds: 4, microseconds: 9) + ) + + XCTAssertTrue(AutomationProcessCorrelator.links( + records: [definition], processes: [process], + now: Date(timeIntervalSince1970: 10_000) + ).isEmpty) + } + + func testEmbeddedSpaceArgumentDoesNotMatchTwoKernelArguments() { + let definition = automationRecord( + Fixtures.userAgent, executable: "/opt/jobs/exact", arguments: ["folder name"] + ) + let split = DevProcess( + pid: 52, parentPID: 1, executable: "/opt/jobs/exact", + command: "/opt/jobs/exact folder name", + argumentVector: ["/opt/jobs/exact", "folder", "name"], + birthToken: ProcessBirthToken(seconds: 5, microseconds: 2) + ) + let exact = DevProcess( + pid: 53, parentPID: 1, executable: "/opt/jobs/exact", + command: "/opt/jobs/exact folder name", + argumentVector: ["/opt/jobs/exact", "folder name"], + birthToken: ProcessBirthToken(seconds: 5, microseconds: 3) + ) + + XCTAssertEqual(AutomationProcessCorrelator.links( + records: [definition], processes: [split, exact], + now: Date(timeIntervalSince1970: 10_000) + ).map(\.processIdentity.pid), [53]) + } + + func testSingleQuotedBackslashRemainsLiteralDuringExactArgumentMatching() { + let definition = automationRecord( + Fixtures.userAgent, + executable: "/opt/jobs/exact", + arguments: ["folder\\name"] + ) + let process = DevProcess( + pid: 47, parentPID: 1, executable: "/opt/jobs/exact", + command: "/opt/jobs/exact 'folder\\name'", + argumentVector: ["/opt/jobs/exact", "folder\\name"], + birthToken: ProcessBirthToken(seconds: 4, microseconds: 7) + ) + + XCTAssertEqual(AutomationProcessCorrelator.links( + records: [definition], processes: [process], + now: Date(timeIntervalSince1970: 10_000) + ).map(\.processIdentity.pid), [47]) + } + + func testExactBundleIdentityLinksAndNameOrPathInferenceDoesNot() { + let exact = DevProcess( + pid: 50, parentPID: 1, + executable: "/Applications/Owner.app/Contents/MacOS/Other", + command: "/Applications/Owner.app/Contents/MacOS/Other", + argumentVector: ["/Applications/Owner.app/Contents/MacOS/Other"], + birthToken: ProcessBirthToken(seconds: 5, microseconds: 1), + bundleIdentifier: "com.example.devscope-fixture-owner" + ) + let inferredOnly = DevProcess( + pid: 51, parentPID: 1, + executable: "/Applications/com.example.devscope-fixture-owner.app/Contents/MacOS/Owner", + command: "com.example.devscope-fixture-owner", + birthToken: ProcessBirthToken(seconds: 5, microseconds: 2) + ) + + let links = AutomationProcessCorrelator.links( + records: [automationRecord(Fixtures.userAgent, arguments: [])], + processes: [exact, inferredOnly], + now: Date(timeIntervalSince1970: 10_000) + ) + + XCTAssertEqual(links.map(\.processIdentity.pid), [50]) + } + + func testRecycledPIDProducesOnlyTheCurrentBirthIdentity() { + let old = Fixtures.runningBackup + let recycled = DevProcess( + pid: old.pid, parentPID: old.parentPID, executable: old.executable, + command: old.command, argumentVector: old.argumentVector, + resourceUsage: old.resourceUsage, + birthToken: ProcessBirthToken(seconds: 20_000, microseconds: 1) + ) + + let links = AutomationProcessCorrelator.links( + records: [Fixtures.userAgent], processes: [recycled], + now: Date(timeIntervalSince1970: 20_100) + ) + + XCTAssertEqual(links.map(\.processIdentity.birthToken), [recycled.birthToken]) + } + + func testDuplicatePIDRowsUseNewestBirthRegardlessOfInputOrder() { + let old = Fixtures.runningBackup + let newest = DevProcess( + pid: old.pid, parentPID: 1, executable: "/bin/other", command: "/bin/other", + argumentVector: ["/bin/other"], + birthToken: ProcessBirthToken(seconds: 50_000, microseconds: 1) + ) + + XCTAssertTrue(AutomationProcessCorrelator.links( + records: [Fixtures.userAgent], processes: [old, newest], + now: Date(timeIntervalSince1970: 50_001) + ).isEmpty) + XCTAssertTrue(AutomationProcessCorrelator.links( + records: [Fixtures.userAgent], processes: [newest, old], + now: Date(timeIntervalSince1970: 50_001) + ).isEmpty) + } + + func testCronRequiresExactSignatureScheduleMinuteAndCronAncestry() { + var calendar = Calendar(identifier: .gregorian) + calendar.timeZone = TimeZone(secondsFromGMT: 0)! + let now = date(2026, 7, 13, 10, 5, 30, calendar: calendar) + let cron = cronRecord(signature: "/bin/sleep 60", expression: "5 10 * * *") + let daemon = DevProcess( + pid: 100, parentPID: 1, executable: "/usr/sbin/cron", command: "/usr/sbin/cron", + argumentVector: ["/usr/sbin/cron"], + birthToken: ProcessBirthToken(seconds: 1, microseconds: 1) + ) + let shell = DevProcess( + pid: 101, parentPID: 100, executable: "/bin/sh", + command: "/bin/sh -c '/bin/sleep 60'", + argumentVector: ["/bin/sh", "-c", "/bin/sleep 60"], + resourceUsage: usage("00:00:30"), + birthToken: ProcessBirthToken(seconds: 2, microseconds: 1) + ) + let child = DevProcess( + pid: 102, parentPID: 101, executable: "/bin/sleep", command: "/bin/sleep 60", + argumentVector: ["/bin/sleep", "60"], + resourceUsage: usage("00:00:25"), + birthToken: ProcessBirthToken(seconds: 3, microseconds: 1) + ) + + let links = AutomationProcessCorrelator.links( + records: [cron], processes: [daemon, shell, child], now: now, calendar: calendar + ) + + XCTAssertEqual(links.map(\.processIdentity.pid), [101]) + XCTAssertTrue(links.allSatisfy { $0.recordID == cron.id }) + } + + func testCronFailsClosedForWrongMinuteWrongAncestryAndReboot() { + var calendar = Calendar(identifier: .gregorian) + calendar.timeZone = TimeZone(secondsFromGMT: 0)! + let now = date(2026, 7, 13, 10, 6, 30, calendar: calendar) + let daemon = DevProcess( + pid: 199, parentPID: 1, executable: "/usr/sbin/cron", command: "/usr/sbin/cron", + argumentVector: ["/usr/sbin/cron"], + birthToken: ProcessBirthToken(seconds: 4, microseconds: 0) + ) + let shell = DevProcess( + pid: 200, parentPID: 1, executable: "/bin/sh", + command: "/bin/sh -c '/bin/sleep 60'", + argumentVector: ["/bin/sh", "-c", "/bin/sleep 60"], resourceUsage: usage("00:00:30"), + birthToken: ProcessBirthToken(seconds: 4, microseconds: 1) + ) + + XCTAssertTrue(AutomationProcessCorrelator.links( + records: [cronRecord(signature: "/bin/sleep 60", expression: "5 10 * * *")], + processes: [shell], now: now, calendar: calendar + ).isEmpty) + let wrongMinuteShell = DevProcess( + pid: 201, parentPID: 199, executable: "/bin/sh", + command: "/bin/sh -c '/bin/sleep 60'", + argumentVector: ["/bin/sh", "-c", "/bin/sleep 60"], resourceUsage: usage("00:00:30"), + birthToken: ProcessBirthToken(seconds: 4, microseconds: 2) + ) + XCTAssertTrue(AutomationProcessCorrelator.links( + records: [cronRecord(signature: "/bin/sleep 60", expression: "5 10 * * *")], + processes: [daemon, wrongMinuteShell], now: now, calendar: calendar + ).isEmpty) + XCTAssertTrue(AutomationProcessCorrelator.links( + records: [cronRecord(signature: "/bin/sleep 60", expression: "@reboot")], + processes: [daemon, wrongMinuteShell], now: now, calendar: calendar + ).isEmpty) + } + + func testCronSignatureSubstringAndShellMetacharactersNeverMatch() { + var calendar = Calendar(identifier: .gregorian) + calendar.timeZone = TimeZone(secondsFromGMT: 0)! + let now = date(2026, 7, 13, 10, 5, 30, calendar: calendar) + let daemon = DevProcess( + pid: 300, parentPID: 1, executable: "/usr/sbin/cron", command: "/usr/sbin/cron", + argumentVector: ["/usr/sbin/cron"], + birthToken: ProcessBirthToken(seconds: 5, microseconds: 0) + ) + let shell = DevProcess( + pid: 301, parentPID: 300, executable: "/bin/sh", + command: "/bin/sh -c '/bin/sleep 600; echo unexpected'", + argumentVector: ["/bin/sh", "-c", "/bin/sleep 600; echo unexpected"], resourceUsage: usage("00:00:30"), + birthToken: ProcessBirthToken(seconds: 5, microseconds: 1) + ) + + XCTAssertTrue(AutomationProcessCorrelator.links( + records: [cronRecord(signature: "/bin/sleep 60", expression: "5 10 * * *")], + processes: [daemon, shell], now: now, calendar: calendar + ).isEmpty) + } + + func testCronRejectsDirectCommandTextWithoutExactShellCarrierArgv() { + let calendar = utcCalendar() + let now = date(2026, 7, 13, 10, 5, 30, calendar: calendar) + let daemon = DevProcess( + pid: 320, parentPID: 1, executable: "/usr/sbin/cron", command: "/usr/sbin/cron", + argumentVector: ["/usr/sbin/cron"], + birthToken: ProcessBirthToken(seconds: 5, microseconds: 0) + ) + let shortcut = DevProcess( + pid: 321, parentPID: 320, executable: "/bin/sleep", command: "/bin/sleep 60", + argumentVector: ["/bin/sleep", "60"], resourceUsage: usage("00:00:30"), + birthToken: ProcessBirthToken(seconds: 5, microseconds: 1) + ) + + XCTAssertTrue(AutomationProcessCorrelator.links( + records: [cronRecord(signature: "/bin/sleep 60", expression: "5 10 * * *")], + processes: [daemon, shortcut], now: now, calendar: calendar + ).isEmpty) + } + + func testCronCalendarRejectsMalformedListAndUsesWildcardOriginDOMDOWSemantics() { + let calendar = utcCalendar() + let monday = date(2026, 7, 13, 10, 5, 30, calendar: calendar) + let daemon = cronDaemon() + let carrier = cronCarrier(parentPID: daemon.pid, elapsed: "00:00:30") + + XCTAssertTrue(AutomationProcessCorrelator.links( + records: [cronRecord(signature: "/bin/sleep 60", expression: "5,bad 10 * * *")], + processes: [daemon, carrier], now: monday, calendar: calendar + ).isEmpty) + XCTAssertTrue(AutomationProcessCorrelator.links( + records: [cronRecord(signature: "/bin/sleep 60", expression: "5 10 */2 * 2")], + processes: [daemon, carrier], now: monday, calendar: calendar + ).isEmpty) + XCTAssertFalse(AutomationProcessCorrelator.links( + records: [cronRecord(signature: "/bin/sleep 60", expression: "5 10 14 * 1")], + processes: [daemon, carrier], now: monday, calendar: calendar + ).isEmpty) + } + + func testCronCalendarUsesInjectedTimezoneAtMinuteBoundary() { + var warsaw = Calendar(identifier: .gregorian) + warsaw.timeZone = TimeZone(identifier: "Europe/Warsaw")! + let now = date(2026, 7, 13, 0, 0, 1, calendar: warsaw) + let daemon = cronDaemon() + let carrier = cronCarrier(parentPID: daemon.pid, elapsed: "00:00:01") + + XCTAssertEqual(AutomationProcessCorrelator.links( + records: [cronRecord(signature: "/bin/sleep 60", expression: "0 0 * * *")], + processes: [daemon, carrier], now: now, calendar: warsaw + ).map(\.processIdentity.pid), [carrier.pid]) + } + + func testCronCalendarRejectsScalarAndNameStepBases() { + let calendar = utcCalendar() + let monday = date(2026, 7, 13, 10, 5, 30, calendar: calendar) + let daemon = cronDaemon() + let carrier = cronCarrier(parentPID: daemon.pid, elapsed: "00:00:30") + + for expression in ["5/10 10 * * *", "5 10 * * MON/2"] { + XCTAssertTrue(AutomationProcessCorrelator.links( + records: [cronRecord(signature: "/bin/sleep 60", expression: expression)], + processes: [daemon, carrier], now: monday, calendar: calendar + ).isEmpty, "Scalar step must fail closed: \(expression)") + } + XCTAssertEqual(AutomationProcessCorrelator.links( + records: [cronRecord(signature: "/bin/sleep 60", expression: "5-15/10 10 * * MON-FRI/2")], + processes: [daemon, carrier], now: monday, calendar: calendar + ).map(\.processIdentity.pid), [carrier.pid]) + } + + func testIndexedCorrelationHandlesFiveHundredRecordsAndOneThousandProcesses() { + let records = (0..<500).map { index in + automationRecord(Fixtures.userAgent, executable: "/opt/jobs/job-\(index)", arguments: ["--exact", "\(index)"]) + } + let processes = (0..<1_000).map { index in + DevProcess( + pid: Int32(10_000 + index), parentPID: 1, + executable: "/opt/jobs/job-\(index)", command: "/opt/jobs/job-\(index) --exact \(index)", + argumentVector: ["/opt/jobs/job-\(index)", "--exact", "\(index)"], + birthToken: ProcessBirthToken(seconds: UInt64(index + 1), microseconds: 1) + ) + } + let start = Date() + + let links = AutomationProcessCorrelator.links(records: records, processes: processes, now: start) + + XCTAssertEqual(links.count, 500) + XCTAssertLessThan(Date().timeIntervalSince(start), 2) + } + + func testCompositeNonCronIndexBoundsSharedExecutableCandidateWork() { + let records = (0..<500).map { index in + automationRecord(Fixtures.userAgent, executable: "/opt/jobs/shared", arguments: ["--job", "\(index)"]) + } + let processes = (0..<1_000).map { index in + DevProcess( + pid: Int32(20_000 + index), parentPID: 1, + executable: "/opt/jobs/shared", command: "display text is not evidence", + argumentVector: ["/opt/jobs/shared", "--job", "\(index)"], + birthToken: ProcessBirthToken(seconds: UInt64(index + 1), microseconds: 2) + ) + } + var work = AutomationCorrelationWork() + + let links = AutomationProcessCorrelator.links( + records: records, processes: processes, + now: Date(timeIntervalSince1970: 30_000), work: &work + ) + + XCTAssertEqual(links.count, 500) + XCTAssertEqual(work.indexedProcesses, 1_000) + XCTAssertEqual(work.nonCronRecordLookups, 1_000) + XCTAssertEqual(work.nonCronCandidateEvaluations, 500) + } + + func testCompositeNonCronIndexReturnsEverySameKeyProcessDeterministically() { + let first = DevProcess( + pid: 401, parentPID: 1, executable: "/bin/sleep", + command: "not evidence", argumentVector: ["/bin/sleep", "14400"], + birthToken: ProcessBirthToken(seconds: 40, microseconds: 1) + ) + let second = DevProcess( + pid: 402, parentPID: 1, executable: "/bin/sleep", + command: "also not evidence", argumentVector: ["/bin/sleep", "14400"], + birthToken: ProcessBirthToken(seconds: 40, microseconds: 2) + ) + + let forward = AutomationProcessCorrelator.links( + records: [Fixtures.userAgent], processes: [first, second], + now: Date(timeIntervalSince1970: 50_000) + ) + let reversed = AutomationProcessCorrelator.links( + records: [Fixtures.userAgent], processes: [second, first], + now: Date(timeIntervalSince1970: 50_000) + ) + + XCTAssertEqual(forward.map(\.processIdentity.pid), [401, 402]) + XCTAssertEqual(reversed, forward) + } + + func testIndexedCronWorkIsBoundedForFiveHundredRecordsAndOneThousandProcesses() { + let calendar = utcCalendar() + let now = date(2026, 7, 13, 10, 5, 30, calendar: calendar) + let records = (0..<500).map { index in + cronRecord(signature: "/bin/job-\(index)", expression: "5 10 * * *") + } + let daemon = cronDaemon() + let carriers = (0..<500).map { index in + DevProcess( + pid: Int32(1_000 + index), parentPID: daemon.pid, + executable: "/bin/sh", command: "display text is not evidence", + argumentVector: ["/bin/sh", "-c", "/bin/job-\(index)"], + resourceUsage: usage("00:00:30"), + birthToken: ProcessBirthToken(seconds: UInt64(100 + index), microseconds: 1) + ) + } + let noise = (0..<499).map { index in + DevProcess( + pid: Int32(2_000 + index), parentPID: 1, + executable: "/bin/noise-\(index)", command: "/bin/noise-\(index)", + argumentVector: ["/bin/noise-\(index)"], + birthToken: ProcessBirthToken(seconds: UInt64(1_000 + index), microseconds: 1) + ) + } + var work = AutomationCorrelationWork() + + let links = AutomationProcessCorrelator.links( + records: records, processes: [daemon] + carriers + noise, + now: now, calendar: calendar, work: &work + ) + + XCTAssertEqual(links.count, 500) + XCTAssertEqual(work.indexedProcesses, 1_000) + XCTAssertEqual(work.cronRecordLookups, 500) + XCTAssertEqual(work.cronCandidateEvaluations, 500) + XCTAssertLessThanOrEqual(work.ancestrySteps, 2_000) + } +} + +private func automationRecord( + _ source: AutomationRecord, + label: String? = nil, + executable: String? = nil, + arguments: [String]? = nil +) -> AutomationRecord { + let label = label ?? source.label + return AutomationRecord( + id: AutomationRecord.ID( + source: source.sourceKind, ownerUID: source.ownerUID ?? 0, + label: label, sourcePath: executable ?? source.sourceURL?.path ?? label + ), + kind: source.kind, sourceKind: source.sourceKind, label: label, + displayName: source.displayName, + providerBundleIdentifier: source.providerBundleIdentifier, + ownerUID: source.ownerUID, ownership: source.ownership, + executable: executable ?? source.executable, + arguments: arguments ?? source.arguments, + commandSignature: source.commandSignature, + environment: source.environment, workingDirectory: source.workingDirectory, + schedule: source.schedule, sourceURL: source.sourceURL, + sourceChecksum: source.sourceChecksum, enabledState: source.enabledState, + loadState: source.loadState, approvalState: source.approvalState, state: source.state, + evidence: source.evidence, capabilities: source.capabilities, + validationFindings: source.validationFindings + ) +} + +private func cronRecord(signature: String, expression: String) -> AutomationRecord { + AutomationRecord( + id: AutomationRecord.ID(source: .crontab, ownerUID: 501, label: expression, sourcePath: "/cron/\(expression)"), + kind: .cron, sourceKind: .crontab, label: "Cron", displayName: "Cron", + providerBundleIdentifier: nil, ownerUID: 501, ownership: .user, + executable: nil, arguments: [], commandSignature: signature, + environment: [:], workingDirectory: nil, + schedule: AutomationSchedule(triggers: [.cron(expression)], summary: expression), + sourceURL: nil, sourceChecksum: nil, enabledState: .enabled, loadState: .unknown, + approvalState: .notApplicable, state: .running, evidence: [], capabilities: [], + validationFindings: [] + ) +} + +private func usage(_ elapsed: String) -> DevProcessResourceUsage { + DevProcessResourceUsage(cpuPercent: 0, residentMemoryBytes: 0, elapsedTime: elapsed) +} + +private func utcCalendar() -> Calendar { + var calendar = Calendar(identifier: .gregorian) + calendar.timeZone = TimeZone(secondsFromGMT: 0)! + return calendar +} + +private func cronDaemon() -> DevProcess { + DevProcess( + pid: 700, parentPID: 1, executable: "/usr/sbin/cron", command: "/usr/sbin/cron", + argumentVector: ["/usr/sbin/cron"], + birthToken: ProcessBirthToken(seconds: 7, microseconds: 0) + ) +} + +private func cronCarrier(parentPID: Int32, elapsed: String) -> DevProcess { + DevProcess( + pid: 701, parentPID: parentPID, executable: "/bin/sh", + command: "/bin/sh -c '/bin/sleep 60'", + argumentVector: ["/bin/sh", "-c", "/bin/sleep 60"], + resourceUsage: usage(elapsed), + birthToken: ProcessBirthToken(seconds: 7, microseconds: 1) + ) +} + +private func date( + _ year: Int, _ month: Int, _ day: Int, _ hour: Int, _ minute: Int, _ second: Int, + calendar: Calendar +) -> Date { + calendar.date(from: DateComponents( + year: year, month: month, day: day, hour: hour, minute: minute, second: second + ))! +} diff --git a/Tests/DevScopeCoreTests/AutomationTestFixtures.swift b/Tests/DevScopeCoreTests/AutomationTestFixtures.swift new file mode 100644 index 0000000..50fcd6d --- /dev/null +++ b/Tests/DevScopeCoreTests/AutomationTestFixtures.swift @@ -0,0 +1,864 @@ +import Foundation +@testable import DevScopeCore + +enum Fixtures { + static let inventoryGeneration7 = AutomationInventorySnapshot( + generation: 7, + records: [userAgent], + health: [ + .launchAgent: AutomationSourceHealth( + kind: .launchAgent, + state: .healthy, + message: nil, + refreshedAt: Date(timeIntervalSince1970: 7_000) + ), + ], + refreshedAt: Date(timeIntervalSince1970: 7_000) + ) + + static let userAgent = makeUserAgent( + idSource: .launchAgent, + kind: .launchAgent, + sourceKind: .launchAgent, + loadState: .unloaded, + state: .idle, + evidenceSource: "synthetic launchd fixture" + ) + + static let runningUserAgent = makeUserAgent( + idSource: .launchAgent, + kind: .launchAgent, + sourceKind: .launchAgent, + loadState: .loaded, + state: .running, + evidenceSource: "synthetic launchd fixture" + ) + + static let backgroundCopyOfUserAgent = makeUserAgent( + idSource: .serviceManagement, + kind: .backgroundItem, + sourceKind: .serviceManagement, + loadState: .loaded, + state: .running, + evidenceSource: "synthetic background fixture" + ) + + static let runningBackup = DevProcess( + pid: 42_001, + parentPID: 1, + executable: "/bin/sleep", + command: "/bin/sleep 14400", + argumentVector: ["/bin/sleep", "14400"], + currentDirectory: "/tmp/devscope-fixtures", + resourceUsage: DevProcessResourceUsage( + cpuPercent: 0.1, + residentMemoryBytes: 1_048_576, + elapsedTime: "04:00:00" + ), + birthToken: ProcessBirthToken(seconds: 10_000, microseconds: 42) + ) + + static let runningBackupWithUpdatedMetrics = DevProcess( + pid: runningBackup.pid, + parentPID: runningBackup.parentPID, + executable: runningBackup.executable, + command: runningBackup.command, + argumentVector: runningBackup.argumentVector, + currentDirectory: runningBackup.currentDirectory, + resourceUsage: DevProcessResourceUsage( + cpuPercent: 0.2, + residentMemoryBytes: 1_114_112, + elapsedTime: "04:00:02" + ), + birthToken: runningBackup.birthToken + ) + + static func presentation(elapsedTime: String) -> AutomationPresentationSnapshot { + let process = DevProcess( + pid: runningBackup.pid, + parentPID: runningBackup.parentPID, + executable: runningBackup.executable, + command: runningBackup.command, + argumentVector: runningBackup.argumentVector, + currentDirectory: runningBackup.currentDirectory, + resourceUsage: DevProcessResourceUsage( + cpuPercent: runningBackup.resourceUsage?.cpuPercent ?? 0, + residentMemoryBytes: runningBackup.resourceUsage?.residentMemoryBytes ?? 0, + elapsedTime: elapsedTime + ), + birthToken: runningBackup.birthToken + ) + return AutomationPresentationSnapshot.build( + inventory: inventoryGeneration7, + processes: [process], + longRunningThreshold: 14_400, + now: Date(timeIntervalSince1970: 10_000) + ) + } + + private static func makeUserAgent( + idSource: AutomationSourceKind, + kind: AutomationKind, + sourceKind: AutomationSourceKind, + loadState: AutomationLoadState, + state: AutomationState, + evidenceSource: String + ) -> AutomationRecord { + let label = "com.example.backup" + let sourcePath = "/tmp/devscope-fixtures/\(sourceKind.rawValue)-backup.plist" + + return AutomationRecord( + id: AutomationRecord.ID( + source: idSource, + ownerUID: 501, + label: label, + sourcePath: sourcePath + ), + kind: kind, + sourceKind: sourceKind, + label: label, + displayName: "Synthetic Backup Fixture", + providerBundleIdentifier: "com.example.devscope-fixture-owner", + ownerUID: 501, + ownership: .user, + executable: "/bin/sleep", + arguments: ["14400"], + environment: ["DEVSCOPE_FIXTURE": "synthetic"], + workingDirectory: "/tmp/devscope-fixtures", + schedule: AutomationSchedule( + triggers: [.runAtLoad, .interval(seconds: 14_400)], + summary: "At load and every four hours" + ), + sourceURL: URL(fileURLWithPath: sourcePath), + sourceChecksum: "synthetic-checksum", + enabledState: .enabled, + loadState: loadState, + approvalState: .notApplicable, + state: state, + evidence: [AutomationEvidence( + strength: .strong, + source: evidenceSource, + detail: "Exact synthetic label and /bin/sleep executable" + )], + capabilities: [.startNow, .disable, .disableAndStop, .edit, .exportRecord], + validationFindings: [] + ) + } +} + +struct FakeAutomationSource: AutomationSource { + let kind: AutomationSourceKind + private let value: AutomationSourceSnapshot + + init(snapshot: AutomationSourceSnapshot) { + kind = snapshot.health.kind + value = snapshot + } + + func snapshot() async -> AutomationSourceSnapshot { + value + } +} + +final class ControllableAutomationClock: @unchecked Sendable { + private let lock = NSLock() + private var value: Date + + init(_ value: Date) { + self.value = value + } + + func now() -> Date { + lock.withLock { value } + } + + func advance(by interval: TimeInterval) { + lock.withLock { + value = value.addingTimeInterval(interval) + } + } +} + +actor CountingAutomationSource: AutomationSource { + nonisolated let kind: AutomationSourceKind + private let value: AutomationSourceSnapshot + private let delayNanoseconds: UInt64 + private var count = 0 + + init(snapshot: AutomationSourceSnapshot, delayNanoseconds: UInt64 = 0) { + kind = snapshot.health.kind + value = snapshot + self.delayNanoseconds = delayNanoseconds + } + + func snapshot() async -> AutomationSourceSnapshot { + count += 1 + if delayNanoseconds > 0 { + try? await Task.sleep(nanoseconds: delayNanoseconds) + } + return value + } + + func invocationCount() -> Int { + count + } +} + +enum AutomationTestFixtureError: Error, Equatable, Sendable { + case commandFailed + case missingItem(URL) + case readFailed(URL) + case authorizationFailed(URL) + case removeFailed(URL) +} + +final class RecordingAutomationCommandRunner: AutomationCommandRunning, @unchecked Sendable { + private let lock = NSLock() + private var recordedInvocations: [AutomationCommand] = [] + private let result: Result + + init( + result: Result = .success( + AutomationCommandResult( + status: 0, + standardOutput: Data(), + standardError: Data() + ) + ) + ) { + self.result = result + } + + var invocations: [AutomationCommand] { + lock.withLock { recordedInvocations } + } + + func run(_ command: AutomationCommand) async throws -> AutomationCommandResult { + lock.withLock { + recordedInvocations.append(command) + } + return try result.get() + } +} + +final class InMemoryAutomationFileSystem: AutomationFileSystem, @unchecked Sendable { + enum Operation: Equatable { + case createDirectory(URL, permissions: Int) + case writeTemporary(URL, permissions: Int) + case replace(URL, URL) + case moveToTrash(URL, URL) + case remove(URL) + } + + private let lock = NSLock() + private var files: [URL: Data] + private var metadataByURL: [URL: AutomationFileMetadata] + private var directories: Set + private var failingReads: Set + private var metadataFailuresRemainingAfterReplace: [URL: Int] + private var metadataOverridesAfterReplace: [URL: AutomationFileMetadata] + private var dataOverridesAfterReplace: [URL: [Int: Data]] + private var partialReplaceOccurrences: [URL: Set] + private var partialReplaceRetainedStagedOccurrences: [URL: Set] + private var unchangedReplaceOccurrences: [URL: Set] + private let unchangedReplaceRetainsStaged: Bool + private var partialReplaceObservedOnly: [URL: [URL]] + private var replaceCounts: [URL: Int] = [:] + private var partialTrashSources: Set + private var partialRemoveSources: Set + private var failingRemoves: Set + private var unchangedRemoves: Set + private var failStagedRemoves: Bool + private var filesInstalledBeforeRemoveFailure: [URL: [URL: Data]] + private var readObserver: (@Sendable (URL, Data) -> Void)? + private var operations: [Operation] = [] + private var temporaryFileSequence = 0 + + init( + files: [URL: Data] = [:], + metadata: [URL: AutomationFileMetadata] = [:], + directories: Set = [], + failingReads: Set = [], + metadataFailuresAfterReplace: [URL: Int] = [:], + metadataAfterReplace: [URL: AutomationFileMetadata] = [:], + dataAfterReplace: [URL: Data] = [:], + dataAfterReplaceOccurrences: [URL: [Int: Data]] = [:], + partialReplaceAfterCommit: Set = [], + partialReplaceAfterCommitOccurrences: [URL: Set] = [:], + partialReplaceRetainsStagedOccurrences: [URL: Set] = [:], + unchangedReplaceOccurrences: [URL: Set] = [:], + unchangedReplaceRetainsStaged: Bool = false, + partialReplaceObservedOnly: [URL: [URL]] = [:], + partialTrashAfterCommit: Set = [], + partialRemoveAfterCommit: Set = [], + failingRemoves: Set = [], + unchangedRemoves: Set = [], + failStagedRemoves: Bool = false, + filesInstalledBeforeRemoveFailure: [URL: [URL: Data]] = [:] + ) { + self.files = Dictionary( + files.map { (Self.canonical($0.key), $0.value) }, + uniquingKeysWith: { _, latest in latest } + ) + metadataByURL = Dictionary( + metadata.map { (Self.canonical($0.key), $0.value) }, + uniquingKeysWith: { _, latest in latest } + ) + self.directories = Set( + directories.map(Self.canonical) + + files.keys.map { Self.canonical($0.deletingLastPathComponent()) } + ) + self.failingReads = Set(failingReads.map(Self.canonical)) + metadataFailuresRemainingAfterReplace = Dictionary( + metadataFailuresAfterReplace.map { (Self.canonical($0.key), $0.value) }, + uniquingKeysWith: + + ) + metadataOverridesAfterReplace = Dictionary( + metadataAfterReplace.map { (Self.canonical($0.key), $0.value) }, + uniquingKeysWith: { _, latest in latest } + ) + dataOverridesAfterReplace = Dictionary( + dataAfterReplaceOccurrences.map { (Self.canonical($0.key), $0.value) }, + uniquingKeysWith: { _, latest in latest } + ) + for (destination, data) in dataAfterReplace { + dataOverridesAfterReplace[Self.canonical(destination), default: [:]][1] = data + } + partialReplaceOccurrences = Dictionary( + partialReplaceAfterCommitOccurrences.map { (Self.canonical($0.key), $0.value) }, + uniquingKeysWith: { $0.union($1) } + ) + for destination in partialReplaceAfterCommit.map(Self.canonical) { + partialReplaceOccurrences[destination, default: []].insert(1) + } + partialReplaceRetainedStagedOccurrences = Dictionary( + partialReplaceRetainsStagedOccurrences.map { (Self.canonical($0.key), $0.value) }, + uniquingKeysWith: { $0.union($1) } + ) + self.unchangedReplaceOccurrences = Dictionary( + unchangedReplaceOccurrences.map { (Self.canonical($0.key), $0.value) }, + uniquingKeysWith: { $0.union($1) } + ) + self.unchangedReplaceRetainsStaged = unchangedReplaceRetainsStaged + self.partialReplaceObservedOnly = Dictionary( + partialReplaceObservedOnly.map { + (Self.canonical($0.key), $0.value.map(Self.canonical)) + }, + uniquingKeysWith: + + ) + partialTrashSources = Set(partialTrashAfterCommit.map(Self.canonical)) + partialRemoveSources = Set(partialRemoveAfterCommit.map(Self.canonical)) + self.failingRemoves = Set(failingRemoves.map(Self.canonical)) + self.unchangedRemoves = Set(unchangedRemoves.map(Self.canonical)) + self.failStagedRemoves = failStagedRemoves + self.filesInstalledBeforeRemoveFailure = Dictionary( + filesInstalledBeforeRemoveFailure.map { failureURL, installedFiles in + ( + Self.canonical(failureURL), + Dictionary( + installedFiles.map { (Self.canonical($0.key), $0.value) }, + uniquingKeysWith: { _, latest in latest } + ) + ) + }, + uniquingKeysWith: { _, latest in latest } + ) + } + + var recordedOperations: [Operation] { + lock.withLock { operations } + } + + func itemExists(at url: URL) -> Bool { + let url = Self.canonical(url) + return lock.withLock { files[url] != nil || directories.contains(url) } + } + + func storedData(at url: URL) -> Data? { + lock.withLock { files[Self.canonical(url)] } + } + + func setStoredData(_ data: Data, at url: URL) { + let url = Self.canonical(url) + lock.withLock { + files[url] = data + directories.insert(Self.canonical(url.deletingLastPathComponent())) + } + } + + func setMetadata(_ metadata: AutomationFileMetadata, for url: URL) { + lock.withLock { + metadataByURL[Self.canonical(url)] = metadata + } + } + + func setReadObserver(_ observer: (@Sendable (URL, Data) -> Void)?) { + lock.withLock { readObserver = observer } + } + + func plistURLs(in directory: URL) throws -> [URL] { + let directory = Self.canonical(directory) + return try lock.withLock { + guard directories.contains(directory) else { + throw AutomationTestFixtureError.missingItem(directory) + } + return files.keys + .filter { + $0.pathExtension == "plist" + && Self.canonical($0.deletingLastPathComponent()) == directory + } + .sorted { $0.path < $1.path } + } + } + + func read(_ url: URL) throws -> Data { + let url = Self.canonical(url) + let (data, observer) = try lock.withLock { + guard !failingReads.contains(url) else { + throw AutomationTestFixtureError.readFailed(url) + } + guard let data = files[url] else { + throw AutomationTestFixtureError.missingItem(url) + } + return (data, readObserver) + } + observer?(url, data) + return data + } + + func metadata(for url: URL) throws -> AutomationFileMetadata { + let url = Self.canonical(url) + return try lock.withLock { + let wasReplaced = operations.contains { operation in + if case .replace(let replacedURL, _) = operation { return replacedURL == url } + return false + } + if wasReplaced, let failures = metadataFailuresRemainingAfterReplace[url], failures > 0 { + metadataFailuresRemainingAfterReplace[url] = failures - 1 + throw AutomationTestFixtureError.readFailed(url) + } + guard files[url] != nil || directories.contains(url) else { + throw AutomationTestFixtureError.missingItem(url) + } + return metadataByURL[url] ?? Self.defaultMetadata(for: url) + } + } + + func createDirectory(_ url: URL, permissions: Int) throws { + let url = Self.canonical(url) + lock.withLock { + directories.insert(url) + let existing = metadataByURL[url] ?? Self.defaultMetadata(for: url) + metadataByURL[url] = AutomationFileMetadata( + canonicalURL: url, + ownerUID: existing.ownerUID, + isSymbolicLink: existing.isSymbolicLink, + modificationDate: existing.modificationDate, + resourceIdentifier: existing.resourceIdentifier, + permissions: permissions + ) + operations.append(.createDirectory(url, permissions: permissions)) + } + } + + func writeStagedFile( + nextTo url: URL, + data: Data, + permissions: Int, + authorization: AutomationDirectoryAuthorization + ) throws -> AutomationStagedFile { + let url = Self.canonical(url) + return try lock.withLock { + let directoryURL = Self.canonical(authorization.directoryURL) + guard directoryURL.path == Self.canonical(url.deletingLastPathComponent()).path, + directories.contains(directoryURL), + (metadataByURL[directoryURL] ?? Self.defaultMetadata(for: directoryURL)) + .resourceIdentifier == authorization.resourceIdentifier + else { throw AutomationTestFixtureError.authorizationFailed(directoryURL) } + + temporaryFileSequence += 1 + let temporaryURL = Self.canonical( + directoryURL.appendingPathComponent(".devscope-fixture-\(temporaryFileSequence).tmp") + ) + files[temporaryURL] = data + let metadata = Self.defaultMetadata(for: temporaryURL) + metadataByURL[temporaryURL] = AutomationFileMetadata( + canonicalURL: temporaryURL, + ownerUID: metadata.ownerUID, + isSymbolicLink: false, + modificationDate: metadata.modificationDate, + resourceIdentifier: metadata.resourceIdentifier, + permissions: permissions + ) + operations.append(.writeTemporary(temporaryURL, permissions: permissions)) + guard let fileAuthorization = AutomationFileAuthorization( + fileURL: temporaryURL, + directory: authorization, + expectation: .existing(resourceIdentifier: metadata.resourceIdentifier ?? "") + ) else { throw AutomationTestFixtureError.authorizationFailed(temporaryURL) } + return AutomationStagedFile( + url: temporaryURL, + authorization: fileAuthorization, + binding: AutomationStagedFileBinding( + data: data, + resourceIdentifier: metadata.resourceIdentifier ?? "", + ownerUID: metadata.ownerUID, + permissions: permissions, + linkCount: 1 + ) + ) + } + } + + func replaceItem( + at destination: AutomationFileAuthorization, + with staged: AutomationStagedFile + ) throws -> AutomationFileMutationOutcome { + let destinationURL = Self.canonical(destination.fileURL) + let stagedURL = Self.canonical(staged.url) + return try lock.withLock { + let directoryURL = Self.canonical(destination.directory.directoryURL) + let directoryMetadata = metadataByURL[directoryURL] ?? Self.defaultMetadata(for: directoryURL) + guard directories.contains(directoryURL), + directoryMetadata.resourceIdentifier == destination.directory.resourceIdentifier, + destinationURL.deletingLastPathComponent().path == directoryURL.path, + staged.authorization.directory == destination.directory, + Self.canonical(staged.authorization.fileURL) == stagedURL, + stagedURL.deletingLastPathComponent().path == directoryURL.path, + let data = files[stagedURL], + let stagedMetadata = metadataByURL[stagedURL], + case .existing(let stagedIdentity) = staged.authorization.expectation, + stagedMetadata.resourceIdentifier == stagedIdentity, + staged.binding.resourceIdentifier == stagedIdentity, + staged.binding.data == data, + staged.binding.ownerUID == stagedMetadata.ownerUID, + staged.binding.permissions == stagedMetadata.permissions, + staged.binding.linkCount == 1 + else { throw AutomationTestFixtureError.authorizationFailed(destinationURL) } + + switch destination.expectation { + case .absent: + guard files[destinationURL] == nil else { + throw AutomationTestFixtureError.authorizationFailed(destinationURL) + } + case .existing(let expectedIdentity): + guard files[destinationURL] != nil, + (metadataByURL[destinationURL] ?? Self.defaultMetadata(for: destinationURL)) + .resourceIdentifier == expectedIdentity + else { throw AutomationTestFixtureError.authorizationFailed(destinationURL) } + } + + let replaceCount = (replaceCounts[destinationURL] ?? 0) + 1 + replaceCounts[destinationURL] = replaceCount + if unchangedReplaceOccurrences[destinationURL]?.remove(replaceCount) != nil { + if !unchangedReplaceRetainsStaged { + files.removeValue(forKey: stagedURL) + metadataByURL.removeValue(forKey: stagedURL) + } + return .unchanged + } + + let displacedData = files[destinationURL] + let displacedMetadata = metadataByURL[destinationURL] + ?? Self.defaultMetadata(for: destinationURL) + + files[destinationURL] = data + metadataByURL[destinationURL] = AutomationFileMetadata( + canonicalURL: destinationURL, + ownerUID: stagedMetadata.ownerUID, + isSymbolicLink: false, + modificationDate: stagedMetadata.modificationDate, + resourceIdentifier: stagedMetadata.resourceIdentifier, + permissions: stagedMetadata.permissions + ) + if let override = metadataOverridesAfterReplace[destinationURL] { + metadataByURL[destinationURL] = override + } + if let override = dataOverridesAfterReplace[destinationURL]?.removeValue( + forKey: replaceCount + ) { + files[destinationURL] = override + } + files.removeValue(forKey: stagedURL) + metadataByURL.removeValue(forKey: stagedURL) + operations.append(.replace(destinationURL, stagedURL)) + let installed = AutomationFileAuthorization( + fileURL: destinationURL, + directory: destination.directory, + expectation: .existing(resourceIdentifier: staged.binding.resourceIdentifier) + ) + let shouldReportPartial = partialReplaceOccurrences[destinationURL]?.contains( + replaceCount + ) == true + partialReplaceOccurrences[destinationURL]?.remove(replaceCount) + if shouldReportPartial, let installed { + let retainsStagedRecovery = partialReplaceRetainedStagedOccurrences[destinationURL]? + .remove(replaceCount) != nil + if retainsStagedRecovery { + files[stagedURL] = data + metadataByURL[stagedURL] = stagedMetadata + } + let recovery: AutomationFileAuthorization + if let displacedData { + temporaryFileSequence += 1 + let recoveryURL = Self.canonical( + destination.directory.directoryURL.appendingPathComponent( + ".devscope-fixture-displaced-\(temporaryFileSequence)" + ) + ) + files[recoveryURL] = displacedData + metadataByURL[recoveryURL] = AutomationFileMetadata( + canonicalURL: recoveryURL, + ownerUID: displacedMetadata.ownerUID, + isSymbolicLink: false, + modificationDate: displacedMetadata.modificationDate, + resourceIdentifier: displacedMetadata.resourceIdentifier, + permissions: displacedMetadata.permissions + ) + guard let displacedIdentity = displacedMetadata.resourceIdentifier, + let displacedRecovery = AutomationFileAuthorization( + fileURL: recoveryURL, + directory: destination.directory, + expectation: .existing(resourceIdentifier: displacedIdentity) + ) + else { throw AutomationTestFixtureError.authorizationFailed(recoveryURL) } + recovery = displacedRecovery + } else { + recovery = installed + } + let observedOnly = (partialReplaceObservedOnly[destinationURL] ?? []).compactMap { + observedURL -> AutomationFileAuthorization? in + guard let observedIdentity = (metadataByURL[observedURL] + ?? Self.defaultMetadata(for: observedURL)).resourceIdentifier, + files[observedURL] != nil + else { return nil } + return AutomationFileAuthorization( + fileURL: observedURL, + directory: destination.directory, + expectation: .existing(resourceIdentifier: observedIdentity) + ) + } + let retainedStaged = retainsStagedRecovery ? [staged.authorization] : [] + throw AutomationFilePartialMutation( + kind: .replace, + commitState: .committed, + observedFiles: [installed, recovery] + retainedStaged + observedOnly, + recoveryHandle: recovery, + recoveryHandles: retainedStaged, + resultURL: destinationURL + ) + } + return .committed(AutomationFileMutationReceipt( + primaryFile: installed, + resultURL: destinationURL, + bindingVerified: true + )) + } + } + + func moveToTrash( + _ source: AutomationFileAuthorization + ) throws -> AutomationFileMutationOutcome { + let sourceURL = Self.canonical(source.fileURL) + return try lock.withLock { + let directoryURL = Self.canonical(source.directory.directoryURL) + guard directories.contains(directoryURL), + (metadataByURL[directoryURL] ?? Self.defaultMetadata(for: directoryURL)) + .resourceIdentifier == source.directory.resourceIdentifier, + sourceURL.deletingLastPathComponent().path == directoryURL.path, + case .existing(let expectedIdentity) = source.expectation, + files[sourceURL] != nil + else { throw AutomationTestFixtureError.authorizationFailed(sourceURL) } + let sourceMetadata = metadataByURL[sourceURL] ?? Self.defaultMetadata(for: sourceURL) + guard let data = files[sourceURL], + sourceMetadata.resourceIdentifier == expectedIdentity + else { throw AutomationTestFixtureError.authorizationFailed(sourceURL) } + + temporaryFileSequence += 1 + let trashURL = Self.canonical(URL(fileURLWithPath: + "/tmp/devscope-fixtures/trash/\(temporaryFileSequence)-\(sourceURL.lastPathComponent)" + )) + files[trashURL] = data + metadataByURL[trashURL] = AutomationFileMetadata( + canonicalURL: trashURL, + ownerUID: sourceMetadata.ownerUID, + isSymbolicLink: false, + modificationDate: sourceMetadata.modificationDate, + resourceIdentifier: sourceMetadata.resourceIdentifier, + permissions: sourceMetadata.permissions + ) + files.removeValue(forKey: sourceURL) + metadataByURL.removeValue(forKey: sourceURL) + operations.append(.moveToTrash(sourceURL, trashURL)) + if partialTrashSources.remove(sourceURL) != nil { + let trashDirectory = Self.canonical(trashURL.deletingLastPathComponent()) + directories.insert(trashDirectory) + let directoryMetadata = metadataByURL[trashDirectory] + ?? Self.defaultMetadata(for: trashDirectory) + metadataByURL[trashDirectory] = directoryMetadata + guard let directory = AutomationDirectoryAuthorization( + directoryURL: trashDirectory, + resourceIdentifier: directoryMetadata.resourceIdentifier + ), let identity = sourceMetadata.resourceIdentifier, + let recovery = AutomationFileAuthorization( + fileURL: trashURL, + directory: directory, + expectation: .existing(resourceIdentifier: identity) + ) else { throw AutomationTestFixtureError.authorizationFailed(trashURL) } + throw AutomationFilePartialMutation( + kind: .trash, + commitState: .committed, + observedFiles: [recovery], + recoveryHandle: recovery, + resultURL: trashURL + ) + } + return .committed(AutomationFileMutationReceipt( + primaryFile: nil, + resultURL: trashURL, + bindingVerified: true + )) + } + } + + func fileMatchesBinding( + _ file: AutomationFileAuthorization, + binding: AutomationStagedFileBinding + ) throws -> Bool { + let url = Self.canonical(file.fileURL) + return lock.withLock { + guard case .existing(let expectedIdentity) = file.expectation, + let data = files[url] + else { return false } + let metadata = metadataByURL[url] ?? Self.defaultMetadata(for: url) + return expectedIdentity == binding.resourceIdentifier + && metadata.resourceIdentifier == binding.resourceIdentifier + && metadata.ownerUID == binding.ownerUID + && metadata.permissions == binding.permissions + && data == binding.data + } + } + + func removeItem( + _ source: AutomationFileAuthorization + ) throws -> AutomationFileMutationOutcome { + let sourceURL = Self.canonical(source.fileURL) + return try lock.withLock { + let directoryURL = Self.canonical(source.directory.directoryURL) + guard directories.contains(directoryURL), + (metadataByURL[directoryURL] ?? Self.defaultMetadata(for: directoryURL)) + .resourceIdentifier == source.directory.resourceIdentifier, + sourceURL.deletingLastPathComponent().path == directoryURL.path, + case .existing(let expectedIdentity) = source.expectation, + files[sourceURL] != nil, + (metadataByURL[sourceURL] ?? Self.defaultMetadata(for: sourceURL)) + .resourceIdentifier == expectedIdentity + else { throw AutomationTestFixtureError.authorizationFailed(sourceURL) } + if unchangedRemoves.contains(sourceURL) { + return .unchanged + } + if failingRemoves.contains(sourceURL) + || failStagedRemoves && sourceURL.lastPathComponent.hasPrefix(".devscope-fixture-") { + for (installedURL, installedData) in filesInstalledBeforeRemoveFailure[sourceURL] ?? [:] { + temporaryFileSequence += 1 + files[installedURL] = installedData + let metadata = Self.defaultMetadata(for: installedURL) + metadataByURL[installedURL] = AutomationFileMetadata( + canonicalURL: installedURL, + ownerUID: metadata.ownerUID, + isSymbolicLink: false, + modificationDate: metadata.modificationDate, + resourceIdentifier: "fixture:remove-failure:\(temporaryFileSequence):\(installedURL.path)", + permissions: metadata.permissions + ) + } + throw AutomationTestFixtureError.removeFailed(sourceURL) + } + let removedData = files[sourceURL] ?? Data() + let removedMetadata = metadataByURL[sourceURL] ?? Self.defaultMetadata(for: sourceURL) + files.removeValue(forKey: sourceURL) + metadataByURL.removeValue(forKey: sourceURL) + operations.append(.remove(sourceURL)) + if partialRemoveSources.remove(sourceURL) != nil { + temporaryFileSequence += 1 + let recoveryURL = Self.canonical( + directoryURL.appendingPathComponent(".devscope-fixture-remove-\(temporaryFileSequence)") + ) + files[recoveryURL] = removedData + metadataByURL[recoveryURL] = removedMetadata + guard let identity = removedMetadata.resourceIdentifier, + let recovery = AutomationFileAuthorization( + fileURL: recoveryURL, + directory: source.directory, + expectation: .existing(resourceIdentifier: identity) + ) + else { throw AutomationTestFixtureError.authorizationFailed(recoveryURL) } + throw AutomationFilePartialMutation( + kind: .remove, + commitState: .committed, + observedFiles: [recovery], + recoveryHandle: recovery, + resultURL: nil + ) + } + return .committed(AutomationFileMutationReceipt( + primaryFile: nil, + resultURL: nil, + bindingVerified: true + )) + } + } + + private static func canonical(_ url: URL) -> URL { + URL(fileURLWithPath: url.standardizedFileURL.path) + } + + private static func defaultMetadata(for url: URL) -> AutomationFileMetadata { + AutomationFileMetadata( + canonicalURL: canonical(url), + ownerUID: 501, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 1_000), + resourceIdentifier: "fixture:\(canonical(url).path)" + ) + } +} + +final class ControllableClock: @unchecked Sendable { + private let lock = NSLock() + private var currentDate: Date + + init(now: Date = Date(timeIntervalSince1970: 1_000)) { + currentDate = now + } + + func now() -> Date { + lock.withLock { currentDate } + } + + func advance(by interval: TimeInterval) { + lock.withLock { + currentDate = currentDate.addingTimeInterval(interval) + } + } +} + +extension AutomationCapabilityContext { + static func fixture( + currentUID: uid_t, + canonicalPathIsApproved: Bool, + ownerUID: uid_t?, + isSymlink: Bool = false, + isManaged: Bool = false, + implementedCapabilities: Set = Set(AutomationCapability.allCases) + ) -> Self { + Self( + currentUID: currentUID, + canonicalPathIsApproved: canonicalPathIsApproved, + sourceOwnerUID: ownerUID, + isSymlink: isSymlink, + isManaged: isManaged, + implementedCapabilities: implementedCapabilities + ) + } +} diff --git a/Tests/DevScopeCoreTests/BoundedSystemCommandRunnerTests.swift b/Tests/DevScopeCoreTests/BoundedSystemCommandRunnerTests.swift new file mode 100644 index 0000000..274d272 --- /dev/null +++ b/Tests/DevScopeCoreTests/BoundedSystemCommandRunnerTests.swift @@ -0,0 +1,149 @@ +import XCTest +@testable import DevScopeCore + +final class BoundedSystemCommandRunnerTests: XCTestCase { + func testProcessScannerRoutesPSAndLsofThroughTheBoundedRunner() throws { + let runner = RecordingSystemCommandRunner(results: [ + "/bin/ps": BoundedSystemCommandResult( + status: 0, + standardOutput: Data("42 1 1.5 2048 00:10 /bin/sleep /bin/sleep 10\n".utf8), + standardError: Data() + ), + "/usr/sbin/lsof": BoundedSystemCommandResult( + status: 0, + standardOutput: Data("p42\nn/var/tmp\n".utf8), + standardError: Data() + ), + ]) + let scanner = SystemProcessScanner( + nativeMetadataProvider: EmptyNativeMetadataProvider(), + commandRunner: runner + ) + + let snapshot = try scanner.snapshot(includeCurrentDirectories: true) + + XCTAssertEqual(snapshot.map(\.pid), [42]) + XCTAssertEqual(snapshot.first?.currentDirectory, "/var/tmp") + XCTAssertEqual(runner.requests.map(\.executablePath), ["/bin/ps", "/usr/sbin/lsof"]) + } + + func testGPUMetricProviderRoutesIORegThroughTheBoundedRunner() throws { + let runner = RecordingSystemCommandRunner(results: [ + "/usr/sbin/ioreg": BoundedSystemCommandResult( + status: 0, + standardOutput: Data("\"Device Utilization %\" = 37\n\"model\" = \"Apple GPU\"\n".utf8), + standardError: Data() + ) + ]) + let provider = SystemGPUMetricProvider( + executableURL: URL(fileURLWithPath: "/usr/sbin/ioreg"), + commandRunner: runner + ) + + XCTAssertEqual( + try provider.snapshot(), + DevGPUMetric(utilizationPercent: 37, modelName: "Apple GPU") + ) + XCTAssertEqual(runner.requests.map(\.executablePath), ["/usr/sbin/ioreg"]) + } + + func testTimeoutTerminatesTheCompleteCommandGroupWithinBound() throws { + let runner = BoundedSystemCommandRunner( + maximumCapturedBytes: 1_024, + executionTimeout: 0.05, + terminationGraceInterval: 0.05 + ) + let startedAt = ContinuousClock.now + + XCTAssertThrowsError( + try runner.run( + executableURL: URL(fileURLWithPath: "/bin/sh"), + arguments: ["-c", "trap '' TERM; while :; do sleep 1; done"] + ) + ) { error in + XCTAssertEqual(error as? BoundedSystemCommandError, .executionTimedOut) + } + + XCTAssertLessThan(startedAt.duration(to: .now), .seconds(2)) + } + + func testOutputLimitDrainsBothStreamsAndFailsClosed() throws { + let runner = BoundedSystemCommandRunner( + maximumCapturedBytes: 1_024, + executionTimeout: 2, + terminationGraceInterval: 0.05 + ) + + XCTAssertThrowsError( + try runner.run( + executableURL: URL(fileURLWithPath: "/bin/sh"), + arguments: [ + "-c", + "i=0; while [ $i -lt 20000 ]; do printf x; printf y >&2; i=$((i+1)); done", + ] + ) + ) { error in + XCTAssertEqual(error as? BoundedSystemCommandError, .outputLimitExceeded) + } + } + + func testSuccessfulCommandReturnsExactStatusAndBothStreams() throws { + let result = try BoundedSystemCommandRunner().run( + executableURL: URL(fileURLWithPath: "/bin/sh"), + arguments: ["-c", "printf output; printf warning >&2; exit 7"] + ) + + XCTAssertEqual(result.status, 7) + XCTAssertEqual(String(data: result.standardOutput, encoding: .utf8), "output") + XCTAssertEqual(String(data: result.standardError, encoding: .utf8), "warning") + } + + func testCommandReceivesTheIntendedMinimalUserEnvironment() throws { + let result = try BoundedSystemCommandRunner().run( + executableURL: URL(fileURLWithPath: "/bin/sh"), + arguments: ["-c", "printf '%s\\n%s\\n%s' \"$HOME\" \"$USER\" \"$LC_ALL\""] + ) + let lines = String(decoding: result.standardOutput, as: UTF8.self) + .split(whereSeparator: \.isNewline) + .map(String.init) + + XCTAssertEqual(lines, [ + FileManager.default.homeDirectoryForCurrentUser.path, + NSUserName(), + "C", + ]) + } +} + +private struct SystemCommandRequest: Equatable, Sendable { + let executablePath: String + let arguments: [String] +} + +private final class RecordingSystemCommandRunner: SystemCommandRunning, @unchecked Sendable { + private let lock = NSLock() + private let results: [String: BoundedSystemCommandResult] + private var recordedRequests: [SystemCommandRequest] = [] + + init(results: [String: BoundedSystemCommandResult]) { + self.results = results + } + + var requests: [SystemCommandRequest] { + lock.withLock { recordedRequests } + } + + func run(executableURL: URL, arguments: [String]) throws -> BoundedSystemCommandResult { + try lock.withLock { + recordedRequests.append(SystemCommandRequest( + executablePath: executableURL.path, + arguments: arguments + )) + return try XCTUnwrap(results[executableURL.path]) + } + } +} + +private struct EmptyNativeMetadataProvider: NativeProcessMetadataProviding { + func metadata(for processID: Int32) -> DevProcess? { nil } +} diff --git a/Tests/DevScopeCoreTests/CronAutomationSourceTests.swift b/Tests/DevScopeCoreTests/CronAutomationSourceTests.swift new file mode 100644 index 0000000..3c9061a --- /dev/null +++ b/Tests/DevScopeCoreTests/CronAutomationSourceTests.swift @@ -0,0 +1,351 @@ +import XCTest +@testable import DevScopeCore + +final class CronAutomationSourceTests: XCTestCase { + func testParsesEnvironmentMacrosSchedulesAndDisabledDevScopeEntries() { + let document = CronParser.parse(""" + SHELL=/bin/zsh + PATH=/opt/homebrew/bin:/usr/bin:/bin + @reboot /Users/example/bin/start-sync + 0 9 * * 1 /Users/example/bin/report --weekly + # devscope-disabled: 30 2 * * * /Users/example/bin/cleanup + """) + + XCTAssertEqual(document.environment["SHELL"], "/bin/zsh") + XCTAssertEqual(document.entries.count, 3) + XCTAssertEqual(document.entries[0].schedule.summary, "At startup") + XCTAssertEqual(document.entries[1].schedule.summary, "Mondays at 09:00") + XCTAssertFalse(document.entries[2].isEnabled) + XCTAssertEqual(document.entries[2].scheduleExpression, "30 2 * * *") + XCTAssertEqual(document.entries[2].command, "/Users/example/bin/cleanup") + XCTAssertEqual( + document.originalLines[4], + "# devscope-disabled: 30 2 * * * /Users/example/bin/cleanup" + ) + XCTAssertEqual( + document.entries[2].id, + CronParser.parse("30 2 * * * /Users/example/bin/cleanup").entries[0].id + ) + } + + func testDisabledMarkerWithInvalidPayloadRemainsVisibleAndMakesSourcePartial() async { + let line = "# devscope-disabled: # not-a-cron-entry" + let document = CronParser.parse(line) + + XCTAssertTrue(document.entries.isEmpty) + XCTAssertEqual(document.invalidLines, [CronInvalidLine( + lineNumber: 1, + content: line, + reason: "Invalid crontab line" + )]) + + let snapshot = await CronAutomationSource( + commandRunner: RecordingAutomationCommandRunner(result: .success(AutomationCommandResult( + status: 0, + standardOutput: Data("\(line)\n".utf8), + standardError: Data() + ))), + currentUID: 501, + currentUsername: "test-user" + ).snapshot() + + XCTAssertEqual(snapshot.health.state, .partial) + XCTAssertEqual(snapshot.health.message, "1 invalid crontab line.") + XCTAssertEqual(snapshot.records.map(\.state), [.invalid]) + } + + func testPreservesOriginalTextEnvironmentOrderAndForwardOnlyContext() { + let source = """ + # retained comment + + TOKEN=first-secret + 0 1 * * * /bin/echo before%mail-body + TOKEN="second secret" + 0 2 * * * /bin/echo after\\%literal + + """ + + let document = CronParser.parse(source) + + XCTAssertEqual(document.originalText, source) + XCTAssertEqual(document.originalLines, source.components(separatedBy: "\n")) + XCTAssertEqual(document.environmentAssignments.map(\.name), ["TOKEN", "TOKEN"]) + XCTAssertEqual(document.environmentAssignments.map(\.lineNumber), [3, 5]) + XCTAssertEqual(document.entries[0].environment["TOKEN"], "first-secret") + XCTAssertEqual(document.entries[1].environment["TOKEN"], "second secret") + XCTAssertEqual(document.entries[0].command, "/bin/echo before%mail-body") + XCTAssertEqual(document.entries[1].command, "/bin/echo after\\%literal") + } + + func testValidatesOfficialFiveFieldFormsAndKeepsMalformedLinesVisible() { + let document = CronParser.parse(""" + */15 0-23/2 1,15 1-12 0-7 /bin/true + 5 4 * jan sun /bin/true + 30 4 1,15 * 5 /bin/true + 60 0 * * * /bin/echo minute-secret + 0 24 * * * /bin/echo hour-secret + 0 0 0 * * /bin/false + 0 0 * 13 * /bin/false + 0 0 * * 8 /bin/false + 0-10/0 * * * * /bin/false + 10-1 * * * * /bin/false + 1,,2 * * * * /bin/false + 0 0 * jan-mar * /bin/false + @annually /bin/false + @midnight /bin/false + not a cron line containing=secret + """) + + XCTAssertEqual(document.entries.count, 3) + XCTAssertEqual(document.invalidLines.map(\.lineNumber), Array(4...15)) + XCTAssertEqual( + document.entries[2].schedule.triggers, + [.cron("30 4 1,15 * 5")] + ) + XCTAssertTrue(document.invalidLines.allSatisfy { $0.reason == "Invalid crontab line" }) + } + + func testStableEntryIdentityExcludesEnvironmentAndCommandSecrets() { + let first = CronParser.parse(""" + API_TOKEN=alpha-secret + 0 6 * * * /bin/run --token command-alpha + 0 6 * * * /bin/run-second + """) + let second = CronParser.parse(""" + # an unrelated retained comment + API_TOKEN=beta-secret + EXTRA_SECRET=gamma-secret + 0 6 * * * /bin/run --token command-beta + 0 6 * * * /bin/run-second + """) + + XCTAssertEqual(first.entries.map(\.id), second.entries.map(\.id)) + XCTAssertEqual(Set(first.entries.map(\.id)).count, 2) + for id in first.entries.map(\.id) + second.entries.map(\.id) { + XCTAssertFalse(id.contains("secret")) + XCTAssertFalse(id.contains("token")) + XCTAssertFalse(id.contains("/bin/run")) + } + } + + func testSourceRunsCurrentUserCrontabWithoutShellAndMapsUIDAndSafeCapabilities() async { + let output = Data(""" + TOKEN=environment-secret + 0 6 * * * /bin/run --token command-secret + # devscope-disabled: @daily /bin/disabled + 61 * * * * /bin/invalid-secret + """.utf8) + let runner = RecordingAutomationCommandRunner(result: .success(AutomationCommandResult( + status: 0, + standardOutput: output, + standardError: Data() + ))) + let source = CronAutomationSource( + commandRunner: runner, + currentUID: 777, + currentUsername: "test-user" + ) + + let snapshot = await source.snapshot() + + XCTAssertEqual(runner.invocations, [AutomationCommand( + executable: "/usr/bin/crontab", + arguments: ["-l"], + environment: ["LC_ALL": "C"] + )]) + XCTAssertEqual(snapshot.health.state, .partial) + XCTAssertEqual(snapshot.health.message, "1 invalid crontab line.") + XCTAssertEqual(snapshot.records.count, 3) + XCTAssertTrue(snapshot.records.allSatisfy { $0.ownerUID == 777 }) + XCTAssertTrue(snapshot.records.allSatisfy { $0.ownership == .user }) + XCTAssertTrue(snapshot.records.allSatisfy { $0.sourceKind == .crontab }) + XCTAssertTrue(snapshot.records.allSatisfy { $0.kind == .cron }) + XCTAssertTrue(snapshot.records.allSatisfy { $0.capabilities.isEmpty }) + let expectedChecksum = CronDocumentChecksum.checksum(output) + XCTAssertTrue(snapshot.records.allSatisfy { $0.sourceChecksum == expectedChecksum }) + XCTAssertEqual(snapshot.records.map(\.state), [.idle, .disabled, .invalid]) + XCTAssertEqual(snapshot.records[0].environment["TOKEN"], "environment-secret") + XCTAssertEqual( + snapshot.records[0].commandSignature, + "/bin/run --token command-secret" + ) + + for record in snapshot.records { + let publicMetadata = [ + record.id.rawValue, + record.label, + record.displayName, + record.validationFindings.joined(separator: " "), + record.evidence.map(\.detail).joined(separator: " "), + ].joined(separator: " ").lowercased() + XCTAssertFalse(publicMetadata.contains("secret")) + XCTAssertFalse(publicMetadata.contains("/bin/")) + } + } + + func testFollowsOfficialCommentEnvironmentAndSupportedMacroGrammar() { + let document = CronParser.parse(""" + #TOKEN=ignored-comment + "SPACED NAME" = " value " + PLAIN = bare + @reboot /bin/one + @hourly /bin/two with args + @daily /bin/three + @weekly /bin/four + @monthly /bin/five + @yearly /bin/six + *,1 * * * * /bin/invalid + +1 * * * * /bin/invalid + UNFINISHED="unterminated + """) + + XCTAssertEqual(document.environmentAssignments.map(\.name), ["SPACED NAME", "PLAIN"]) + XCTAssertEqual(document.environment["SPACED NAME"], " value ") + XCTAssertNil(document.environment["#TOKEN"]) + XCTAssertEqual(document.entries.count, 6) + XCTAssertEqual(document.entries[1].command, "/bin/two with args") + XCTAssertEqual(document.entries.map(\.schedule.summary), [ + "At startup", "Hourly", "Daily", "Weekly", "Monthly", "Yearly", + ]) + XCTAssertEqual(document.invalidLines.map(\.lineNumber), [10, 11, 12]) + } + + func testSourceDistinguishesStandardNoCrontabFromAllOtherFailures() async { + let emptyRunner = RecordingAutomationCommandRunner(result: .success(AutomationCommandResult( + status: 1, + standardOutput: Data(), + standardError: Data("crontab: no crontab for ExampleUser\n".utf8) + ))) + let empty = await CronAutomationSource( + commandRunner: emptyRunner, + currentUID: 501, + currentUsername: "ExampleUser" + ).snapshot() + XCTAssertEqual(empty.health.state, .healthy) + XCTAssertNil(empty.health.message) + XCTAssertTrue(empty.records.isEmpty) + + let failures = [ + AutomationCommandResult( + status: 1, + standardOutput: Data(), + standardError: Data("crontab: permission denied for diagnostic-secret\n".utf8) + ), + AutomationCommandResult( + status: 2, + standardOutput: Data("0 0 * * * /bin/fabricated\n".utf8), + standardError: Data("diagnostic-secret\n".utf8) + ), + AutomationCommandResult( + status: 0, + standardOutput: Data([0xFF]), + standardError: Data() + ), + ] + + for result in failures { + let snapshot = await CronAutomationSource( + commandRunner: RecordingAutomationCommandRunner(result: .success(result)), + currentUID: 501, + currentUsername: "ExampleUser" + ).snapshot() + XCTAssertEqual(snapshot.health.kind, .crontab) + XCTAssertEqual(snapshot.health.state, .failed) + XCTAssertTrue(snapshot.records.isEmpty) + XCTAssertFalse(snapshot.health.message?.contains("secret") ?? false) + XCTAssertFalse(snapshot.health.message?.contains("/bin/fabricated") ?? false) + } + + let thrown = await CronAutomationSource( + commandRunner: RecordingAutomationCommandRunner(result: .failure(.commandFailed)), + currentUID: 501, + currentUsername: "ExampleUser" + ).snapshot() + XCTAssertEqual(thrown.health.state, .failed) + XCTAssertTrue(thrown.records.isEmpty) + } + + func testNoCrontabDiagnosticRejectsEveryNearMatchForInjectedUsername() async { + let diagnostic = "crontab: no crontab for ExampleUser" + let failures = [ + AutomationCommandResult( + status: 1, + standardOutput: Data(), + standardError: Data("crontab: no crontab for OtherUser\n".utf8) + ), + AutomationCommandResult( + status: 1, + standardOutput: Data(), + standardError: Data("\(diagnostic)-extra\n".utf8) + ), + AutomationCommandResult( + status: 1, + standardOutput: Data(), + standardError: Data("crontab: no crontab for exampleuser\n".utf8) + ), + AutomationCommandResult( + status: 1, + standardOutput: Data(), + standardError: Data(" \(diagnostic)\n".utf8) + ), + AutomationCommandResult( + status: 1, + standardOutput: Data(), + standardError: Data("\(diagnostic) \n".utf8) + ), + AutomationCommandResult( + status: 1, + standardOutput: Data(), + standardError: Data("\(diagnostic)\nextra line\n".utf8) + ), + AutomationCommandResult( + status: 1, + standardOutput: Data(), + standardError: Data("\(diagnostic)\n\n".utf8) + ), + AutomationCommandResult( + status: 1, + standardOutput: Data("unexpected stdout".utf8), + standardError: Data("\(diagnostic)\n".utf8) + ), + AutomationCommandResult( + status: 1, + standardOutput: Data(), + standardError: Data([0xFF]) + ), + AutomationCommandResult( + status: 2, + standardOutput: Data(), + standardError: Data("\(diagnostic)\n".utf8) + ), + ] + + for result in failures { + let snapshot = await CronAutomationSource( + commandRunner: RecordingAutomationCommandRunner(result: .success(result)), + currentUID: 501, + currentUsername: "ExampleUser" + ).snapshot() + + XCTAssertEqual(snapshot.health.state, .failed) + XCTAssertTrue(snapshot.records.isEmpty) + } + } + + func testNoCrontabDiagnosticAcceptsExactUsernameWithOneTerminalNewlineConvention() async { + for terminator in ["", "\n", "\r\n"] { + let snapshot = await CronAutomationSource( + commandRunner: RecordingAutomationCommandRunner(result: .success(AutomationCommandResult( + status: 1, + standardOutput: Data(), + standardError: Data("crontab: no crontab for ExampleUser\(terminator)".utf8) + ))), + currentUID: 501, + currentUsername: "ExampleUser" + ).snapshot() + + XCTAssertEqual(snapshot.health.state, .healthy) + XCTAssertTrue(snapshot.records.isEmpty) + } + } +} diff --git a/Tests/DevScopeCoreTests/FullDiskAccessGuidanceTests.swift b/Tests/DevScopeCoreTests/FullDiskAccessGuidanceTests.swift new file mode 100644 index 0000000..018c552 --- /dev/null +++ b/Tests/DevScopeCoreTests/FullDiskAccessGuidanceTests.swift @@ -0,0 +1,118 @@ +import XCTest + +@testable import DevScopeCore + +final class FullDiskAccessGuidanceTests: XCTestCase { + func testFullDiskAccessSettingsRouteStopsAfterAcceptedDeepLink() { + var openedURL: URL? + var fallbackCount = 0 + + let didOpen = FullDiskAccessSettingsRoute.open( + using: { url in + openedURL = url + return true + }, + fallback: { fallbackCount += 1 } + ) + + XCTAssertTrue(didOpen) + XCTAssertEqual( + openedURL?.absoluteString, + "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles" + ) + XCTAssertEqual(fallbackCount, 0) + } + + func testFullDiskAccessSettingsRouteFallsBackWhenDeepLinkIsRejected() { + var fallbackCount = 0 + + let didOpen = FullDiskAccessSettingsRoute.open( + using: { _ in false }, + fallback: { fallbackCount += 1 } + ) + + XCTAssertFalse(didOpen) + XCTAssertEqual(fallbackCount, 1) + } + + func testFullDiskAccessSettingsRouteFallsBackWhenDeepLinkIsInvalid() { + var openerCount = 0 + var fallbackCount = 0 + + let didOpen = FullDiskAccessSettingsRoute.open( + deepLink: "http://[", + using: { _ in + openerCount += 1 + return true + }, + fallback: { fallbackCount += 1 } + ) + + XCTAssertFalse(didOpen) + XCTAssertEqual(openerCount, 0) + XCTAssertEqual(fallbackCount, 1) + } + + func testFullInstalledBuildProvidesSixManualAddSteps() { + let guidance = FullDiskAccessGuidance( + appPath: "/Applications/DevScope.app", + isSandboxed: false + ) + + XCTAssertTrue(guidance.isInstalledInApplications) + XCTAssertEqual(guidance.title, "Add DevScope to Full Disk Access") + XCTAssertEqual(guidance.steps.count, 6) + XCTAssertTrue(guidance.steps[0].contains("Open Full Disk Access")) + XCTAssertTrue(guidance.steps[1].contains("+")) + XCTAssertTrue(guidance.steps[2].contains("/Applications/DevScope.app")) + XCTAssertTrue(guidance.steps[3].contains("Enable")) + XCTAssertTrue(guidance.steps[4].contains("reopen DevScope")) + XCTAssertTrue(guidance.steps[5].contains("Check Access")) + } + + func testFullBuildOutsideApplicationsWarnsBeforeManualStepsAndRetainsExactPath() { + let appPath = "/Users/test/Downloads/DevScope Preview.app" + let guidance = FullDiskAccessGuidance(appPath: appPath, isSandboxed: false) + + XCTAssertFalse(guidance.isInstalledInApplications) + XCTAssertTrue(guidance.detail.contains("Install DevScope in Applications first")) + XCTAssertEqual(guidance.steps.count, 6) + XCTAssertTrue(guidance.steps[2].contains(appPath)) + } + + func testSandboxBuildRefusesToDescribeFullDiskAccessAsFix() { + let guidance = FullDiskAccessGuidance( + appPath: "/tmp/DevScope.app", + isSandboxed: true + ) + + XCTAssertFalse(guidance.isInstalledInApplications) + XCTAssertEqual(guidance.title, "This sandbox build cannot use Full Disk Access") + XCTAssertTrue(guidance.detail.contains("Install the full DevScope build")) + XCTAssertTrue(guidance.steps.isEmpty) + } + + func testSandboxBuildOutsideApplicationsIsNamedAsValidationDevelopmentBuild() { + let appPath = "/tmp/DevScope.app" + let guidance = FullDiskAccessGuidance(appPath: appPath, isSandboxed: true) + + XCTAssertTrue(guidance.detail.contains("validation/development build")) + XCTAssertTrue(guidance.detail.contains(appPath)) + XCTAssertTrue(guidance.detail.contains("App Sandbox")) + } + + func testApplicationsPathDetectionRequiresDirectoryBoundary() { + XCTAssertTrue( + FullDiskAccessGuidance( + appPath: "/Applications/Utilities/DevScope.app", + isSandboxed: false + ).isInstalledInApplications + ) + XCTAssertFalse( + FullDiskAccessGuidance( + appPath: "/ApplicationsBackup/DevScope.app", + isSandboxed: false + ).isInstalledInApplications + ) + } +} diff --git a/Tests/DevScopeCoreTests/InterfacePresentationTests.swift b/Tests/DevScopeCoreTests/InterfacePresentationTests.swift new file mode 100644 index 0000000..ce6da81 --- /dev/null +++ b/Tests/DevScopeCoreTests/InterfacePresentationTests.swift @@ -0,0 +1,110 @@ +import XCTest +@testable import DevScopeCore + +final class InterfacePresentationTests: XCTestCase { + func testWorkspaceModeAndAutomationSettingKeysAreStableAcrossReleases() { + XCTAssertEqual(DevScopeWorkspaceMode.processes.rawValue, "processes") + XCTAssertEqual(DevScopeWorkspaceMode.automations.rawValue, "automations") + XCTAssertEqual( + AutomationPresentationSettings.selectedWorkspaceModeKey, + "selectedWorkspaceMode" + ) + XCTAssertEqual( + AutomationPresentationSettings.longRunningThresholdSecondsKey, + "longRunningThresholdSeconds" + ) + XCTAssertEqual( + AutomationPresentationSettings.includeAppleSystemServicesKey, + "includeAppleSystemServices" + ) + } + + func testLongRunningThresholdClampsAndRoundsToOneHourSteps() { + XCTAssertEqual(AutomationPresentationSettings.normalizedThreshold(-1), 3_600) + XCTAssertEqual(AutomationPresentationSettings.normalizedThreshold(5_399), 3_600) + XCTAssertEqual(AutomationPresentationSettings.normalizedThreshold(5_400), 7_200) + XCTAssertEqual(AutomationPresentationSettings.normalizedThreshold(999_999), 604_800) + XCTAssertEqual(AutomationPresentationSettings.normalizedThreshold(.nan), 14_400) + } + + func testFocusWorkflowsShowSixItemsAndRetainAnOutOfRangeSelection() { + let workflows = (1...10).map(workflow) + + XCTAssertEqual( + InterfacePresentation.visibleFocusWorkflows( + workflows, + selectedID: workflows[8].id, + showsAll: false + ).map(\.id), + workflows.prefix(6).map(\.id) + [workflows[8].id] + ) + } + + func testKeyboardSelectionMovesWithinVisibleRowsAndRecoversFromAStaleSelection() { + let visibleIDs: [Int32] = [11, 22, 33] + + XCTAssertEqual( + InterfacePresentation.movedSelection(in: visibleIDs, current: 22, direction: .next), + 33 + ) + XCTAssertEqual( + InterfacePresentation.movedSelection(in: visibleIDs, current: 33, direction: .next), + 33 + ) + XCTAssertEqual( + InterfacePresentation.movedSelection(in: visibleIDs, current: 999, direction: .previous), + 33 + ) + XCTAssertNil( + InterfacePresentation.movedSelection(in: [], current: 22, direction: .next) + ) + } + + func testFocusDisclosureReturnsEveryWorkflowWhenExpanded() { + let workflows = (1...10).map(workflow) + + XCTAssertEqual( + InterfacePresentation.visibleFocusWorkflows( + workflows, + selectedID: workflows[8].id, + showsAll: true + ), + workflows + ) + } + + func testKeyboardSelectionStartsAndClampsAtTheRequestedEdge() { + let visibleIDs: [Int32] = [11, 22, 33] + + XCTAssertEqual( + InterfacePresentation.movedSelection(in: visibleIDs, current: nil, direction: .next), + 11 + ) + XCTAssertEqual( + InterfacePresentation.movedSelection(in: visibleIDs, current: nil, direction: .previous), + 33 + ) + XCTAssertEqual( + InterfacePresentation.movedSelection(in: visibleIDs, current: 11, direction: .previous), + 11 + ) + } + + private func workflow(_ index: Int) -> DevWorkflow { + DevWorkflow( + id: "workflow:\(index)", + title: "Workflow \(index)", + subtitle: "1 process", + kind: .projectWorkspace, + processIDs: [Int32(index)], + primaryProject: nil, + tags: [], + totalCPU: 0, + totalMemoryBytes: 0, + risk: .normal, + confidence: 1, + summary: "Summary", + suggestedAction: "Inspect" + ) + } +} diff --git a/Tests/DevScopeCoreTests/LaunchdAutomationSourceTests.swift b/Tests/DevScopeCoreTests/LaunchdAutomationSourceTests.swift new file mode 100644 index 0000000..8116673 --- /dev/null +++ b/Tests/DevScopeCoreTests/LaunchdAutomationSourceTests.swift @@ -0,0 +1,871 @@ +import CryptoKit +import XCTest +@testable import DevScopeCore + +final class LaunchdAutomationSourceTests: XCTestCase { + func testSnapshotAppliesInjectedUserDomainEnabledAndLoadedState() async throws { + let root = LaunchdScanRoot( + url: URL(fileURLWithPath: "/fixtures/Library/LaunchAgents"), + kind: .launchAgent, + ownership: .user, + isMutable: true + ) + let enabledURL = root.url.appendingPathComponent("enabled.plist") + let disabledURL = root.url.appendingPathComponent("disabled.plist") + let provider = FakeLaunchdRuntimeStateProvider(states: [ + "com.example.enabled": LaunchdRuntimeState( + enabledState: .enabled, + loadState: .loaded + ), + "com.example.disabled": LaunchdRuntimeState( + enabledState: .disabled, + loadState: .loaded + ), + ]) + let source = LaunchdAutomationSource( + fileSystem: InMemoryAutomationFileSystem(files: [ + enabledURL: try plistData(["Label": "com.example.enabled", "Program": "/bin/echo"]), + disabledURL: try plistData(["Label": "com.example.disabled", "Program": "/bin/echo"]), + ]), + currentUID: 501, + roots: [root], + runtimeStateProvider: provider + ) + + let records = Dictionary( + uniqueKeysWithValues: await source.snapshot().records.map { ($0.label, $0) } + ) + + XCTAssertEqual(records["com.example.enabled"]?.enabledState, .enabled) + XCTAssertEqual(records["com.example.enabled"]?.loadState, .loaded) + XCTAssertEqual(records["com.example.enabled"]?.state, .idle) + XCTAssertEqual(records["com.example.disabled"]?.enabledState, .disabled) + XCTAssertEqual(records["com.example.disabled"]?.loadState, .loaded) + XCTAssertEqual(records["com.example.disabled"]?.state, .disabled) + let requestedLabels = await provider.requestedLabels() + XCTAssertEqual(requestedLabels, ["com.example.disabled", "com.example.enabled"]) + } + + func testHealthMessageBoundsLargeDiagnosticSetsAndKeepsRepresentativeIssues() { + let errors = (1...20).map { index in + "definition-\(index): " + String(repeating: "unreadable diagnostic detail ", count: 20) + } + + let message = LaunchdAutomationSource.healthMessage(for: errors) + + XCTAssertTrue(message.contains("Launchd inspection found 20 issues.")) + XCTAssertTrue(message.contains("definition-1")) + XCTAssertTrue(message.contains("definition-2")) + XCTAssertFalse(message.contains("definition-3")) + XCTAssertTrue(message.hasSuffix("Select an Invalid record for per-definition details.")) + XCTAssertLessThanOrEqual(message.count, 360) + } + + func testParsesLabelProgramArgumentsAndCalendarSchedule() throws { + let plist: [String: Any] = [ + "Label": "com.example.weekly-report", + "ProgramArguments": ["/usr/bin/python3", "/Users/example/report.py"], + "StartCalendarInterval": ["Weekday": 1, "Hour": 9, "Minute": 0], + ] + let data = try PropertyListSerialization.data( + fromPropertyList: plist, + format: .xml, + options: 0 + ) + + let record = try LaunchdPlistParser.parse( + data: data, + sourceURL: URL( + fileURLWithPath: "/Users/example/Library/LaunchAgents/com.example.weekly-report.plist" + ), + ownerUID: 501, + ownership: .user + ) + + XCTAssertEqual(record.label, "com.example.weekly-report") + XCTAssertEqual(record.executable, "/usr/bin/python3") + XCTAssertEqual(record.arguments, ["/Users/example/report.py"]) + XCTAssertEqual(record.schedule.summary, "Mondays at 09:00") + } + + func testParsesEnvironmentAndWorkingDirectoryWithoutDroppingDefinitionState() throws { + let record = try LaunchdPlistParser.parse( + data: plistData([ + "Label": "com.example.complete-definition", + "Program": "/bin/echo", + "EnvironmentVariables": ["MODE": "audit", "TOKEN": "local"], + "WorkingDirectory": "/Users/example/Workspace", + ]), + sourceURL: URL(fileURLWithPath: "/Users/example/Library/LaunchAgents/com.example.complete-definition.plist"), + ownerUID: 501, + ownership: .user + ) + + XCTAssertEqual(record.environment, ["MODE": "audit", "TOKEN": "local"]) + XCTAssertEqual(record.workingDirectory, "/Users/example/Workspace") + } + + func testRejectsNonStringEnvironmentValuesAndEmptyWorkingDirectory() throws { + XCTAssertThrowsError(try LaunchdPlistParser.parse( + data: plistData([ + "Label": "com.example.invalid-environment", + "Program": "/bin/echo", + "EnvironmentVariables": ["RETRIES": 3], + ]), + sourceURL: URL(fileURLWithPath: "/tmp/invalid-environment.plist"), + ownerUID: 501, + ownership: .user + )) { error in + XCTAssertEqual(error as? AutomationParseError, .invalidField("EnvironmentVariables")) + } + XCTAssertThrowsError(try LaunchdPlistParser.parse( + data: plistData([ + "Label": "com.example.invalid-directory", + "Program": "/bin/echo", + "WorkingDirectory": "", + ]), + sourceURL: URL(fileURLWithPath: "/tmp/invalid-directory.plist"), + ownerUID: 501, + ownership: .user + )) { error in + XCTAssertEqual(error as? AutomationParseError, .invalidField("WorkingDirectory")) + } + } + + func testProgramTakesExecutablePrecedenceAndProgramArgumentsRemainArgv() throws { + let data = try plistData([ + "Label": "com.example.explicit-program", + "Program": "/usr/local/bin/worker", + "ProgramArguments": ["custom-argv-zero", "--once"], + "StartCalendarInterval": ["Weekday": 1, "Hour": 9, "Minute": 0], + ]) + + let record = try LaunchdPlistParser.parse( + data: data, + sourceURL: URL(fileURLWithPath: "/Library/LaunchDaemons/com.example.explicit-program.plist"), + ownerUID: 0, + ownership: .thirdPartySystem + ) + + XCTAssertEqual(record.kind, .launchDaemon) + XCTAssertEqual(record.sourceKind, .launchDaemon) + XCTAssertEqual(record.executable, "/usr/local/bin/worker") + XCTAssertEqual(record.arguments, ["custom-argv-zero", "--once"]) + } + + func testParsesBundleRelativeProgramWithoutProgramArguments() throws { + let record = try LaunchdPlistParser.parse( + data: plistData([ + "Label": "com.example.bundle-worker", + "BundleProgram": "Contents/MacOS/Worker", + "StartCalendarInterval": ["Weekday": 1, "Hour": 9, "Minute": 0], + ]), + sourceURL: URL(fileURLWithPath: "/Library/LaunchAgents/com.example.bundle-worker.plist"), + ownerUID: 0, + ownership: .thirdPartySystem + ) + + XCTAssertEqual(record.executable, "Contents/MacOS/Worker") + XCTAssertEqual(record.arguments, []) + } + + func testParsesSupportedLaunchTriggersAndRelativeArgvZero() throws { + let cases: [(fields: [String: Any], triggers: [AutomationSchedule.Trigger], summary: String)] = [ + (["RunAtLoad": true], [.runAtLoad], "At load"), + (["KeepAlive": true], [.runAtLoad, .keepAlive], "At load, kept alive"), + ( + ["KeepAlive": ["SuccessfulExit": false]], + [.runAtLoad, .keepAlive], + "At load, kept alive conditionally" + ), + ( + ["RunAtLoad": true, "KeepAlive": true], + [.runAtLoad, .keepAlive], + "At load, kept alive" + ), + (["StartInterval": 300], [.interval(seconds: 300)], "Every 5 minutes"), + (["RunAtLoad": false, "KeepAlive": false], [.demand], "On demand"), + ([:], [.demand], "On demand"), + ] + + for (index, testCase) in cases.enumerated() { + var plist = testCase.fields + plist["Label"] = "com.example.trigger-\(index)" + plist["ProgramArguments"] = ["relative-tool", "--once"] + + let record = try LaunchdPlistParser.parse( + data: plistData(plist), + sourceURL: URL(fileURLWithPath: "/Library/LaunchAgents/com.example.trigger-\(index).plist"), + ownerUID: 0, + ownership: .thirdPartySystem + ) + + XCTAssertEqual(record.executable, "relative-tool", "case \(index)") + XCTAssertEqual(record.arguments, ["--once"], "case \(index)") + XCTAssertEqual(record.schedule.triggers, testCase.triggers, "case \(index)") + XCTAssertEqual(record.schedule.summary, testCase.summary, "case \(index)") + } + } + + func testKeepAliveAcceptsOnlyDocumentedConditionKeysAndTypes() throws { + let validConditions: [[String: Any]] = [ + ["SuccessfulExit": true], + ["NetworkState": false], + ["Crashed": true], + ["PathState": ["/tmp/ready": true, "/tmp/blocked": false]], + ["OtherJobEnabled": ["com.example.dependency": true]], + [ + "SuccessfulExit": false, + "PathState": ["/tmp/ready": true], + "OtherJobEnabled": ["com.example.dependency": false], + ], + ] + + for (index, conditions) in validConditions.enumerated() { + let record = try LaunchdPlistParser.parse( + data: plistData([ + "Label": "com.example.valid-keepalive-\(index)", + "Program": "/bin/echo", + "KeepAlive": conditions, + ]), + sourceURL: URL(fileURLWithPath: "/tmp/valid-keepalive-\(index).plist"), + ownerUID: 501, + ownership: .user + ) + + XCTAssertEqual(record.schedule.triggers, [.runAtLoad, .keepAlive], "case \(index)") + } + + let invalidConditions: [(name: String, conditions: [String: Any])] = [ + ("unsupported key", ["AfterInitialDemand": true]), + ("boolean as integer", ["SuccessfulExit": 1]), + ("boolean as string", ["Crashed": "true"]), + ("path state scalar", ["PathState": true]), + ("path state non-boolean value", ["PathState": ["/tmp/ready": 1]]), + ("path state nested value", ["PathState": ["/tmp/ready": ["nested": true]]]), + ("other job scalar", ["OtherJobEnabled": false]), + ("other job non-boolean value", ["OtherJobEnabled": ["com.example.job": "yes"]]), + ] + + for testCase in invalidConditions { + XCTAssertThrowsError( + try LaunchdPlistParser.parse( + data: plistData([ + "Label": "com.example.invalid-keepalive", + "Program": "/bin/echo", + "KeepAlive": testCase.conditions, + ]), + sourceURL: URL(fileURLWithPath: "/tmp/invalid-keepalive.plist"), + ownerUID: 501, + ownership: .user + ), + testCase.name + ) { error in + XCTAssertEqual( + error as? AutomationParseError, + .invalidField("KeepAlive"), + testCase.name + ) + } + } + } + + func testParsesCalendarIntervalArrayAsIndependentTriggers() throws { + let record = try LaunchdPlistParser.parse( + data: plistData([ + "Label": "com.example.calendar-array", + "Program": "/bin/echo", + "StartCalendarInterval": [ + ["Weekday": 7, "Hour": 7, "Minute": 15], + ["Month": 1, "Day": 1, "Hour": 0, "Minute": 0], + ], + ]), + sourceURL: URL(fileURLWithPath: "/Library/LaunchAgents/com.example.calendar-array.plist"), + ownerUID: 0, + ownership: .thirdPartySystem + ) + + XCTAssertEqual(record.schedule.triggers, [ + .calendar("Sundays at 07:15"), + .calendar("January 1 at 00:00"), + ]) + XCTAssertEqual(record.schedule.summary, "Sundays at 07:15; January 1 at 00:00") + } + + func testCalendarMonthScopesTheDayOrWeekdayAlternatives() throws { + let cases: [(fields: [String: Any], summary: String)] = [ + ( + ["Month": 1, "Weekday": 1, "Hour": 9, "Minute": 0], + "Mondays in January at 09:00" + ), + ( + ["Month": 1, "Day": 15, "Weekday": 1, "Hour": 9, "Minute": 0], + "January (day 15 or Mondays) at 09:00" + ), + ] + + for (index, testCase) in cases.enumerated() { + let record = try LaunchdPlistParser.parse( + data: plistData([ + "Label": "com.example.calendar-precedence-\(index)", + "Program": "/bin/echo", + "StartCalendarInterval": testCase.fields, + ]), + sourceURL: URL(fileURLWithPath: "/tmp/calendar-precedence-\(index).plist"), + ownerUID: 501, + ownership: .user + ) + + XCTAssertEqual(record.schedule.summary, testCase.summary, "case \(index)") + } + } + + func testCalendarWildcardsRemainIndependentFromStartInterval() throws { + let record = try LaunchdPlistParser.parse( + data: plistData([ + "Label": "com.example.calendar-wildcards", + "Program": "/bin/echo", + "StartInterval": 60, + "StartCalendarInterval": [ + ["Minute": 15], + [:], + ], + ]), + sourceURL: URL(fileURLWithPath: "/Library/LaunchAgents/com.example.calendar-wildcards.plist"), + ownerUID: 0, + ownership: .thirdPartySystem + ) + + XCTAssertEqual(record.schedule.triggers, [ + .interval(seconds: 60), + .calendar("Every day at minute 15 of every hour"), + .calendar("Every day every minute"), + ]) + XCTAssertEqual( + record.schedule.summary, + "Every 1 minute, Every day at minute 15 of every hour; Every day every minute" + ) + } + + func testRejectsMalformedOrContractViolatingPropertyListsWithTypedErrors() throws { + let cases: [(name: String, data: Data, error: AutomationParseError)] = [ + ("malformed XML", Data("".utf8), .unreadablePropertyList), + ("missing label", try plistData(["Program": "/bin/echo"]), .missingLabel), + ( + "invalid label type", + try plistData(["Label": 42, "Program": "/bin/echo"]), + .invalidField("Label") + ), + ("missing program", try plistData(["Label": "com.example.missing"]), .missingProgram), + ( + "relative Program", + try plistData(["Label": "com.example.relative", "Program": "bin/tool"]), + .invalidField("Program") + ), + ( + "absolute BundleProgram", + try plistData(["Label": "com.example.bundle", "BundleProgram": "/Contents/MacOS/Tool"]), + .invalidField("BundleProgram") + ), + ( + "non-string argv", + try plistData([ + "Label": "com.example.argv", + "Program": "/bin/echo", + "ProgramArguments": ["echo", 42], + ]), + .invalidField("ProgramArguments") + ), + ( + "non-boolean RunAtLoad", + try plistData(["Label": "com.example.run", "Program": "/bin/echo", "RunAtLoad": 1]), + .invalidField("RunAtLoad") + ), + ( + "invalid KeepAlive", + try plistData(["Label": "com.example.keep", "Program": "/bin/echo", "KeepAlive": "yes"]), + .invalidField("KeepAlive") + ), + ( + "non-positive interval", + try plistData(["Label": "com.example.interval", "Program": "/bin/echo", "StartInterval": 0]), + .invalidField("StartInterval") + ), + ( + "calendar value outside man-page range", + try plistData([ + "Label": "com.example.calendar", + "Program": "/bin/echo", + "StartCalendarInterval": ["Hour": 24], + ]), + .invalidField("StartCalendarInterval.Hour") + ), + ( + "empty calendar array", + try plistData([ + "Label": "com.example.empty-calendar", + "Program": "/bin/echo", + "StartCalendarInterval": [], + ]), + .invalidField("StartCalendarInterval") + ), + ] + + for testCase in cases { + XCTAssertThrowsError( + try LaunchdPlistParser.parse( + data: testCase.data, + sourceURL: URL(fileURLWithPath: "/tmp/\(testCase.name).plist"), + ownerUID: 501, + ownership: .user + ), + testCase.name + ) { error in + XCTAssertEqual(error as? AutomationParseError, testCase.error, testCase.name) + } + } + } + + func testPublicParserPreservesCanonicalIdentityButDefaultsToReadOnlyCapabilities() throws { + let data = try plistData([ + "Label": "com.example.mutable-user-agent", + "Program": "/bin/sleep", + "ProgramArguments": ["sleep", "60"], + ]) + let sourceURL = URL( + fileURLWithPath: "/Users/example/Library/LaunchAgents/folder/../com.example.mutable-user-agent.plist" + ) + + let record = try LaunchdPlistParser.parse( + data: data, + sourceURL: sourceURL, + ownerUID: 501, + ownership: .user + ) + + XCTAssertEqual(record.ownerUID, 501) + XCTAssertEqual(record.ownership, .user) + XCTAssertEqual(record.kind, .launchAgent) + XCTAssertEqual(record.sourceKind, .launchAgent) + XCTAssertEqual(record.sourceURL, sourceURL.standardizedFileURL) + XCTAssertEqual( + record.id, + AutomationRecord.ID( + source: .launchAgent, + ownerUID: 501, + label: "com.example.mutable-user-agent", + sourcePath: sourceURL.standardizedFileURL.path + ) + ) + XCTAssertEqual( + record.sourceChecksum, + SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined() + ) + XCTAssertEqual(record.capabilities, [.exportRecord]) + XCTAssertEqual(record.evidence.first?.strength, .strong) + XCTAssertEqual(record.state, .unresolved) + } + + func testPublicParserInfersLaunchDaemonKindFromCanonicalDotSegmentPath() throws { + let data = try plistData([ + "Label": "com.example.daemon", + "Program": "/bin/echo", + ]) + let sourceURL = URL( + fileURLWithPath: "/Library/LaunchDaemons/folder/../job.plist" + ) + let canonicalSourceURL = sourceURL.standardizedFileURL + + let record = try LaunchdPlistParser.parse( + data: data, + sourceURL: sourceURL, + ownerUID: 0, + ownership: .thirdPartySystem + ) + + XCTAssertEqual(record.sourceURL, canonicalSourceURL) + XCTAssertEqual(record.kind, .launchDaemon) + XCTAssertEqual(record.sourceKind, .launchDaemon) + XCTAssertEqual( + record.id, + AutomationRecord.ID( + source: .launchDaemon, + ownerUID: 0, + label: "com.example.daemon", + sourcePath: canonicalSourceURL.path + ) + ) + } + + func testDefaultRootsInventoryUserThirdPartyAndAppleLocations() { + let home = URL(fileURLWithPath: "/Users/fixture") + let roots = LaunchdAutomationSource.defaultRoots(homeDirectory: home) + + XCTAssertEqual(roots.map(\.url), [ + URL(fileURLWithPath: "/Users/fixture/Library/LaunchAgents"), + URL(fileURLWithPath: "/Library/LaunchAgents"), + URL(fileURLWithPath: "/Library/LaunchDaemons"), + URL(fileURLWithPath: "/System/Library/LaunchAgents"), + URL(fileURLWithPath: "/System/Library/LaunchDaemons"), + ]) + XCTAssertEqual(roots.map(\.kind), [ + .launchAgent, .launchAgent, .launchDaemon, .launchAgent, .launchDaemon, + ]) + XCTAssertEqual(roots.map(\.ownership), [ + .user, .thirdPartySystem, .thirdPartySystem, .appleSystem, .appleSystem, + ]) + XCTAssertEqual(roots.map(\.isMutable), [true, false, false, false, false]) + } + + func testSnapshotInventoriesEveryRootAndKeepsDuplicateLabelsDistinct() async throws { + let roots = LaunchdAutomationSource.defaultRoots( + homeDirectory: URL(fileURLWithPath: "/Users/fixture") + ) + var files: [URL: Data] = [:] + var metadata: [URL: AutomationFileMetadata] = [:] + + for (index, root) in roots.enumerated() { + let url = root.url.appendingPathComponent("duplicate-\(index).plist") + files[url] = try plistData([ + "Label": "com.example.duplicate", + "Program": "/bin/echo", + ]) + metadata[url] = AutomationFileMetadata( + canonicalURL: url, + ownerUID: index == 0 ? 501 : 0, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 1_000), + resourceIdentifier: "file-\(index)" + ) + } + + let source = LaunchdAutomationSource( + fileSystem: InMemoryAutomationFileSystem(files: files, metadata: metadata), + currentUID: 501, + roots: roots + ) + let snapshot = await source.snapshot() + + XCTAssertEqual(snapshot.health.state, .healthy, snapshot.health.message ?? "no health message") + XCTAssertEqual(snapshot.records.count, 5) + XCTAssertEqual(Set(snapshot.records.map(\.id)).count, 5) + XCTAssertEqual(snapshot.records.map(\.ownership), [ + .user, .thirdPartySystem, .thirdPartySystem, .appleSystem, .appleSystem, + ]) + XCTAssertEqual(snapshot.records.map(\.ownerUID), [501, 0, 0, 0, 0]) + XCTAssertEqual(snapshot.records.map(\.kind), [ + .launchAgent, .launchAgent, .launchDaemon, .launchAgent, .launchDaemon, + ]) + XCTAssertEqual( + snapshot.records.first?.capabilities, + [.edit, .duplicate, .exportRecord, .remove] + ) + XCTAssertEqual(snapshot.records.dropFirst().first?.capabilities, [.exportRecord]) + XCTAssertTrue(snapshot.records.contains { $0.ownership == .appleSystem }) + } + + func testUserRootPreservesFileOwnerAndRequiresCurrentUIDForMutability() async throws { + let root = LaunchdScanRoot( + url: URL(fileURLWithPath: "/fixtures/Library/LaunchAgents"), + kind: .launchAgent, + ownership: .user, + isMutable: true + ) + let url = root.url.appendingPathComponent("other-owner.plist") + let fileSystem = InMemoryAutomationFileSystem( + files: [url: try plistData([ + "Label": "com.example.other-owner", + "Program": "/bin/echo", + ])], + metadata: [url: AutomationFileMetadata( + canonicalURL: url, + ownerUID: 777, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 1_000), + resourceIdentifier: "other-owner" + )] + ) + let source = LaunchdAutomationSource( + fileSystem: fileSystem, + currentUID: 501, + roots: [root] + ) + + let record = await source.snapshot().records.first + + XCTAssertEqual(record?.ownerUID, 777) + XCTAssertEqual(record?.ownership, .user) + XCTAssertEqual(record?.capabilities, [.exportRecord]) + } + + func testSourceGrantsMutabilityOnlyToSafeRegularFilesInsideApprovedUserRoot() async throws { + let root = LaunchdScanRoot( + url: URL(fileURLWithPath: "/fixtures/Library/LaunchAgents"), + kind: .launchAgent, + ownership: .user, + isMutable: true + ) + let cases: [( + name: String, + canonicalURL: URL, + isSymbolicLink: Bool, + expectedCapabilities: Set + )] = [ + ( + "safe", + root.url.appendingPathComponent("safe.plist"), + false, + [.edit, .duplicate, .exportRecord, .remove] + ), + ( + "symlink", + root.url.appendingPathComponent("symlink.plist"), + true, + [.exportRecord] + ), + ( + "canonical-escape", + URL(fileURLWithPath: "/fixtures/outside/canonical-escape.plist"), + false, + [.exportRecord] + ), + ( + "prefix-sibling", + URL(fileURLWithPath: "/fixtures/Library/LaunchAgents-escaped/prefix-sibling.plist"), + false, + [.exportRecord] + ), + ] + var files: [URL: Data] = [:] + var metadata: [URL: AutomationFileMetadata] = [:] + + for testCase in cases { + let enumeratedURL = root.url.appendingPathComponent("\(testCase.name).plist") + files[enumeratedURL] = try plistData([ + "Label": "com.example.\(testCase.name)", + "Program": "/bin/echo", + ]) + metadata[enumeratedURL] = AutomationFileMetadata( + canonicalURL: testCase.canonicalURL, + ownerUID: 501, + isSymbolicLink: testCase.isSymbolicLink, + modificationDate: Date(timeIntervalSince1970: 1_000), + resourceIdentifier: testCase.name + ) + } + let source = LaunchdAutomationSource( + fileSystem: InMemoryAutomationFileSystem(files: files, metadata: metadata), + currentUID: 501, + roots: [root] + ) + + let recordsByLabel = Dictionary( + uniqueKeysWithValues: await source.snapshot().records.map { ($0.label, $0) } + ) + + for testCase in cases { + let record = recordsByLabel["com.example.\(testCase.name)"] + XCTAssertEqual(record?.capabilities, testCase.expectedCapabilities, testCase.name) + XCTAssertEqual(record?.sourceURL, testCase.canonicalURL.standardizedFileURL, testCase.name) + } + } + + func testSnapshotSurfacesMalformedDefinitionsAndMissingDirectoriesAsPartialHealth() async throws { + let presentRoot = LaunchdScanRoot( + url: URL(fileURLWithPath: "/fixtures/Library/LaunchAgents"), + kind: .launchAgent, + ownership: .user, + isMutable: true + ) + let missingRoot = LaunchdScanRoot( + url: URL(fileURLWithPath: "/fixtures/System/Library/LaunchDaemons"), + kind: .launchDaemon, + ownership: .appleSystem, + isMutable: false + ) + let validURL = presentRoot.url.appendingPathComponent("valid.plist") + let malformedURL = presentRoot.url.appendingPathComponent("broken.plist") + let fileSystem = InMemoryAutomationFileSystem(files: [ + validURL: try plistData(["Label": "com.example.valid", "Program": "/bin/echo"]), + malformedURL: Data("not a property list".utf8), + ]) + let source = LaunchdAutomationSource( + fileSystem: fileSystem, + currentUID: 501, + roots: [presentRoot, missingRoot] + ) + + let snapshot = await source.snapshot() + + XCTAssertEqual(snapshot.health.state, .partial) + XCTAssertTrue(snapshot.health.message?.contains("broken.plist") == true) + XCTAssertTrue(snapshot.health.message?.contains(missingRoot.url.path) == true) + XCTAssertEqual(snapshot.records.count, 2) + let invalid = snapshot.records.first { $0.state == .invalid } + XCTAssertEqual(invalid?.label, "broken") + XCTAssertEqual(invalid?.ownership, .user) + XCTAssertEqual(invalid?.ownerUID, 501) + XCTAssertEqual(invalid?.sourceURL, malformedURL.standardizedFileURL) + XCTAssertTrue(invalid?.validationFindings.contains("Unreadable property list") == true) + XCTAssertEqual(invalid?.capabilities, []) + } + + func testReadFailurePreservesSuccessfulMetadataProvenanceAndCanonicalIdentity() async throws { + let root = LaunchdScanRoot( + url: URL(fileURLWithPath: "/fixtures/Library/LaunchAgents"), + kind: .launchAgent, + ownership: .user, + isMutable: true + ) + let enumeratedURL = root.url.appendingPathComponent("read-failure.plist") + let canonicalURL = root.url.appendingPathComponent("canonical-read-failure.plist") + let fileSystem = InMemoryAutomationFileSystem( + files: [enumeratedURL: try plistData([ + "Label": "com.example.unreadable", + "Program": "/bin/echo", + ])], + metadata: [enumeratedURL: AutomationFileMetadata( + canonicalURL: canonicalURL, + ownerUID: 777, + isSymbolicLink: false, + modificationDate: Date(timeIntervalSince1970: 1_000), + resourceIdentifier: "read-failure" + )], + failingReads: [enumeratedURL] + ) + let source = LaunchdAutomationSource( + fileSystem: fileSystem, + currentUID: 501, + roots: [root] + ) + + let snapshot = await source.snapshot() + let invalid = snapshot.records.first + + XCTAssertEqual(snapshot.health.state, .partial) + XCTAssertEqual(invalid?.state, .invalid) + XCTAssertEqual(invalid?.ownerUID, 777) + XCTAssertEqual(invalid?.sourceURL, canonicalURL.standardizedFileURL) + XCTAssertEqual(invalid?.label, "canonical-read-failure") + XCTAssertEqual( + invalid?.id, + AutomationRecord.ID( + source: .launchAgent, + ownerUID: 777, + label: "canonical-read-failure", + sourcePath: canonicalURL.standardizedFileURL.path + ) + ) + } + + func testCacheReusesOnlyTheCompleteCanonicalResourceDateAndChecksumIdentity() async throws { + let root = LaunchdScanRoot( + url: URL(fileURLWithPath: "/fixtures/Library/LaunchAgents"), + kind: .launchAgent, + ownership: .user, + isMutable: true + ) + let url = root.url.appendingPathComponent("cached.plist") + let originalData = try plistData([ + "Label": "com.example.cached", + "Program": "/bin/echo", + ]) + let initialDate = Date(timeIntervalSince1970: 1_000) + let fileSystem = InMemoryAutomationFileSystem( + files: [url: originalData], + metadata: [url: AutomationFileMetadata( + canonicalURL: url, + ownerUID: 501, + isSymbolicLink: false, + modificationDate: initialDate, + resourceIdentifier: "resource-a" + )] + ) + let parseCounter = LockedCounter() + let source = LaunchdAutomationSource( + fileSystem: fileSystem, + currentUID: 501, + roots: [root], + recordParser: { data, sourceURL, ownerUID, ownership, kind, isMutable in + parseCounter.increment() + return try LaunchdPlistParser.parse( + data: data, + canonicalSourceURL: sourceURL, + ownerUID: ownerUID, + ownership: ownership, + kind: kind, + isMutable: isMutable + ) + } + ) + + _ = await source.snapshot() + _ = await source.snapshot() + XCTAssertEqual(parseCounter.value, 1, "unchanged identity should reuse the parsed record") + + fileSystem.setMetadata(AutomationFileMetadata( + canonicalURL: url, + ownerUID: 501, + isSymbolicLink: false, + modificationDate: initialDate, + resourceIdentifier: "resource-b" + ), for: url) + _ = await source.snapshot() + XCTAssertEqual(parseCounter.value, 2, "resource identity must invalidate reuse") + + fileSystem.setMetadata(AutomationFileMetadata( + canonicalURL: url, + ownerUID: 501, + isSymbolicLink: false, + modificationDate: initialDate.addingTimeInterval(1), + resourceIdentifier: "resource-b" + ), for: url) + _ = await source.snapshot() + XCTAssertEqual(parseCounter.value, 3, "modification date must invalidate reuse") + + fileSystem.setStoredData(try plistData([ + "Label": "com.example.cached", + "Program": "/bin/echo", + "RunAtLoad": true, + ]), at: url) + _ = await source.snapshot() + XCTAssertEqual(parseCounter.value, 4, "checksum must invalidate reuse") + } + + private func plistData(_ plist: [String: Any]) throws -> Data { + try PropertyListSerialization.data( + fromPropertyList: plist, + format: .xml, + options: 0 + ) + } +} + +private actor FakeLaunchdRuntimeStateProvider: LaunchdRuntimeStateProviding { + private let values: [String: LaunchdRuntimeState] + private var labels: [String] = [] + + init(states: [String: LaunchdRuntimeState]) { + values = states + } + + func states(for requestedLabels: [String], guiUID: uid_t) async -> [String: LaunchdRuntimeState] { + labels = requestedLabels + return values + } + + func requestedLabels() -> [String] { labels } +} + +private final class LockedCounter: @unchecked Sendable { + private let lock = NSLock() + private var count = 0 + + var value: Int { + lock.withLock { count } + } + + func increment() { + lock.withLock { count += 1 } + } +} diff --git a/Tests/DevScopeCoreTests/LiveActivityLayoutPolicyTests.swift b/Tests/DevScopeCoreTests/LiveActivityLayoutPolicyTests.swift new file mode 100644 index 0000000..e4097c8 --- /dev/null +++ b/Tests/DevScopeCoreTests/LiveActivityLayoutPolicyTests.swift @@ -0,0 +1,86 @@ +import XCTest + +@testable import DevScopeCore + +final class LiveActivityLayoutPolicyTests: XCTestCase { + func testUpdatedPreferredHeightPreservesCurrentValueInConstrainedWorkspace() { + XCTAssertEqual( + LiveActivityLayoutPolicy.updatedPreferredHeight( + currentPreferredHeight: 244, + measuredHeight: 132, + workspaceHeight: 500 + ), + 244 + ) + } + + func testUpdatedPreferredHeightAcceptsValidDragMeasurement() { + XCTAssertEqual( + LiveActivityLayoutPolicy.updatedPreferredHeight( + currentPreferredHeight: 190, + measuredHeight: 244, + workspaceHeight: 820 + ), + 244 + ) + } + + func testUpdatedPreferredHeightIgnoresSubpixelMeasurementNoise() { + XCTAssertEqual( + LiveActivityLayoutPolicy.updatedPreferredHeight( + currentPreferredHeight: 244, + measuredHeight: 244.25, + workspaceHeight: 820 + ), + 244 + ) + } + + func testHeightUsesDefaultAndClampsToSupportedRange() { + XCTAssertEqual( + LiveActivityLayoutPolicy.resolvedHeight(preferredHeight: .nan, workspaceHeight: 900), 190) + XCTAssertEqual( + LiveActivityLayoutPolicy.resolvedHeight(preferredHeight: 80, workspaceHeight: 900), 120) + XCTAssertEqual( + LiveActivityLayoutPolicy.resolvedHeight(preferredHeight: 420, workspaceHeight: 900), 360) + } + + func testShortWorkspaceProtectsProcessArea() { + XCTAssertEqual( + LiveActivityLayoutPolicy.resolvedHeight(preferredHeight: 300, workspaceHeight: 500), 132) + XCTAssertFalse( + LiveActivityLayoutPolicy.shouldPersist(measuredHeight: 132, workspaceHeight: 500)) + } + + func testComfortableWorkspacePersistsDraggedHeight() { + XCTAssertTrue(LiveActivityLayoutPolicy.shouldPersist(measuredHeight: 244, workspaceHeight: 820)) + } + + func testWidthChoosesWideStackedAndCompactModes() { + XCTAssertEqual(LiveActivityLayoutPolicy.mode(availableWidth: 1_200), .wide) + XCTAssertEqual(LiveActivityLayoutPolicy.mode(availableWidth: 820), .stacked) + XCTAssertEqual(LiveActivityLayoutPolicy.mode(availableWidth: 620), .compact) + } + + func testAttainableWorkspaceWidthChoosesCompactMode() { + XCTAssertEqual(LiveActivityLayoutPolicy.mode(availableWidth: 780), .compact) + } + + func testMinimumHeightUsesCondensedVerticalContent() { + XCTAssertEqual( + LiveActivityLayoutPolicy.verticalMode(availableHeight: 120, layoutMode: .compact), + .condensed + ) + } + + func testDefaultHeightCondensesStackedContentToAvoidClipping() { + XCTAssertEqual( + LiveActivityLayoutPolicy.verticalMode(availableHeight: 190, layoutMode: .stacked), + .condensed + ) + XCTAssertEqual( + LiveActivityLayoutPolicy.verticalMode(availableHeight: 240, layoutMode: .stacked), + .normal + ) + } +} diff --git a/Tests/DevScopeCoreTests/LoginBackgroundAutomationSourceTests.swift b/Tests/DevScopeCoreTests/LoginBackgroundAutomationSourceTests.swift new file mode 100644 index 0000000..a4ef7d7 --- /dev/null +++ b/Tests/DevScopeCoreTests/LoginBackgroundAutomationSourceTests.swift @@ -0,0 +1,459 @@ +import CryptoKit +import Foundation +import XCTest +@testable import DevScopeCore + +final class LoginBackgroundAutomationSourceTests: XCTestCase { + func testBackgroundDiagnosticPolicyFailsClosedOnMacOS27AndLater() { + XCTAssertEqual( + BackgroundTaskDiagnosticPolicy.forOperatingSystemMajorVersion(14), + .available + ) + XCTAssertEqual( + BackgroundTaskDiagnosticPolicy.forOperatingSystemMajorVersion(26), + .available + ) + XCTAssertEqual( + BackgroundTaskDiagnosticPolicy.forOperatingSystemMajorVersion(27), + .administratorApprovalRequired + ) + XCTAssertEqual( + BackgroundTaskDiagnosticPolicy.forOperatingSystemMajorVersion(28), + .administratorApprovalRequired + ) + } + + func testBackgroundDiagnosticRequiringAdministratorApprovalNeverInvokesRunner() async { + let runner = RecordingAutomationCommandRunner() + let source = BackgroundTaskAutomationSource( + runner: runner, + diagnosticPolicy: .administratorApprovalRequired, + now: { Date(timeIntervalSince1970: 3_000) } + ) + + let first = await source.snapshot() + let second = await source.snapshot() + let fingerprint = await source.currentRawOutputHash() + + XCTAssertTrue(first.records.isEmpty) + XCTAssertEqual(first, second) + XCTAssertEqual(first.health.kind, .serviceManagement) + XCTAssertEqual(first.health.state, .permissionRequired) + XCTAssertEqual( + first.health.message, + "macOS 27 or later requires administrator approval for this Background Items diagnostic. " + + "Review Background Items in System Settings; DevScope did not run the diagnostic." + ) + XCTAssertEqual(first.health.refreshedAt, Date(timeIntervalSince1970: 3_000)) + XCTAssertNil(fingerprint) + XCTAssertTrue(runner.invocations.isEmpty) + } + + func testLegacyLoginItemSuccessNormalizesCurrentUserEvidence() async { + let adapter = StubLegacyLoginItemAdapter(result: .success([ + LegacyLoginItemDescriptor( + name: "Synthetic Login Fixture", + path: "/tmp/devscope-fixtures/Synthetic Login Fixture.app", + isHidden: false + ), + ])) + let source = LegacyLoginItemAutomationSource( + adapter: adapter, + currentUID: 501, + now: { Date(timeIntervalSince1970: 1_000) } + ) + + let snapshot = await source.snapshot() + + XCTAssertEqual(snapshot.health.state, .healthy) + XCTAssertEqual(snapshot.records.count, 1) + XCTAssertEqual(snapshot.records[0].sourceKind, .legacyLoginItem) + XCTAssertEqual(snapshot.records[0].ownerUID, 501) + XCTAssertEqual(snapshot.records[0].ownership, .user) + XCTAssertEqual(snapshot.records[0].schedule.triggers, [.atLogin]) + XCTAssertEqual(snapshot.records[0].capabilities, [.exportRecord]) + let recoveryData = try! LegacyLoginItemRecoveryDocument.encode( + selectedRecord: snapshot.records[0], + descriptor: LegacyLoginItemDescriptor( + name: "Synthetic Login Fixture", + path: "/tmp/devscope-fixtures/Synthetic Login Fixture.app", + isHidden: false + ), + currentUID: 501 + ) + XCTAssertEqual(snapshot.records[0].sourceChecksum, sha256(recoveryData)) + } + + func testLegacyLoginItemChecksumBindsHiddenStateAndCanonicalPath() async { + let visible = await legacySnapshot(LegacyLoginItemDescriptor( + name: "Synthetic Login Fixture", + path: "/tmp/devscope-fixtures/Synthetic Login Fixture.app", + isHidden: false + )) + let hidden = await legacySnapshot(LegacyLoginItemDescriptor( + name: "Synthetic Login Fixture", + path: "/tmp/devscope-fixtures/Synthetic Login Fixture.app", + isHidden: true + )) + let changedPath = await legacySnapshot(LegacyLoginItemDescriptor( + name: "Synthetic Login Fixture", + path: "/tmp/devscope-fixtures/Changed Fixture.app", + isHidden: false + )) + + XCTAssertNotNil(visible.sourceChecksum) + XCTAssertNotEqual(visible.sourceChecksum, hidden.sourceChecksum) + XCTAssertNotEqual(visible.sourceChecksum, changedPath.sourceChecksum) + } + + func testLegacyLoginItemDenialRequiresExplicitUserActionWithoutLeakingDetails() async { + let source = LegacyLoginItemAutomationSource( + adapter: StubLegacyLoginItemAdapter(result: .failure(.permissionDenied)), + currentUID: 501, + now: { Date(timeIntervalSince1970: 1_000) } + ) + + let snapshot = await source.snapshot() + + XCTAssertTrue(snapshot.records.isEmpty) + XCTAssertEqual(snapshot.health.state, .permissionRequired) + XCTAssertEqual( + snapshot.health.message, + "Allow DevScope to inspect current-user login items in System Settings." + ) + } + + func testBackgroundDiagnosticUsesExactObservedBoundaryFieldsAndFingerprint() async { + let output = Data(""" + Background Task Management diagnostic + Items: 1 + #1: + Name: Synthetic Backup Fixture + Identifier: com.example.backup + URL: file:///tmp/devscope-fixtures/background-backup.plist + Bundle Identifier: com.example.devscope-fixture-owner + Executable Path: /bin/sleep + Disposition: enabled, allowed + Parent Identifier: com.example.devscope-fixture-owner + Team Identifier: SYNTHETIC1 + Developer Name: Synthetic Developer + Type: agent + """.utf8) + let runner = RecordingAutomationCommandRunner(result: .success( + AutomationCommandResult(status: 0, standardOutput: output, standardError: Data()) + )) + let source = BackgroundTaskAutomationSource( + runner: runner, + diagnosticPolicy: .available, + now: { Date(timeIntervalSince1970: 2_000) } + ) + + let snapshot = await source.snapshot() + let fingerprint = await source.currentRawOutputHash() + + XCTAssertEqual( + runner.invocations, + [AutomationCommand(executable: "/usr/bin/sfltool", arguments: ["dumpbtm"])] + ) + XCTAssertEqual(snapshot.health.state, .healthy) + XCTAssertEqual(snapshot.records.count, 1) + XCTAssertEqual(snapshot.records[0].label, "com.example.backup") + XCTAssertEqual(snapshot.records[0].providerBundleIdentifier, "com.example.devscope-fixture-owner") + XCTAssertEqual(snapshot.records[0].executable, "/bin/sleep") + XCTAssertEqual(snapshot.records[0].state, .unresolved) + XCTAssertEqual(snapshot.records[0].capabilities, [.exportRecord]) + XCTAssertEqual(snapshot.records[0].sourceChecksum, fingerprint) + XCTAssertEqual( + fingerprint, + SHA256.hash(data: output).map { String(format: "%02x", $0) }.joined() + ) + } + + func testBackgroundDiagnosticFormatDriftFailsWithoutGuessingOrLeakingRawOutput() async { + let privatePath = "/Users/example/Library/secret-token-value" + let output = Data(""" + Background Task Management changed format + Identifier: com.example.looks-valid + Executable Path: \(privatePath) + """.utf8) + let runner = RecordingAutomationCommandRunner(result: .success( + AutomationCommandResult(status: 0, standardOutput: output, standardError: Data()) + )) + let source = BackgroundTaskAutomationSource(runner: runner, diagnosticPolicy: .available) + + let snapshot = await source.snapshot() + let fingerprint = await source.currentRawOutputHash() + + XCTAssertTrue(snapshot.records.isEmpty) + XCTAssertEqual(snapshot.health.state, .failed) + XCTAssertFalse(snapshot.health.message?.contains(privatePath) ?? true) + XCTAssertEqual(fingerprint?.count, 64) + } + + func testBackgroundDiagnosticRejectsUnobservedExecutableAndParentURLAliases() async { + let output = Data(""" + #1: + Name: Alias Only + Identifier: com.example.alias-only + Executable URL: file:///tmp/devscope-fixtures/alias-helper + Parent URL: file:///tmp/devscope-fixtures/Alias.app + """.utf8) + let runner = RecordingAutomationCommandRunner(result: .success( + AutomationCommandResult(status: 0, standardOutput: output, standardError: Data()) + )) + let source = BackgroundTaskAutomationSource(runner: runner, diagnosticPolicy: .available) + + let snapshot = await source.snapshot() + + XCTAssertTrue(snapshot.records.isEmpty) + XCTAssertEqual(snapshot.health.state, .failed) + XCTAssertEqual( + snapshot.health.message, + "Some Background Task Management records were unresolved." + ) + } + + func testBackgroundDiagnosticKeepsUsableRecordsAndReportsUnresolvedBoundedRecords() async { + let output = Data(""" + #1: + Name: Bundle Identity + Bundle Identifier: com.example.bundle-identity + Unknown Secret: /Users/example/private-value + #2: + Name: Display Name Is Not Identity + Developer Name: Synthetic Developer + Type: agent + Disposition: allowed + """.utf8) + let runner = RecordingAutomationCommandRunner(result: .success( + AutomationCommandResult(status: 0, standardOutput: output, standardError: Data()) + )) + let source = BackgroundTaskAutomationSource(runner: runner, diagnosticPolicy: .available) + + let snapshot = await source.snapshot() + + XCTAssertEqual(snapshot.records.map(\.label), ["com.example.bundle-identity"]) + XCTAssertEqual(snapshot.health.state, .partial) + XCTAssertEqual( + snapshot.health.message, + "Some Background Task Management records were unresolved." + ) + XCTAssertFalse(snapshot.records[0].evidence.contains { + $0.detail.contains("private-value") + }) + } + + func testBackgroundDiagnosticBoundaryRequiresASCIIDigits() async { + let output = Data(""" + #١: + Identifier: com.example.unicode-boundary + Executable Path: /bin/sleep + """.utf8) + let runner = RecordingAutomationCommandRunner(result: .success( + AutomationCommandResult(status: 0, standardOutput: output, standardError: Data()) + )) + let source = BackgroundTaskAutomationSource(runner: runner, diagnosticPolicy: .available) + + let snapshot = await source.snapshot() + + XCTAssertTrue(snapshot.records.isEmpty) + XCTAssertEqual(snapshot.health.state, .failed) + XCTAssertEqual( + snapshot.health.message, + "Background Task Management record boundaries were not recognized." + ) + } + + func testBackgroundDiagnosticBoundaryDriftQuarantinesFollowingFields() async { + let privatePath = "/Users/example/private-drift-value" + let output = Data(""" + #1: + Identifier: com.example.valid-before-drift + Executable Path: /bin/sleep + #١: + Bundle Identifier: com.example.must-not-augment-first + #malformed: + Identifier: com.example.must-not-replace-first + Executable Path: \(privatePath) + """.utf8) + let runner = RecordingAutomationCommandRunner(result: .success( + AutomationCommandResult(status: 0, standardOutput: output, standardError: Data()) + )) + let source = BackgroundTaskAutomationSource(runner: runner, diagnosticPolicy: .available) + + let snapshot = await source.snapshot() + + XCTAssertEqual(snapshot.records.map(\.label), ["com.example.valid-before-drift"]) + let record = snapshot.records.first + XCTAssertEqual(record?.executable, "/bin/sleep") + XCTAssertNil(record?.providerBundleIdentifier) + XCTAssertEqual(snapshot.health.state, .partial) + XCTAssertFalse(snapshot.health.message?.contains(privatePath) ?? true) + XCTAssertFalse(snapshot.records.contains { record in + record.label.contains("must-not") + || record.executable?.contains("private-drift-value") == true + || record.evidence.contains { $0.detail.contains("must-not") } + }) + } + + func testBackgroundDiagnosticExactBoundaryRecoversAfterDriftQuarantine() async { + let output = Data(""" + #1: + Identifier: com.example.before-drift + Executable Path: /bin/sleep + #٢: + Identifier: com.example.quarantined-unicode + Executable Path: /bin/date + #not-a-number: + Bundle Identifier: com.example.quarantined-malformed + #2: + Identifier: com.example.after-drift + Executable Path: /usr/bin/true + """.utf8) + let runner = RecordingAutomationCommandRunner(result: .success( + AutomationCommandResult(status: 0, standardOutput: output, standardError: Data()) + )) + let source = BackgroundTaskAutomationSource(runner: runner, diagnosticPolicy: .available) + + let snapshot = await source.snapshot() + + XCTAssertEqual( + snapshot.records.map(\.label), + ["com.example.before-drift", "com.example.after-drift"] + ) + XCTAssertEqual(snapshot.records.map(\.executable), ["/bin/sleep", "/usr/bin/true"]) + XCTAssertEqual(snapshot.health.state, .partial) + XCTAssertFalse(snapshot.records.contains { record in + record.label.contains("quarantined") + || record.providerBundleIdentifier?.contains("quarantined") == true + }) + } + + func testLegacyProductionAdapterUsesOnlyFixedArgumentFreeProgramText() async throws { + let privatePath = "/tmp/devscope-fixtures/User Controlled.app" + let output = try JSONSerialization.data(withJSONObject: [[ + "name": "User Controlled", + "path": privatePath, + "hidden": false, + ]]) + let runner = RecordingAutomationCommandRunner(result: .success( + AutomationCommandResult(status: 0, standardOutput: output, standardError: Data()) + )) + let adapter = OSACommandLegacyLoginItemAdapter(runner: runner) + + let items = try await adapter.currentUserLoginItems() + + XCTAssertEqual(items.map(\.path), [privatePath]) + XCTAssertEqual(runner.invocations.count, 1) + XCTAssertEqual(runner.invocations[0].executable, "/usr/bin/osascript") + XCTAssertEqual(Array(runner.invocations[0].arguments.prefix(3)), ["-l", "JavaScript", "-e"]) + XCTAssertEqual(runner.invocations[0].arguments.count, 4) + XCTAssertFalse(runner.invocations[0].arguments[3].contains(privatePath)) + } + + func testBackgroundCommandAndEncodingFailuresRemainGenericAndSecretFree() async { + let privateDetail = "/Users/example/private-token" + let failedRunner = RecordingAutomationCommandRunner(result: .success( + AutomationCommandResult( + status: 1, + standardOutput: Data(privateDetail.utf8), + standardError: Data(privateDetail.utf8) + ) + )) + let invalidUTF8Runner = RecordingAutomationCommandRunner(result: .success( + AutomationCommandResult( + status: 0, + standardOutput: Data([0xFF, 0xFE]), + standardError: Data(privateDetail.utf8) + ) + )) + + let failedSource = BackgroundTaskAutomationSource( + runner: failedRunner, + diagnosticPolicy: .available + ) + let invalidUTF8Source = BackgroundTaskAutomationSource( + runner: invalidUTF8Runner, + diagnosticPolicy: .available + ) + let failed = await failedSource.snapshot() + let invalidUTF8 = await invalidUTF8Source.snapshot() + let failedFingerprint = await failedSource.currentRawOutputHash() + + for snapshot in [failed, invalidUTF8] { + XCTAssertTrue(snapshot.records.isEmpty) + XCTAssertEqual(snapshot.health.state, .failed) + XCTAssertFalse(snapshot.health.message?.contains(privateDetail) ?? true) + } + XCTAssertEqual( + failedFingerprint, + SHA256.hash(data: Data(privateDetail.utf8)) + .map { String(format: "%02x", $0) } + .joined() + ) + } + + func testLegacyProductionAdapterMapsAppleEventsFailureToPermissionDenial() async { + let runner = RecordingAutomationCommandRunner(result: .success( + AutomationCommandResult( + status: 1, + standardOutput: Data(), + standardError: Data("private Apple Events detail".utf8) + ) + )) + let adapter = OSACommandLegacyLoginItemAdapter(runner: runner) + + do { + _ = try await adapter.currentUserLoginItems() + XCTFail("Expected permission denial") + } catch { + XCTAssertEqual(error as? LegacyLoginItemAdapterError, .permissionDenied) + } + } + + func testBackgroundDiagnosticConflictingRecognizedFieldsAreUnresolved() async { + let output = Data(""" + #1: + Identifier: com.example.first + Identifier: com.example.second + Executable Path: /bin/sleep + """.utf8) + let runner = RecordingAutomationCommandRunner(result: .success( + AutomationCommandResult(status: 0, standardOutput: output, standardError: Data()) + )) + let source = BackgroundTaskAutomationSource(runner: runner, diagnosticPolicy: .available) + + let snapshot = await source.snapshot() + + XCTAssertTrue(snapshot.records.isEmpty) + XCTAssertEqual(snapshot.health.state, .failed) + XCTAssertEqual( + snapshot.health.message, + "Some Background Task Management records were unresolved." + ) + } +} + +private func legacySnapshot( + _ descriptor: LegacyLoginItemDescriptor +) async -> AutomationRecord { + let source = LegacyLoginItemAutomationSource( + adapter: StubLegacyLoginItemAdapter(result: .success([descriptor])), + currentUID: 501, + now: { Date(timeIntervalSince1970: 1_000) } + ) + let snapshot = await source.snapshot() + return try! XCTUnwrap(snapshot.records.first) +} + +private func sha256(_ data: Data) -> String { + SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined() +} + +private struct StubLegacyLoginItemAdapter: LegacyLoginItemListing { + let result: Result<[LegacyLoginItemDescriptor], LegacyLoginItemAdapterError> + + func currentUserLoginItems() async throws -> [LegacyLoginItemDescriptor] { + try result.get() + } +} diff --git a/Tests/DevScopeCoreTests/LongRunningAssessmentTests.swift b/Tests/DevScopeCoreTests/LongRunningAssessmentTests.swift new file mode 100644 index 0000000..d3408f0 --- /dev/null +++ b/Tests/DevScopeCoreTests/LongRunningAssessmentTests.swift @@ -0,0 +1,32 @@ +import XCTest +@testable import DevScopeCore + +final class LongRunningAssessmentTests: XCTestCase { + func testFourHourBoundaryIsInclusive() { + let below = DevProcessResourceUsage( + cpuPercent: 0, residentMemoryBytes: 1, elapsedTime: "03:59:59") + let boundary = DevProcessResourceUsage( + cpuPercent: 0, residentMemoryBytes: 1, elapsedTime: "04:00:00") + + XCTAssertFalse(LongRunningAssessment.isLongRunning(below, threshold: 14_400)) + XCTAssertTrue(LongRunningAssessment.isLongRunning(boundary, threshold: 14_400)) + } + + func testUnavailableElapsedTimeDoesNotBecomeLongRunning() { + let usage = DevProcessResourceUsage( + cpuPercent: 0, residentMemoryBytes: 1, elapsedTime: "-") + + XCTAssertFalse(LongRunningAssessment.isLongRunning(usage, threshold: 14_400)) + XCTAssertFalse(LongRunningAssessment.isLongRunning(nil, threshold: 14_400)) + } + + func testDefaultThresholdIsExactlyFourHoursAndInclusive() { + let below = DevProcessResourceUsage( + cpuPercent: 0, residentMemoryBytes: 1, elapsedTime: "03:59:59") + let boundary = DevProcessResourceUsage( + cpuPercent: 0, residentMemoryBytes: 1, elapsedTime: "04:00:00") + + XCTAssertFalse(LongRunningAssessment.isLongRunning(below)) + XCTAssertTrue(LongRunningAssessment.isLongRunning(boundary)) + } +} diff --git a/Tests/DevScopeCoreTests/PersistentCopyStoreTests.swift b/Tests/DevScopeCoreTests/PersistentCopyStoreTests.swift new file mode 100644 index 0000000..8de287d --- /dev/null +++ b/Tests/DevScopeCoreTests/PersistentCopyStoreTests.swift @@ -0,0 +1,107 @@ +import XCTest +@testable import DevScopeCore + +final class PersistentCopyStoreTests: XCTestCase { + func testSavesLoadsAndClearsLastCopiedPayload() throws { + let directory = FileManager.default.temporaryDirectory + .appendingPathComponent(UUID().uuidString, isDirectory: true) + let store = PersistentCopyStore(directory: directory) + defer { try? FileManager.default.removeItem(at: directory) } + + let timestamp = Date() + let saved = try store.save(text: "pid\tcommand\n1\tnode", label: "Visible rows", timestamp: timestamp) + let loaded = try XCTUnwrap(store.load()) + + XCTAssertEqual(loaded.label, "Visible rows") + XCTAssertEqual(loaded.text, "pid\tcommand\n1\tnode") + XCTAssertEqual(loaded.isTruncated, saved.isTruncated) + XCTAssertEqual(loaded.timestamp.timeIntervalSince1970, timestamp.timeIntervalSince1970, accuracy: 1.0) + + try store.clear() + XCTAssertNil(try store.load()) + } + + func testBoundsLargePayloadsForRecoveryCache() throws { + let directory = FileManager.default.temporaryDirectory + .appendingPathComponent(UUID().uuidString, isDirectory: true) + let store = PersistentCopyStore(directory: directory, maxStoredBytes: 64) + defer { try? FileManager.default.removeItem(at: directory) } + + let saved = try store.save(text: String(repeating: "a", count: 200), label: "Large export") + + XCTAssertTrue(saved.isTruncated) + XCTAssertLessThanOrEqual(Data(saved.text.utf8).count, 64) + XCTAssertTrue(saved.text.contains("[truncated")) + } + + func testBoundsPayloadWhenByteLimitIsSmallerThanTruncationMarker() throws { + let directory = FileManager.default.temporaryDirectory + .appendingPathComponent(UUID().uuidString, isDirectory: true) + let store = PersistentCopyStore(directory: directory, maxStoredBytes: 8) + defer { try? FileManager.default.removeItem(at: directory) } + + let saved = try store.save(text: "sensitive-value", label: "Small cache") + + XCTAssertTrue(saved.isTruncated) + XCTAssertLessThanOrEqual(Data(saved.text.utf8).count, 8) + } + + func testMaximumSizedEscapedPayloadCanBeLoadedAfterSaving() throws { + let directory = FileManager.default.temporaryDirectory + .appendingPathComponent(UUID().uuidString, isDirectory: true) + let maxStoredBytes = 70_000 + let store = PersistentCopyStore(directory: directory, maxStoredBytes: maxStoredBytes) + defer { try? FileManager.default.removeItem(at: directory) } + let text = String(repeating: "\"", count: maxStoredBytes) + + let saved = try store.save(text: text, label: "Escaped export") + let loaded = try XCTUnwrap(store.load()) + + XCTAssertFalse(saved.isTruncated) + XCTAssertEqual(loaded.label, saved.label) + XCTAssertEqual(loaded.text, saved.text) + XCTAssertEqual(loaded.isTruncated, saved.isTruncated) + XCTAssertEqual(loaded.timestamp.timeIntervalSince1970, saved.timestamp.timeIntervalSince1970, accuracy: 1.0) + } + + func testSaveRejectsPayloadWhoseEncodedMetadataExceedsReadableLimit() throws { + let directory = FileManager.default.temporaryDirectory + .appendingPathComponent(UUID().uuidString, isDirectory: true) + let store = PersistentCopyStore(directory: directory, maxStoredBytes: 64) + defer { try? FileManager.default.removeItem(at: directory) } + let oversizedLabel = String(repeating: "x", count: 70_000) + + XCTAssertThrowsError(try store.save(text: "value", label: oversizedLabel)) { error in + XCTAssertEqual(error as? PersistentCopyStoreError, .payloadTooLarge) + } + } + + func testRecoveryCacheUsesOwnerOnlyPermissions() throws { + let directory = FileManager.default.temporaryDirectory + .appendingPathComponent(UUID().uuidString, isDirectory: true) + let store = PersistentCopyStore(directory: directory) + defer { try? FileManager.default.removeItem(at: directory) } + + try store.save(text: "pid\tcommand\n1\tnode --token ", label: "Visible rows") + + let payloadURL = directory.appendingPathComponent("last-copy.json", isDirectory: false) + let directoryMode = try XCTUnwrap(FileManager.default.attributesOfItem(atPath: directory.path)[.posixPermissions] as? NSNumber) + let payloadMode = try XCTUnwrap(FileManager.default.attributesOfItem(atPath: payloadURL.path)[.posixPermissions] as? NSNumber) + + XCTAssertEqual(directoryMode.intValue & 0o777, 0o700) + XCTAssertEqual(payloadMode.intValue & 0o777, 0o600) + } + + func testLoadRejectsAnOversizedTamperedRecoveryCache() throws { + let directory = FileManager.default.temporaryDirectory + .appendingPathComponent(UUID().uuidString, isDirectory: true) + let store = PersistentCopyStore(directory: directory, maxStoredBytes: 64) + defer { try? FileManager.default.removeItem(at: directory) } + try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) + try Data(repeating: 0x41, count: 70_000).write( + to: directory.appendingPathComponent("last-copy.json") + ) + + XCTAssertThrowsError(try store.load()) + } +} diff --git a/Tests/DevScopeCoreTests/ProcessAccessAssessmentTests.swift b/Tests/DevScopeCoreTests/ProcessAccessAssessmentTests.swift new file mode 100644 index 0000000..77068ba --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessAccessAssessmentTests.swift @@ -0,0 +1,76 @@ +import DevScopeCore +import XCTest + +final class ProcessAccessAssessmentTests: XCTestCase { + func testSandboxedBuildDoesNotSuggestPrivacyPermissions() { + let assessment = ProcessAccessAssessment.assess( + isSandboxed: true, + processes: [], + errorDescription: "Native process scanning returned no visible processes" + ) + + XCTAssertTrue(assessment.hasBlockedRequirement) + XCTAssertFalse(assessment.hasNeededAction) + XCTAssertEqual(assessment.requirements.map(\.kind), [.processMetadata]) + XCTAssertEqual(assessment.requirements.first?.state, .blocked) + XCTAssertNil(assessment.requirements.first?.action) + } + + func testReadyProcessAndFolderAccessShowsNoNeededActions() { + let assessment = ProcessAccessAssessment.assess( + isSandboxed: false, + processes: [ + process(pid: 100, currentDirectory: "/Users/example/dev/sample-app"), + process(pid: 101, currentDirectory: "/Users/example/dev/sample-service") + ], + errorDescription: nil + ) + + XCTAssertFalse(assessment.hasBlockedRequirement) + XCTAssertFalse(assessment.hasNeededAction) + XCTAssertEqual(assessment.processCount, 2) + XCTAssertEqual(assessment.currentDirectoryCount, 2) + XCTAssertEqual(assessment.requirements.map(\.state), [.ready, .ready]) + } + + func testMissingFolderContextSuggestsFullDiskAccessOnly() { + let assessment = ProcessAccessAssessment.assess( + isSandboxed: false, + processes: [ + process(pid: 100, currentDirectory: nil), + process(pid: 101, currentDirectory: nil) + ], + errorDescription: nil + ) + + XCTAssertTrue(assessment.hasNeededAction) + XCTAssertEqual(assessment.requirements.map(\.kind), [.processMetadata, .workingDirectories]) + XCTAssertEqual(assessment.requirements.map(\.state), [.ready, .needed]) + XCTAssertEqual(assessment.requirements.last?.action, .fullDiskAccess) + } + + func testProcessScanFailureDoesNotSuggestUserGrantablePermission() { + let assessment = ProcessAccessAssessment.assess( + isSandboxed: false, + processes: nil, + errorDescription: "Process scanning is blocked by macOS" + ) + + XCTAssertTrue(assessment.hasBlockedRequirement) + XCTAssertFalse(assessment.hasNeededAction) + XCTAssertEqual(assessment.requirements.map(\.kind), [.processMetadata]) + XCTAssertEqual(assessment.requirements.first?.state, .blocked) + XCTAssertNil(assessment.requirements.first?.action) + } + + private func process(pid: Int32, currentDirectory: String?) -> DevProcess { + DevProcess( + pid: pid, + parentPID: 1, + executable: "python3.12", + command: "python train.py", + currentDirectory: currentDirectory, + resourceUsage: nil + ) + } +} diff --git a/Tests/DevScopeCoreTests/ProcessActionPolicyTests.swift b/Tests/DevScopeCoreTests/ProcessActionPolicyTests.swift new file mode 100644 index 0000000..5aa6195 --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessActionPolicyTests.swift @@ -0,0 +1,80 @@ +import Foundation +import XCTest + +@testable import DevScopeCore + +final class ProcessActionPolicyTests: XCTestCase { + func testProtectsPIDZero() { + let item = classified(pid: 0, executable: "/usr/bin/ordinary", kind: .other) + + let decision = ProcessActionPolicy.decision(for: item, currentProcessID: 9000) + + XCTAssertFalse(decision.isAllowed) + XCTAssertEqual(decision.reason, "macOS launch infrastructure is protected") + } + + func testProtectsPIDOneWithNonLaunchdExecutable() { + let item = classified(pid: 1, executable: "/usr/bin/ordinary", kind: .other) + + let decision = ProcessActionPolicy.decision(for: item, currentProcessID: 9000) + + XCTAssertFalse(decision.isAllowed) + XCTAssertEqual(decision.reason, "macOS launch infrastructure is protected") + } + + func testProtectsLaunchdAtAnOrdinaryPID() { + let item = classified(pid: 4200, executable: "/sbin/launchd", kind: .other) + + let decision = ProcessActionPolicy.decision(for: item, currentProcessID: 9000) + + XCTAssertFalse(decision.isAllowed) + XCTAssertEqual(decision.reason, "macOS launch infrastructure is protected") + } + + func testProtectsCriticalSystemService() { + let item = classified( + pid: 4200, executable: "/System/Library/CoreServices/WindowServer", kind: .systemService) + + let decision = ProcessActionPolicy.decision(for: item, currentProcessID: 9000) + + XCTAssertFalse(decision.isAllowed) + XCTAssertEqual(decision.reason, "Critical macOS system infrastructure is protected") + } + + func testProtectsCriticalExecutableEvenWhenClassificationDegrades() { + let item = classified( + pid: 4200, executable: "/System/Library/CoreServices/WindowServer", kind: .other) + + XCTAssertFalse(ProcessActionPolicy.decision(for: item, currentProcessID: 9000).isAllowed) + } + + func testProtectsTheRunningDevScopeProcess() { + let item = classified( + pid: 42, executable: "/Applications/DevScope.app/Contents/MacOS/DevScope", kind: .macApp) + + let decision = ProcessActionPolicy.decision(for: item, currentProcessID: 42) + + XCTAssertFalse(decision.isAllowed) + XCTAssertEqual(decision.reason, "DevScope cannot terminate itself") + } + + func testAllowsAUserOwnedDevelopmentProcess() { + let item = classified(pid: 4200, executable: "/opt/homebrew/bin/node", kind: .javascript) + + XCTAssertEqual( + ProcessActionPolicy.decision(for: item, currentProcessID: 42), + .allowed + ) + } + + private func classified(pid: Int32, executable: String, kind: DevRuntimeKind) + -> ClassifiedDevProcess + { + ClassifiedDevProcess( + process: DevProcess(pid: pid, parentPID: 1, executable: executable, command: executable), + classification: DevProcessClassification( + kind: kind, displayName: URL(fileURLWithPath: executable).lastPathComponent, + projectHint: nil) + ) + } +} diff --git a/Tests/DevScopeCoreTests/ProcessActionPresentationTests.swift b/Tests/DevScopeCoreTests/ProcessActionPresentationTests.swift new file mode 100644 index 0000000..ef5d84f --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessActionPresentationTests.swift @@ -0,0 +1,56 @@ +import XCTest +@testable import DevScopeCore + +final class ProcessActionPresentationTests: XCTestCase { + func testEndedProcessGuidanceWinsForEveryDestructiveControl() { + for control in ProcessActionControl.allCases { + let presentation = ProcessActionPresentation.control( + control, + isEnded: true, + actionDecision: .protected(reason: "policy reason must not win") + ) + + XCTAssertTrue(presentation.isDisabled) + XCTAssertEqual(presentation.help, "Unavailable: This process has ended.") + XCTAssertEqual( + presentation.accessibilityHint, + "Unavailable because the selected process has ended." + ) + } + } + + func testProtectedProcessDisablesEveryDestructiveControlWithPolicyGuidance() { + let reason = "Critical macOS system infrastructure is protected" + + for control in ProcessActionControl.allCases { + let presentation = ProcessActionPresentation.control( + control, + isEnded: false, + actionDecision: .protected(reason: reason) + ) + + XCTAssertTrue(presentation.isDisabled) + XCTAssertEqual(presentation.help, "Unavailable: \(reason)") + XCTAssertEqual(presentation.accessibilityHint, "Unavailable: \(reason)") + } + } + + func testTerminationConsequencesStaySpecificToTheSelectedAction() { + XCTAssertEqual( + ProcessTerminationAction.single.consequence(pid: 77, descendantCount: 2), + "Sends SIGTERM to PID 77. The process may save state before exiting." + ) + XCTAssertEqual( + ProcessTerminationAction.tree.consequence(pid: 77, descendantCount: 2), + "Sends SIGTERM to PID 77 after 2 descendant processes." + ) + XCTAssertEqual( + ProcessTerminationAction.forceSingle.consequence(pid: 77, descendantCount: 2), + "Sends SIGKILL to PID 77. The process cannot save state." + ) + XCTAssertEqual( + ProcessTerminationAction.forceTree.consequence(pid: 77, descendantCount: 1), + "Sends SIGKILL to PID 77 and 1 descendant process. None can save state." + ) + } +} diff --git a/Tests/DevScopeCoreTests/ProcessCacheIdentityTests.swift b/Tests/DevScopeCoreTests/ProcessCacheIdentityTests.swift new file mode 100644 index 0000000..a0fced2 --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessCacheIdentityTests.swift @@ -0,0 +1,89 @@ +import XCTest +@testable import DevScopeCore + +final class ProcessCacheIdentityTests: XCTestCase { + func testKnownBirthIdentityIgnoresMetadataEnrichment() { + let birthToken = ProcessBirthToken(seconds: 1_000, microseconds: 42) + let initial = DevProcess( + pid: 700, + parentPID: 1, + executable: "node", + command: "node", + birthToken: birthToken + ) + let enriched = DevProcess( + pid: 700, + parentPID: 99, + executable: "/opt/homebrew/bin/node", + command: "/opt/homebrew/bin/node server.js", + birthToken: birthToken + ) + + XCTAssertEqual( + ProcessCacheIdentity(process: initial), + ProcessCacheIdentity(process: enriched) + ) + } + + func testUnknownBirthUsesConservativeProcessMetadataFallback() { + let original = processWithoutBirth() + let originalIdentity = ProcessCacheIdentity(process: original) + + XCTAssertEqual( + originalIdentity, + ProcessCacheIdentity(process: processWithoutBirth()) + ) + XCTAssertNotEqual( + originalIdentity, + ProcessCacheIdentity(process: processWithoutBirth(parentPID: 2)) + ) + XCTAssertNotEqual( + originalIdentity, + ProcessCacheIdentity(process: processWithoutBirth(executable: "python")) + ) + XCTAssertNotEqual( + originalIdentity, + ProcessCacheIdentity(process: processWithoutBirth(command: "worker --other")) + ) + } + + func testKnownBirthChangeAndBirthAvailabilityTransitionChangeIdentity() { + let withoutBirth = processWithoutBirth() + let firstBirth = DevProcess( + pid: withoutBirth.pid, + parentPID: withoutBirth.parentPID, + executable: withoutBirth.executable, + command: withoutBirth.command, + birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 1) + ) + let secondBirth = DevProcess( + pid: firstBirth.pid, + parentPID: firstBirth.parentPID, + executable: firstBirth.executable, + command: firstBirth.command, + birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 2) + ) + + XCTAssertNotEqual( + ProcessCacheIdentity(process: firstBirth), + ProcessCacheIdentity(process: secondBirth) + ) + XCTAssertNotEqual( + ProcessCacheIdentity(process: withoutBirth), + ProcessCacheIdentity(process: firstBirth) + ) + } + + private func processWithoutBirth( + parentPID: Int32 = 1, + executable: String = "worker", + command: String = "worker --serve" + ) -> DevProcess { + DevProcess( + pid: 700, + parentPID: parentPID, + executable: executable, + command: command + ) + } +} diff --git a/Tests/DevScopeCoreTests/ProcessClassificationCacheTests.swift b/Tests/DevScopeCoreTests/ProcessClassificationCacheTests.swift new file mode 100644 index 0000000..dec87bf --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessClassificationCacheTests.swift @@ -0,0 +1,186 @@ +import XCTest +@testable import DevScopeCore + +final class ProcessClassificationCacheTests: XCTestCase { + func testBudgetedWorkspaceRevalidationProgressivelyReclassifiesChangedFacts() throws { + var cache = ProcessClassificationCache() + var classificationCalls = 0 + var flutterDirectories: Set = [] + let workspaceFactsCache = WorkspaceFactsCache { path in + flutterDirectories.contains(String(path.dropLast("/pubspec.yaml".count))) && + path.hasSuffix("/pubspec.yaml") + } + let processes = (1...9).map { index in + process( + pid: Int32(700 + index), + command: "workspace-runner serve", + currentDirectory: "/workspace/\(index)" + ) + } + + _ = cache.classified(processes, workspaceFactsCache: workspaceFactsCache) { process, workspaceFacts in + classificationCalls += 1 + return ProcessClassifier.classify(process, workspaceFacts: workspaceFacts) + } + flutterDirectories.insert("/workspace/9") + workspaceFactsCache.invalidateAll() + let firstCycle = cache.classified( + processes, + workspaceFactsCache: workspaceFactsCache + ) { process, workspaceFacts in + classificationCalls += 1 + return ProcessClassifier.classify(process, workspaceFacts: workspaceFacts) + } + let secondCycle = cache.classified( + processes, + workspaceFactsCache: workspaceFactsCache + ) { process, workspaceFacts in + classificationCalls += 1 + return ProcessClassifier.classify(process, workspaceFacts: workspaceFacts) + } + + XCTAssertEqual(try XCTUnwrap(firstCycle.last).classification.kind, .other) + XCTAssertEqual(try XCTUnwrap(secondCycle.last).classification.kind, .flutter) + XCTAssertEqual(classificationCalls, 10) + } + + func testReusesClassificationWhenInvalidatedWorkspaceFactsAreUnchanged() { + var cache = ProcessClassificationCache() + var classificationCalls = 0 + var workspaceProbeCount = 0 + let workspaceFactsCache = WorkspaceFactsCache { _ in + workspaceProbeCount += 1 + return false + } + let unchanged = process() + + _ = cache.classified( + [unchanged], + workspaceFactsCache: workspaceFactsCache + ) { process, workspaceFacts in + classificationCalls += 1 + return ProcessClassifier.classify(process, workspaceFacts: workspaceFacts) + } + let initialWorkspaceProbeCount = workspaceProbeCount + workspaceFactsCache.invalidateAll() + _ = cache.classified( + [unchanged], + workspaceFactsCache: workspaceFactsCache + ) { process, workspaceFacts in + classificationCalls += 1 + return ProcessClassifier.classify(process, workspaceFacts: workspaceFacts) + } + + XCTAssertEqual(classificationCalls, 1) + XCTAssertGreaterThan(workspaceProbeCount, initialWorkspaceProbeCount) + } + + func testReclassifiesImmediatelyWhenRefreshedWorkspaceFactsChange() throws { + var cache = ProcessClassificationCache() + var classificationCalls = 0 + var manifestExists = false + let workspaceFactsCache = WorkspaceFactsCache { path in + manifestExists && path.hasSuffix("/pubspec.yaml") + } + let process = process(command: "workspace-runner serve") + + let beforeManifest = cache.classified( + [process], + workspaceFactsCache: workspaceFactsCache + ) { process, workspaceFacts in + classificationCalls += 1 + return ProcessClassifier.classify(process, workspaceFacts: workspaceFacts) + } + manifestExists = true + workspaceFactsCache.invalidateAll() + let afterManifest = cache.classified( + [process], + workspaceFactsCache: workspaceFactsCache + ) { process, workspaceFacts in + classificationCalls += 1 + return ProcessClassifier.classify(process, workspaceFacts: workspaceFacts) + } + + XCTAssertEqual(classificationCalls, 2) + XCTAssertEqual(try XCTUnwrap(beforeManifest.first).classification.kind, .other) + XCTAssertEqual(try XCTUnwrap(afterManifest.first).classification.kind, .flutter) + } + + func testReusesClassificationWhenOnlyMetricsChange() throws { + var cache = ProcessClassificationCache() + var classificationCalls = 0 + let first = process(cpu: 1) + let second = process(cpu: 80) + + _ = cache.classified([first]) { process in + classificationCalls += 1 + return ProcessClassifier.classify(process) + } + let updated = cache.classified([second]) { process in + classificationCalls += 1 + return ProcessClassifier.classify(process) + } + + XCTAssertEqual(classificationCalls, 1) + XCTAssertEqual(try XCTUnwrap(updated.first).process.resourceUsage?.cpuPercent, 80) + } + + func testReclassifiesWhenCommandChangesAndDropsMissingPIDs() { + var cache = ProcessClassificationCache() + var calls = 0 + _ = cache.classified([process(command: "node vite")]) { process in + calls += 1 + return ProcessClassifier.classify(process) + } + let updated = cache.classified([process(command: "python worker.py")]) { process in + calls += 1 + return ProcessClassifier.classify(process) + } + let empty = cache.classified([]) { process in + calls += 1 + return ProcessClassifier.classify(process) + } + + XCTAssertEqual(calls, 2) + XCTAssertEqual(updated.first?.classification.kind, .python) + XCTAssertTrue(empty.isEmpty) + XCTAssertEqual(cache.cachedProcessIDs, []) + } + + func testReclassifiesRecycledPIDWhenBirthTokenChanges() throws { + var cache = ProcessClassificationCache() + var calls = 0 + let first = process(birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 100)) + let recycled = process(birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 101)) + + _ = cache.classified([first]) { process in + calls += 1 + return ProcessClassifier.classify(process) + } + let updated = cache.classified([recycled]) { process in + calls += 1 + return ProcessClassifier.classify(process) + } + + XCTAssertEqual(calls, 2) + XCTAssertEqual(try XCTUnwrap(updated.first).process.birthToken, recycled.birthToken) + } + + private func process( + pid: Int32 = 700, + cpu: Double = 1, + command: String = "node vite", + currentDirectory: String = NSHomeDirectory() + "/dev/example/devscope", + birthToken: ProcessBirthToken? = nil + ) -> DevProcess { + DevProcess( + pid: pid, + parentPID: 1, + executable: command.split(separator: " ").first.map(String.init) ?? command, + command: command, + currentDirectory: currentDirectory, + resourceUsage: DevProcessResourceUsage(cpuPercent: cpu, residentMemoryBytes: 100, elapsedTime: "00:10"), + birthToken: birthToken + ) + } +} diff --git a/Tests/DevScopeCoreTests/ProcessClassifierTests.swift b/Tests/DevScopeCoreTests/ProcessClassifierTests.swift new file mode 100644 index 0000000..8263788 --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessClassifierTests.swift @@ -0,0 +1,750 @@ +import XCTest +@testable import DevScopeCore + +final class ProcessClassifierTests: XCTestCase { + func testCachedFlutterWorkspaceFactsAvoidRepeatedFileProbe() { + var probeCount = 0 + let cache = WorkspaceFactsCache { path in + probeCount += 1 + return path.hasSuffix("/pubspec.yaml") + } + let directory = NSHomeDirectory() + "/dev/example/app" + + XCTAssertTrue(cache.isFlutterWorkspace(directory)) + XCTAssertTrue(cache.isFlutterWorkspace(directory)) + XCTAssertEqual(probeCount, 1) + } + + func testSuppliedWorkspaceFactsDriveFlutterClassification() throws { + let process = DevProcess( + pid: 99, + parentPID: 1, + executable: "workspace-runner", + command: "workspace-runner serve", + currentDirectory: NSHomeDirectory() + "/dev/example/app" + ) + + let classification = try XCTUnwrap( + ProcessClassifier.classify( + process, + workspaceFacts: WorkspaceFacts(isFlutterWorkspace: true) + ) + ) + + XCTAssertEqual(classification.kind, .flutter) + } + + func testSuppliedNonFlutterFactsOverridePathHeuristic() throws { + let process = DevProcess( + pid: 100, + parentPID: 1, + executable: "workspace-runner", + command: "workspace-runner serve", + currentDirectory: NSHomeDirectory() + "/dev/flutter/app" + ) + + let classification = try XCTUnwrap( + ProcessClassifier.classify( + process, + workspaceFacts: WorkspaceFacts(isFlutterWorkspace: false) + ) + ) + + XCTAssertEqual(classification.kind, .other) + } + + func testWorkspaceFactsInvalidationRefreshesFileProbe() { + var manifestExists = false + var probeCount = 0 + let cache = WorkspaceFactsCache { path in + probeCount += 1 + return manifestExists && path.hasSuffix("/pubspec.yaml") + } + let directory = NSHomeDirectory() + "/dev/example/app" + + XCTAssertFalse(cache.isFlutterWorkspace(directory)) + manifestExists = true + XCTAssertFalse(cache.isFlutterWorkspace(directory)) + cache.invalidateAll() + XCTAssertTrue(cache.isFlutterWorkspace(directory)) + XCTAssertEqual(probeCount, 3) + } + + func testWorkspaceFactsClassificationCacheReturnsFreshMetricsWithoutRepeatedProbe() throws { + var probeCount = 0 + let workspaceFactsCache = WorkspaceFactsCache { path in + probeCount += 1 + return path.hasSuffix("/pubspec.yaml") + } + var cache = ProcessClassificationCache() + let directory = NSHomeDirectory() + "/dev/example/app" + let first = DevProcess( + pid: 101, + parentPID: 1, + executable: "workspace-runner", + command: "workspace-runner serve", + currentDirectory: directory, + resourceUsage: DevProcessResourceUsage(cpuPercent: 1, residentMemoryBytes: 100, elapsedTime: "00:01") + ) + let second = DevProcess( + pid: 101, + parentPID: 1, + executable: "workspace-runner", + command: "workspace-runner serve", + currentDirectory: directory, + resourceUsage: DevProcessResourceUsage(cpuPercent: 90, residentMemoryBytes: 100, elapsedTime: "00:03") + ) + + _ = cache.classified([first], workspaceFactsCache: workspaceFactsCache) + let classified = cache.classified([second], workspaceFactsCache: workspaceFactsCache) + + XCTAssertEqual(probeCount, 1) + XCTAssertEqual(try XCTUnwrap(classified.first).classification.kind, .flutter) + XCTAssertEqual(classified.first?.process.resourceUsage?.cpuPercent, 90) + } + + func testParsesPsRowsWithCommandContainingSpaces() throws { + let row = " 4312 112 /opt/homebrew/bin/node node ./node_modules/.bin/next dev --port 3014" + + let process = try XCTUnwrap(ProcessScanner.parsePSLine(row)) + + XCTAssertEqual(process.pid, 4312) + XCTAssertEqual(process.parentPID, 112) + XCTAssertEqual(process.executable, "/opt/homebrew/bin/node") + XCTAssertEqual(process.command, "node ./node_modules/.bin/next dev --port 3014") + } + + func testParsesPsRowsWithResourceMetrics() throws { + let row = " 4312 112 12.5 65536 01:02:03 /opt/homebrew/bin/node node ./node_modules/.bin/vite --host 0.0.0.0" + + let process = try XCTUnwrap(ProcessScanner.parsePSLine(row)) + + XCTAssertEqual(process.pid, 4312) + XCTAssertEqual(process.parentPID, 112) + XCTAssertEqual(process.executable, "/opt/homebrew/bin/node") + XCTAssertEqual(process.command, "node ./node_modules/.bin/vite --host 0.0.0.0") + XCTAssertEqual(process.resourceUsage?.cpuPercent, 12.5) + XCTAssertEqual(process.resourceUsage?.residentMemoryBytes, 67_108_864) + XCTAssertEqual(process.resourceUsage?.elapsedTime, "01:02:03") + } + + func testClassifiesExpectedDevProcesses() throws { + let samples: [(String, DevRuntimeKind)] = [ + ("node ./node_modules/.bin/next dev --port 3014", .javascript), + ("npm run dev", .javascript), + ("pnpm vite --host 0.0.0.0", .javascript), + ("python scripts/smoke.py --watch", .python), + ("python3 -m uvicorn app.main:app --reload", .python), + ("pytest tests/test_api.py -v", .python), + ("swift build", .swift), + ("cargo watch -x run", .rust), + ("go run ./cmd/server", .go), + ("flutter run -d macos", .flutter), + ("dart run build_runner watch", .flutter), + ("basic-memory mcp --transport stdio", .mcp) + ] + + for (command, expectedKind) in samples { + let process = DevProcess(pid: 10, parentPID: 1, executable: command.split(separator: " ").first.map(String.init) ?? command, command: command) + + let classification = try XCTUnwrap(ProcessClassifier.classify(process)) + + XCTAssertEqual(classification.kind, expectedKind, command) + } + } + + func testClassifiesFlutterWorkspaceProcessesWithoutLeavingThemAsOther() throws { + let home = NSHomeDirectory() + let samples = [ + DevProcess( + pid: 11, + parentPID: 1, + executable: "/Applications/Xcode.app/Contents/MacOS/Xcode", + command: "/Applications/Xcode.app/Contents/MacOS/Xcode", + currentDirectory: "\(home)/dev/flutter/usafe" + ), + DevProcess( + pid: 12, + parentPID: 1, + executable: "dart", + command: "dart \(home)/dev/flutter/usafe/.dart_tool/flutter_build/frontend_server.dart.snapshot", + currentDirectory: "\(home)/dev/flutter/usafe" + ) + ] + + for process in samples { + let classification = try XCTUnwrap(ProcessClassifier.classify(process), process.command) + XCTAssertEqual(classification.kind, .flutter) + XCTAssertEqual(classification.projectHint, "usafe") + } + } + + func testClassifiesSandboxNativeScannerAppPaths() throws { + let samples: [(DevProcess, DevRuntimeKind)] = [ + ( + DevProcess( + pid: 13, + parentPID: 1, + executable: "Cursor", + command: "/Applications/Cursor.app/Contents/MacOS/Cursor" + ), + .javascript + ), + ( + DevProcess( + pid: 14, + parentPID: 1, + executable: "Xcode", + command: "/Applications/Xcode.app/Contents/MacOS/Xcode" + ), + .swift + ), + ( + DevProcess( + pid: 15, + parentPID: 1, + executable: "Ollama", + command: "/Applications/Ollama.app/Contents/MacOS/Ollama" + ), + .ai + ) + ] + + for (process, expectedKind) in samples { + let classification = try XCTUnwrap(ProcessClassifier.classify(process), process.command) + XCTAssertEqual(classification.kind, expectedKind) + } + } + + func testClassifiesWholeMachineActivityInsteadOfDroppingNonDevRows() throws { + let samples: [(DevProcess, DevRuntimeKind, String)] = [ + ( + DevProcess( + pid: 20, + parentPID: 1, + executable: "/System/Library/CoreServices/Finder.app/Contents/MacOS/Finder", + command: "/System/Library/CoreServices/Finder.app/Contents/MacOS/Finder" + ), + .macApp, + "Finder" + ), + ( + DevProcess( + pid: 21, + parentPID: 1, + executable: "/Applications/Go", + command: "/Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Helper.app/Contents/MacOS/Google Chrome Helper --type=renderer", + currentDirectory: "/" + ), + .browser, + "Google Chrome Helper" + ), + ( + DevProcess( + pid: 22, + parentPID: 1, + executable: "/System/Library/PrivateFrameworks/CloudKitDaemon.framework/Support/cloudd", + command: "/System/Library/PrivateFrameworks/CloudKitDaemon.framework/Support/cloudd" + ), + .backgroundAgent, + "cloudd" + ), + ( + DevProcess( + pid: 23, + parentPID: 1, + executable: "/usr/libexec/logd", + command: "/usr/libexec/logd" + ), + .systemService, + "logd" + ), + ( + DevProcess( + pid: 24, + parentPID: 1, + executable: "/opt/custom/bin/worker", + command: "/opt/custom/bin/worker --foreground" + ), + .other, + "worker" + ) + ] + + for (process, expectedKind, expectedDisplayName) in samples { + let classification = try XCTUnwrap(ProcessClassifier.classify(process), process.command) + XCTAssertEqual(classification.kind, expectedKind) + XCTAssertEqual(classification.displayName, expectedDisplayName) + } + } + + func testDerivesSystemProcessNameFromCommandWhenExecutableIsGenericPathFragment() throws { + let samples: [(DevProcess, DevRuntimeKind, String)] = [ + ( + DevProcess( + pid: 25, + parentPID: 1, + executable: "Library", + command: "/System/Library/PrivateFrameworks/EmailDaemon.framework/Versions/A/maild" + ), + .backgroundAgent, + "maild" + ), + ( + DevProcess( + pid: 26, + parentPID: 1, + executable: "Contents", + command: "/System/Library/CoreServices/Spotlight.app/Contents/MacOS/Spotlight" + ), + .macApp, + "Spotlight" + ), + ( + DevProcess( + pid: 27, + parentPID: 1, + executable: "Versions", + command: "/System/Library/PrivateFrameworks/CloudKitDaemon.framework/Versions/A/Support/cloudd" + ), + .backgroundAgent, + "cloudd" + ) + ] + + for (process, expectedKind, expectedDisplayName) in samples { + let classification = try XCTUnwrap(ProcessClassifier.classify(process), process.command) + XCTAssertEqual(classification.kind, expectedKind) + XCTAssertEqual(classification.displayName, expectedDisplayName) + XCTAssertNotEqual(classification.displayName.lowercased(), process.executable.lowercased()) + } + } + + func testDerivesSystemProcessNameFromCommandWhenExecutableIsShortPrefix() throws { + let samples: [(DevProcess, String)] = [ + ( + DevProcess( + pid: 28, + parentPID: 1, + executable: "/usr/libexec/Use", + command: "/usr/libexec/UserEventAgent (System)" + ), + "UserEventAgent" + ), + ( + DevProcess( + pid: 29, + parentPID: 1, + executable: "/usr/libexec/con", + command: "/usr/libexec/configd" + ), + "configd" + ), + ( + DevProcess( + pid: 30, + parentPID: 1, + executable: "/usr/libexec/IOM", + command: "/usr/libexec/IOMFB_bics_daemon" + ), + "IOMFB_bics_daemon" + ), + ( + DevProcess( + pid: 31, + parentPID: 1, + executable: "/usr/libexec/log", + command: "/usr/libexec/logd" + ), + "logd" + ) + ] + + for (process, expectedDisplayName) in samples { + let classification = try XCTUnwrap(ProcessClassifier.classify(process), process.command) + XCTAssertEqual(classification.kind, .systemService) + XCTAssertEqual(classification.displayName, expectedDisplayName) + } + } + + func testClassifiedSnapshotIncludesAllScannedRows() { + let processes = [ + DevProcess(pid: 31, parentPID: 1, executable: "node", command: "node ./node_modules/.bin/vite"), + DevProcess(pid: 32, parentPID: 1, executable: "/System/Library/CoreServices/Finder.app/Contents/MacOS/Finder", command: "/System/Library/CoreServices/Finder.app/Contents/MacOS/Finder"), + DevProcess(pid: 33, parentPID: 1, executable: "/usr/libexec/logd", command: "/usr/libexec/logd"), + DevProcess(pid: 34, parentPID: 1, executable: "/opt/custom/bin/worker", command: "/opt/custom/bin/worker --foreground") + ] + + let classified = ProcessClassifier.classified(processes) + + XCTAssertEqual(classified.map(\.process.pid), [31, 32, 33, 34]) + XCTAssertEqual(classified.map(\.classification.kind), [.javascript, .macApp, .systemService, .other]) + } + + func testClassifiesSystemProcessesWhoseTruncatedExecutableLooksLikeADevToolByCommandPath() throws { + let samples = [ + ( + DevProcess( + pid: 25, + parentPID: 1, + executable: "/usr/libexec/Air", + command: "/usr/libexec/AirPlayXPCHelper", + currentDirectory: "/" + ), + DevRuntimeKind.systemService + ), + ( + DevProcess( + pid: 26, + parentPID: 1, + executable: "/Applications/Go", + command: "/Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Helper.app/Contents/MacOS/Google Chrome Helper", + currentDirectory: "/" + ), + DevRuntimeKind.browser + ) + ] + + for (process, expectedKind) in samples { + let classification = try XCTUnwrap(ProcessClassifier.classify(process), process.command) + XCTAssertEqual(classification.kind, expectedKind) + } + } + + func testDerivesReadableDisplayNameAndProjectHint() throws { + let process = DevProcess(pid: 30, parentPID: 1, executable: "node", command: "node \(NSHomeDirectory())/dev/mission-control/node_modules/.bin/next dev --port 3010") + + let classification = try XCTUnwrap(ProcessClassifier.classify(process)) + + XCTAssertEqual(classification.displayName, "next dev") + XCTAssertEqual(classification.projectHint, "mission-control") + } + + func testDerivesCodexPluginProjectHintFromCachePath() throws { + let process = DevProcess( + pid: 30, + parentPID: 1, + executable: "\(NSHomeDirectory())/.codex/plugins/cache/openai-bundled/computer-use/1.0.829/Codex", + command: "\(NSHomeDirectory())/.codex/plugins/cache/openai-bundled/computer-use/1.0.829/Codex", + currentDirectory: "\(NSHomeDirectory())/.codex/plugins/cache/openai-bundled/computer-use/1.0.829" + ) + + let classification = try XCTUnwrap(ProcessClassifier.classify(process)) + + XCTAssertEqual(classification.projectHint, "computer-use") + } + + func testDerivesReadablePythonAndNodeProcessNamesFromCommands() throws { + let samples: [(DevProcess, String)] = [ + ( + DevProcess( + pid: 31, + parentPID: 1, + executable: "\(NSHomeDirectory())/dev/job_hunt/.venv/bin/python3.11", + command: "\(NSHomeDirectory())/dev/job_hunt/.venv/bin/python3.11 \(NSHomeDirectory())/dev/job_hunt/.venv/bin/uvicorn forge_app.app:app --host 127.0.0.1", + currentDirectory: "\(NSHomeDirectory())/dev/job_hunt" + ), + "uvicorn forge_app" + ), + ( + DevProcess( + pid: 32, + parentPID: 1, + executable: "\(NSHomeDirectory())/.hermes/hermes-agent/venv/bin/python", + command: "\(NSHomeDirectory())/.hermes/hermes-agent/venv/bin/python -m hermes_cli.main gateway run --replace", + currentDirectory: "\(NSHomeDirectory())/.hermes/hermes-agent" + ), + "python module hermes_cli.main" + ), + ( + DevProcess( + pid: 33, + parentPID: 1, + executable: "node", + command: "node ./mcp/server.bundle.mjs", + currentDirectory: "\(NSHomeDirectory())/.codex/plugins/cache/openai-curated" + ), + "MCP server" + ), + ( + DevProcess( + pid: 34, + parentPID: 1, + executable: "node", + command: "node \(NSHomeDirectory())/.npm/_npx/99336612077b7094/node_modules/.bin/xcodebuildmcp mcp", + currentDirectory: "\(NSHomeDirectory())/.codex" + ), + "xcodebuildmcp" + ), + ( + DevProcess( + pid: 35, + parentPID: 1, + executable: "/opt/homebrew/Cellar/python@3.14/3.14.6/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python", + command: "/opt/homebrew/Cellar/python@3.14/3.14.6/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python \(NSHomeDirectory())/.cursor/extensions/ms-python.python-2025.6.1/python_files/run-jedi-language-server.py", + currentDirectory: "\(NSHomeDirectory())/dev/example/sample-service" + ), + "python run-jedi-language-server" + ), + ( + DevProcess( + pid: 36, + parentPID: 1, + executable: "/opt/homebrew/Cellar/python@3.14/3.14.6/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python", + command: "/opt/homebrew/Cellar/python@3.14/3.14.6/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python -c from multiprocessing.resource_tracker import main", + currentDirectory: "\(NSHomeDirectory())/dev/example/sample-service" + ), + "python command" + ) + ] + + for (process, expectedDisplayName) in samples { + let classification = try XCTUnwrap(ProcessClassifier.classify(process), process.command) + XCTAssertEqual(classification.displayName, expectedDisplayName) + } + } + + func testDerivesReadableMCPAndToolLauncherNamesFromCommands() throws { + let samples: [(DevProcess, String)] = [ + ( + DevProcess( + pid: 38, + parentPID: 1, + executable: "npm", + command: "npm exec xcodebuildmcp@latest mcp", + currentDirectory: "\(NSHomeDirectory())/.codex" + ), + "xcodebuildmcp" + ), + ( + DevProcess( + pid: 39, + parentPID: 1, + executable: "npm", + command: "npm exec shadcn@latest mcp", + currentDirectory: "\(NSHomeDirectory())/.codex" + ), + "shadcn MCP" + ), + ( + DevProcess( + pid: 42, + parentPID: 1, + executable: "node", + command: "node \(NSHomeDirectory())/.npm/_npx/99336612077b7094/node_modules/.bin/xcodebuildmcp mcp", + currentDirectory: "\(NSHomeDirectory())/.codex" + ), + "xcodebuildmcp" + ), + ( + DevProcess( + pid: 43, + parentPID: 1, + executable: "node", + command: "node ./mcp/server.cjs --stdio", + currentDirectory: "\(NSHomeDirectory())/dev/example/devscope" + ), + "MCP server" + ), + ( + DevProcess( + pid: 44, + parentPID: 1, + executable: "basic-memory", + command: "basic-memory mcp --transport stdio", + currentDirectory: "\(NSHomeDirectory())/.codex" + ), + "basic-memory MCP" + ) + ] + + for (process, expectedDisplayName) in samples { + let classification = try XCTUnwrap(ProcessClassifier.classify(process), process.command) + XCTAssertEqual(classification.displayName, expectedDisplayName) + } + } + + func testDerivesReadableNamesForTruncatedMacAppExecutablesInDevWorkspaces() throws { + let samples: [(DevProcess, DevRuntimeKind, String)] = [ + ( + DevProcess( + pid: 45, + parentPID: 1, + executable: "/Applications/Cu", + command: "/Applications/Cursor.app/Contents/Frameworks/Cursor Helper.app/Contents/MacOS/Cursor Helper --type=renderer --clientProcessId=69096", + currentDirectory: "\(NSHomeDirectory())/dev/example/sample-service" + ), + .other, + "Cursor" + ), + ( + DevProcess( + pid: 47, + parentPID: 1, + executable: "./Codex", + command: "Computer ./Codex Computer Use.app/Contents/SharedSupport/SkyComputerUseClient.app/Contents/MacOS/SkyComputerUseClient mcp", + currentDirectory: "\(NSHomeDirectory())/.codex/plugins/cache/openai-bundled/computer-use/1.0.829" + ), + .mcp, + "Computer Use MCP" + ) + ] + + for (process, expectedKind, displayName) in samples { + let classification = try XCTUnwrap(ProcessClassifier.classify(process)) + + XCTAssertEqual(classification.kind, expectedKind) + XCTAssertEqual(classification.displayName, displayName) + } + } + + func testDerivesReadableNamesForShellWrappedPythonCommands() throws { + let process = DevProcess( + pid: 46, + parentPID: 1, + executable: "/bin/zsh", + command: "/bin/zsh -c snap=$(command cat <&3); builtin unalias -m '*' 2>/dev/null || true; cd \(NSHomeDirectory())/dev/example/sample-service/research-lab && PYTHONPATH=. conda run -n research-lab python3 -c \"print('pages')\"", + currentDirectory: "\(NSHomeDirectory())/dev/example/sample-service/research-lab" + ) + + let classification = try XCTUnwrap(ProcessClassifier.classify(process)) + + XCTAssertEqual(classification.kind, .shell) + XCTAssertEqual(classification.displayName, "python command") + } + + func testDerivesReadableAIProcessNamesWithoutModelGuessing() throws { + let samples: [(DevProcess, String)] = [ + ( + DevProcess( + pid: 35, + parentPID: 1, + executable: "/Applications/Ol", + command: "ollama serve" + ), + "Ollama server" + ), + ( + DevProcess( + pid: 36, + parentPID: 1, + executable: "/Applications/Ol", + command: "/Applications/Ollama.app/Contents/Resources/ollama serve" + ), + "Ollama server" + ), + ( + DevProcess( + pid: 37, + parentPID: 1, + executable: "lmstudio", + command: "lmstudio --server" + ), + "LM Studio" + ) + ] + + for (process, expectedDisplayName) in samples { + let classification = try XCTUnwrap(ProcessClassifier.classify(process), process.command) + XCTAssertEqual(classification.kind, .ai) + XCTAssertEqual(classification.displayName, expectedDisplayName) + } + } + + func testTagsMachineLearningAndLLMDeveloperProcesses() throws { + let samples: [(DevProcess, [DevProcessTag])] = [ + ( + DevProcess(pid: 50, parentPID: 1, executable: "python", command: "python -m ipykernel_launcher -f kernel.json"), + [.notebook] + ), + ( + DevProcess(pid: 51, parentPID: 1, executable: "torchrun", command: "torchrun train_llama.py --model llama"), + [.training, .inference, .llm] + ), + ( + DevProcess(pid: 52, parentPID: 1, executable: "uvicorn", command: "uvicorn fastapi_app:app --reload"), + [.api] + ), + ( + DevProcess(pid: 53, parentPID: 1, executable: "python", command: "python -m qdrant_client.local"), + [.vectorDB] + ), + ( + DevProcess(pid: 54, parentPID: 1, executable: "streamlit", command: "streamlit run dashboard.py"), + [.dataApp] + ) + ] + + for (process, expectedTags) in samples { + let classification = try XCTUnwrap(ProcessClassifier.classify(process), process.command) + for expectedTag in expectedTags { + XCTAssertTrue(classification.tags.contains(expectedTag), "\(process.command) missing \(expectedTag.title)") + } + } + } + + func testIncludesShellsWhoseCurrentDirectoryIsADevProject() throws { + let samples: [(DevProcess, String)] = [ + ( + DevProcess( + pid: 40, + parentPID: 1, + executable: "/bin/zsh", + command: "-zsh", + currentDirectory: "\(NSHomeDirectory())/dev/devscope" + ), + "devscope" + ), + ( + DevProcess( + pid: 41, + parentPID: 1, + executable: "/bin/zsh", + command: "-zsh", + currentDirectory: "\(NSHomeDirectory())/.codex" + ), + "Codex" + ) + ] + + for (process, projectHint) in samples { + let classification = try XCTUnwrap(ProcessClassifier.classify(process)) + + XCTAssertEqual(classification.kind, .shell) + XCTAssertEqual(classification.displayName, "zsh") + XCTAssertEqual(classification.projectHint, projectHint) + } + } + + func testDoesNotTreatCodexSiblingFoldersAsCodexWorkspaces() throws { + let process = DevProcess( + pid: 42, + parentPID: 1, + executable: "/bin/zsh", + command: "-zsh", + currentDirectory: "\(NSHomeDirectory())/.codex-backup" + ) + + let classification = try XCTUnwrap(ProcessClassifier.classify(process)) + + XCTAssertEqual(classification.kind, .other) + XCTAssertEqual(classification.displayName, "zsh") + XCTAssertNil(classification.projectHint) + } + + func testClassifiesShellsOutsideDevWorkspacesAsGenericActivity() throws { + let process = DevProcess( + pid: 41, + parentPID: 1, + executable: "/bin/zsh", + command: "-zsh", + currentDirectory: "\(NSHomeDirectory())/Downloads" + ) + + let classification = try XCTUnwrap(ProcessClassifier.classify(process)) + + XCTAssertEqual(classification.kind, .other) + XCTAssertEqual(classification.displayName, "zsh") + XCTAssertNil(classification.projectHint) + } +} diff --git a/Tests/DevScopeCoreTests/ProcessIntelligenceTests.swift b/Tests/DevScopeCoreTests/ProcessIntelligenceTests.swift new file mode 100644 index 0000000..a6ec4e1 --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessIntelligenceTests.swift @@ -0,0 +1,639 @@ +import XCTest +@testable import DevScopeCore + +final class ProcessIntelligenceTests: XCTestCase { + func testWorkflowSubtitlesUseExplicitOSProcessLanguage() throws { + let item = classified( + pid: 100, + kind: .javascript, + displayName: "vite", + project: "devscope", + command: "pnpm vite" + ) + + let workflow = try XCTUnwrap(ProcessIntelligence.workflows(for: [item]).first) + + XCTAssertTrue(workflow.subtitle.contains("1 OS process")) + XCTAssertFalse(workflow.subtitle.contains("proc ·")) + } + + func testBuildsAIMLLabAndProjectWorkflows() { + let items = [ + classified(pid: 101, kind: .python, displayName: "torchrun train", project: "research", command: "torchrun train_llama.py", cpu: 92, memory: 900_000_000, tags: [.training, .llm]), + classified(pid: 102, kind: .python, displayName: "ipykernel", project: "research", command: "python -m ipykernel_launcher", cpu: 5, memory: 300_000_000, tags: [.notebook]), + classified(pid: 103, kind: .ai, displayName: "Ollama server", project: "research", command: "ollama serve", cpu: 12, memory: 5_000_000_000, tags: [.llmServer]), + classified(pid: 201, kind: .javascript, displayName: "vite", project: "web", command: "pnpm vite", cpu: 3, memory: 200_000_000) + ] + + let workflows = ProcessIntelligence.workflows(for: items) + + XCTAssertTrue(workflows.contains { $0.id == "workflow:ai-ml-lab" }) + XCTAssertTrue(workflows.contains { $0.id == "workflow:local-llm-stack" }) + XCTAssertTrue(workflows.contains { $0.title == "Research Training" }) + XCTAssertEqual(workflows.first?.risk, .heavy) + } + + func testCanonicalizesNoisyCacheAndOwnerWorkspaceNames() { + let home = NSHomeDirectory() + let items = [ + classified( + pid: 401, + kind: .javascript, + displayName: "MCP server", + project: "0.1.23", + command: "node \(home)/.codex/plugins/cache/openai-curated-remote/computer-use/0.1.23/mcp/server.bundle.mjs", + currentDirectory: "\(home)/.codex/plugins/cache/openai-curated-remote/computer-use/0.1.23/mcp" + ), + classified( + pid: 402, + kind: .javascript, + displayName: "MCP server", + project: "3fdeeb49", + command: "node \(home)/.codex/plugins/cache/openai-curated/build-web-apps/3fdeeb49/skills/server.mjs", + currentDirectory: "\(home)/.codex/plugins/cache/openai-curated/build-web-apps/3fdeeb49/skills" + ), + classified( + pid: 403, + kind: .swift, + displayName: "swift build", + project: "example", + command: "swift build", + currentDirectory: "\(home)/dev/example/devscope" + ) + ] + + let workflows = ProcessIntelligence.workflows(for: items) + let titles = workflows.map(\.title) + + XCTAssertTrue(titles.contains("Computer Use Web")) + XCTAssertTrue(titles.contains("Build Web Apps Web")) + XCTAssertTrue(titles.contains("DevScope Build")) + XCTAssertFalse(titles.contains { $0.contains("0.1.23") }) + XCTAssertFalse(titles.contains { $0.contains("3fdeeb49") }) + XCTAssertFalse(titles.contains("example Build")) + } + + func testWorkflowOrderStaysStableWhenCpuChangesWithinSameRiskBand() { + let items = [ + classified(pid: 201, kind: .javascript, displayName: "vite", project: "alpha", command: "pnpm vite", cpu: 1, memory: 200_000_000), + classified(pid: 202, kind: .javascript, displayName: "vite", project: "beta", command: "pnpm vite", cpu: 19, memory: 200_000_000) + ] + + let workflows = ProcessIntelligence.workflows(for: items) + + XCTAssertEqual(workflows.map(\.title), ["Alpha Web", "Beta Web"]) + } + + func testPunctuationCollidingProjectsHaveDistinctDeterministicWorkflows() { + let home = NSHomeDirectory() + let dotted = classified( + pid: 220, + kind: .javascript, + displayName: "vite", + project: "foo.bar", + command: "pnpm vite", + currentDirectory: "\(home)/dev/foo.bar" + ) + let dashed = classified( + pid: 221, + kind: .javascript, + displayName: "vite", + project: "foo-bar", + command: "pnpm vite", + currentDirectory: "\(home)/dev/foo-bar" + ) + let dottedWorker = classified( + pid: 222, + kind: .javascript, + displayName: "vite worker", + project: "foo.bar", + command: "pnpm vite --worker", + currentDirectory: "\(home)/dev/foo.bar" + ) + + let items = [dotted, dashed, dottedWorker] + let forward = ProcessIntelligence.workflows(for: items) + let reversed = ProcessIntelligence.workflows(for: Array(items.reversed())) + + XCTAssertEqual(forward, reversed) + XCTAssertEqual(Set(forward.map(\.title)), Set(["Foo.bar Web", "Foo Bar Web"])) + XCTAssertEqual(Set(forward.map(\.id)).count, 2) + XCTAssertEqual(Set(forward.flatMap(\.processIDs)), Set([220, 221, 222])) + XCTAssertEqual(forward.first { $0.title == "Foo.bar Web" }?.processIDs, [220, 222]) + } + + func testProjectWorkflowAggregationIsDeterministicAcrossInputOrder() throws { + let items = [ + classified( + pid: 903, + kind: .javascript, + displayName: "experiment worker", + project: "determinism", + command: "node experiment.js", + cpu: 10_000_000_000_000_000, + tags: [.experiment] + ), + classified( + pid: 901, + kind: .javascript, + displayName: "API worker", + project: "determinism", + command: "node api.js", + cpu: 1, + tags: [.api] + ), + classified( + pid: 902, + kind: .javascript, + displayName: "MCP worker", + project: "determinism", + command: "node mcp.js", + cpu: 1, + tags: [.mcp] + ), + ] + + let forward = try XCTUnwrap( + ProcessIntelligence.workflows(for: items).first { $0.title == "Determinism API" } + ) + let reversed = try XCTUnwrap( + ProcessIntelligence.workflows(for: Array(items.reversed())).first { $0.title == "Determinism API" } + ) + + XCTAssertEqual(forward, reversed) + XCTAssertEqual(forward.tags.map(\.id), ["api", "experiment", "mcp"]) + } + + func testAIMLAggregationIsDeterministicAcrossInputOrder() throws { + let items = [ + classified( + pid: 913, + kind: .python, + displayName: "experiment worker", + project: "gamma", + command: "python experiment.py", + cpu: 10_000_000_000_000_000, + tags: [.experiment] + ), + classified( + pid: 911, + kind: .python, + displayName: "training worker", + project: "alpha", + command: "python train.py", + cpu: 1, + tags: [.training] + ), + classified( + pid: 912, + kind: .python, + displayName: "notebook worker", + project: "beta", + command: "python notebook.py", + cpu: 1, + tags: [.notebook] + ), + ] + + let forward = try XCTUnwrap( + ProcessIntelligence.workflows(for: items).first { $0.id == "workflow:ai-ml-lab" } + ) + let reversed = try XCTUnwrap( + ProcessIntelligence.workflows(for: Array(items.reversed())).first { $0.id == "workflow:ai-ml-lab" } + ) + + XCTAssertEqual(forward, reversed) + XCTAssertEqual(forward.tags.map(\.id), ["experiment", "notebook", "training"]) + } + + func testAvoidsDuplicateKindWordsAndNoisySingleProcessFocusGroups() { + let items = [ + classified( + pid: 301, + kind: .python, + displayName: "streamlit", + project: "data", + command: "streamlit run app.py", + cpu: 4, + memory: 200_000_000, + tags: [.dataApp], + currentDirectory: "\(NSHomeDirectory())/dev/example/data" + ), + classified( + pid: 302, + kind: .python, + displayName: "(python3.12)", + project: "(python3.12)", + command: "(python3.12)", + cpu: 0, + memory: 12_000_000, + currentDirectory: nil + ), + classified( + pid: 303, + kind: .swift, + displayName: "CalendarThumbnailExtension Launcher", + project: "CalendarThumbnailExtension Launcher", + command: "/System/Library/ExtensionKit/Extensions/CalendarThumbnailExtension.appex", + cpu: 0, + memory: 10_000_000, + currentDirectory: "/System/Library/ExtensionKit/Extensions" + ) + ] + + let workflows = ProcessIntelligence.workflows(for: items) + + XCTAssertTrue(workflows.contains { $0.title == "Data App" }) + XCTAssertFalse(workflows.contains { $0.title.contains("Data Data") }) + XCTAssertFalse(workflows.contains { $0.title.contains("python3.12") }) + XCTAssertFalse(workflows.contains { $0.title.contains("CalendarThumbnail") }) + } + + func testRejectsBrowserRendererCommandsAsFocusWorkflows() { + let command = "/Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Helper.app/Contents/MacOS/Google Chrome Helper --type=renderer --origin-trial-disabled-features=CanvasTextNG --lang=en-US --renderer-client-id=3645" + let items = [ + classified(pid: 501, kind: .browser, displayName: "Google Chrome Helper", project: "Google Chrome Helper", command: command, cpu: 45, memory: 2_000_000_000, currentDirectory: "/"), + classified(pid: 502, kind: .browser, displayName: "Google Chrome Helper", project: "Google Chrome Helper", command: command + " --renderer-client-id=3646", cpu: 30, memory: 1_500_000_000, currentDirectory: "/") + ] + + let workflows = ProcessIntelligence.workflows(for: items) + + XCTAssertTrue(workflows.isEmpty) + XCTAssertFalse(workflows.contains { $0.title.contains("renderer") || $0.title.count > 72 }) + } + + func testCommandPathCandidateBuildsADevelopmentWorkflow() { + let home = NSHomeDirectory() + let command = "node \(home)/dev/example/devscope/node_modules/.bin/vite --host 127.0.0.1" + let item = ClassifiedDevProcess( + process: DevProcess( + pid: 503, + parentPID: 1, + executable: "node", + command: command, + currentDirectory: nil, + resourceUsage: DevProcessResourceUsage(cpuPercent: 0, residentMemoryBytes: 0, elapsedTime: "00:01") + ), + classification: DevProcessClassification( + kind: .javascript, + displayName: "vite", + projectHint: "devscope", + tags: [] + ) + ) + + XCTAssertTrue(ProcessIntelligence.workflows(for: [item]).contains { $0.title == "DevScope Web" }) + } + + func testConfiguredOwnerDirectoryKeepsDeepSiblingRepositoriesDistinct() { + let home = NSHomeDirectory() + let items = [ + classified( + pid: 504, + kind: .javascript, + displayName: "vite", + project: "fallback", + command: "pnpm vite", + currentDirectory: "\(home)/dev/acme-labs/alpha-app/feature-one/task" + ), + classified( + pid: 505, + kind: .javascript, + displayName: "vite", + project: "fallback", + command: "pnpm vite", + currentDirectory: "\(home)/dev/acme-labs/beta-app/feature-two/task" + ), + ] + + XCTAssertEqual( + ProcessIntelligence.workflows( + for: items, + workspaceOwnerComponents: ["acme-labs"] + ).map(\.title), + ["Alpha App Web", "Beta App Web"] + ) + } + + func testDirectProjectNestedDirectoryKeepsRepositoryIdentity() { + let item = classified( + pid: 506, + kind: .javascript, + displayName: "vite", + project: "devscope", + command: "pnpm vite", + currentDirectory: "\(NSHomeDirectory())/dev/devscope/feature-one" + ) + + XCTAssertEqual( + ProcessIntelligence.workflows(for: [item]).map(\.title), + ["DevScope Web"] + ) + } + + func testLexicalAbsolutePathNormalizationDoesNotDependOnFilesystemState() { + let home = NSHomeDirectory() + + XCTAssertEqual( + ProcessIntelligence.lexicallyNormalizedAbsolutePath( + "\(home)//dev/./example/../devscope/" + ), + "\(home)/dev/devscope" + ) + XCTAssertEqual( + ProcessIntelligence.lexicallyNormalizedAbsolutePath("/Users/example/../../tmp"), + "/tmp" + ) + XCTAssertNil(ProcessIntelligence.lexicallyNormalizedAbsolutePath("../../tmp")) + XCTAssertNil(ProcessIntelligence.lexicallyNormalizedAbsolutePath("/../../tmp")) + } + + func testLexicalProjectPathsPreserveDevRootsAndRejectHomeEscape() throws { + let home = NSHomeDirectory() + let homeName = try XCTUnwrap(home.split(separator: "/").last.map(String.init)) + let items = [ + classified( + pid: 550, + kind: .javascript, + displayName: "vite", + project: "fallback", + command: "pnpm vite", + currentDirectory: "\(home)//dev/./example/scratch/../devscope//" + ), + classified( + pid: 551, + kind: .javascript, + displayName: "vite", + project: "escaped", + command: "pnpm vite", + currentDirectory: "\(home)/dev/../../\(homeName)/dev/example/escaped" + ), + classified( + pid: 552, + kind: .javascript, + displayName: "vite", + project: "nested", + command: "pnpm vite", + currentDirectory: "\(home)/Downloads/../Documents/dev/nested" + ), + ] + + XCTAssertEqual(ProcessIntelligence.workflows(for: items).map(\.title), ["DevScope Web"]) + } + + func testRejectsHomeLibraryPathsAsDevelopmentWorkflows() { + let directory = "\(NSHomeDirectory())/Library/Application Support/Acme Helper" + let items = [ + classified( + pid: 601, + kind: .javascript, + displayName: "helper", + project: "Acme Helper", + command: "/usr/bin/node helper.js", + currentDirectory: directory + ), + classified( + pid: 602, + kind: .javascript, + displayName: "helper", + project: "Acme Helper", + command: "/usr/bin/node helper.js --worker", + currentDirectory: directory + ), + ] + + XCTAssertTrue(ProcessIntelligence.workflows(for: items).isEmpty) + } + + func testAcceptsKnownDevelopmentRootsAndSkipsNestedRootBuckets() { + let home = NSHomeDirectory() + let fixtures = [ + ("dev/apps/meeting_vault", "Meeting Vault Web"), + ("apps/clip_vault", "Clip Vault Web"), + ("projects/alpha", "Alpha Web"), + ("workspaces/beta", "Beta Web"), + ("source/gamma", "Gamma Web"), + ("src/delta", "Delta Web"), + ] + let items = fixtures.enumerated().map { index, fixture in + classified( + pid: Int32(620 + index), + kind: .javascript, + displayName: "vite", + project: "fallback-\(index)", + command: "pnpm vite", + currentDirectory: "\(home)/\(fixture.0)" + ) + } + + let titles = Set(ProcessIntelligence.workflows(for: items).map(\.title)) + + XCTAssertEqual(titles, Set(fixtures.map(\.1))) + } + + func testRejectsDevelopmentMarkerNestedUnderDownloads() { + let item = classified( + pid: 640, + kind: .javascript, + displayName: "vite", + project: "helper", + command: "pnpm vite", + currentDirectory: "\(NSHomeDirectory())/Downloads/dev/helper" + ) + + XCTAssertTrue(ProcessIntelligence.workflows(for: [item]).isEmpty) + } + + func testRejectsProjectsMarkerNestedUnderDesktop() { + let item = classified( + pid: 641, + kind: .javascript, + displayName: "vite", + project: "tool", + command: "pnpm vite", + currentDirectory: "\(NSHomeDirectory())/Desktop/projects/tool" + ) + + XCTAssertTrue(ProcessIntelligence.workflows(for: [item]).isEmpty) + } + + func testRejectsCodexMarkerNestedUnderDocuments() { + let item = classified( + pid: 642, + kind: .mcp, + displayName: "MCP server", + project: "codex", + command: "node server.mjs", + currentDirectory: "\(NSHomeDirectory())/Documents/.codex/plugins/tool" + ) + + XCTAssertTrue(ProcessIntelligence.workflows(for: [item]).isEmpty) + } + + func testQuotedCommandPathWithSpacesBuildsADevelopmentWorkflow() { + let home = NSHomeDirectory() + let command = "node \"\(home)/dev/My Great App/node_modules/.bin/vite\" --host 127.0.0.1" + let item = ClassifiedDevProcess( + process: DevProcess( + pid: 603, + parentPID: 1, + executable: "node", + command: command, + resourceUsage: DevProcessResourceUsage(cpuPercent: 0, residentMemoryBytes: 0, elapsedTime: "00:01") + ), + classification: DevProcessClassification( + kind: .javascript, + displayName: "vite", + projectHint: "fallback", + tags: [] + ) + ) + + XCTAssertTrue( + ProcessIntelligence.workflows(for: [item]).contains { $0.title == "My Great App Web" } + ) + } + + func testRejectsMalformedQuotedAssignmentsAndRawCommandIdentifiers() { + let home = NSHomeDirectory() + let items = [ + classifiedWithoutCurrentDirectory( + pid: 604, + command: "node \"\(home)/dev/alpha/server.js" + ), + classifiedWithoutCurrentDirectory( + pid: 605, + command: "node PROJECT=\(home)/dev/beta/server.js" + ), + classifiedWithoutCurrentDirectory( + pid: 606, + command: "node devscope-worker" + ), + classifiedWithoutCurrentDirectory( + pid: 607, + command: "node \"\(home)/dev/gamma/server.js\"suffix" + ), + ] + + XCTAssertTrue(ProcessIntelligence.workflows(for: items).isEmpty) + } + + func testCapsWorkflowsWhilePreservingAIMLAggregates() { + var items = (0..<15).map { index in + classified( + pid: Int32(700 + index), + kind: .javascript, + displayName: "vite", + project: "project-\(index)", + command: "pnpm vite" + ) + } + items.append( + classified( + pid: 799, + kind: .ai, + displayName: "Ollama server", + project: "research", + command: "ollama serve", + tags: [.llmServer] + ) + ) + + let workflows = ProcessIntelligence.workflows(for: items) + + XCTAssertLessThanOrEqual(workflows.count, 12) + XCTAssertTrue(workflows.contains { $0.id == "workflow:ai-ml-lab" }) + XCTAssertTrue(workflows.contains { $0.id == "workflow:local-llm-stack" }) + XCTAssertEqual(workflows.map(\.id), ProcessIntelligence.workflows(for: items).map(\.id)) + } + + func testProcessInsightExplainsRoleAndSafeAction() { + let item = classified(pid: 101, kind: .python, displayName: "torchrun train", project: "research", command: "torchrun train_llama.py", cpu: 92, memory: 900_000_000, tags: [.training, .llm]) + let workflow = ProcessIntelligence.workflows(for: [item]).first + let family = ProcessFamilySummary(parentPID: 1, childCount: 1, descendantCount: 2) + + let insight = ProcessIntelligence.insight( + for: item, + workflow: workflow, + familySummary: family, + metricHistory: [] + ) + + XCTAssertEqual(insight.title, "Training workload") + XCTAssertEqual(insight.role, "Training or fine-tuning workload") + XCTAssertTrue(insight.resourceBehavior.contains("High CPU")) + XCTAssertTrue(insight.safeAction.contains("TERM Tree")) + } + + func testProtectedProcessInsightNeverRecommendsTermTree() { + let item = classified( + pid: 1, + kind: .systemService, + displayName: "launchd", + project: "system", + command: "/sbin/launchd" + ) + let family = ProcessFamilySummary(parentPID: 0, childCount: 700, descendantCount: 890) + + let insight = ProcessIntelligence.insight( + for: item, + workflow: nil, + familySummary: family, + metricHistory: [], + actionDecision: .protected(reason: "macOS launch infrastructure is protected") + ) + + XCTAssertEqual(insight.safeAction, "Protected: macOS launch infrastructure is protected.") + XCTAssertFalse(insight.safeAction.contains("TERM")) + } + + private func classified( + pid: Int32, + kind: DevRuntimeKind, + displayName: String, + project: String, + command: String, + cpu: Double = 0, + memory: Int64 = 0, + tags: [DevProcessTag] = [], + currentDirectory: String? = nil + ) -> ClassifiedDevProcess { + ClassifiedDevProcess( + process: DevProcess( + pid: pid, + parentPID: 1, + executable: command.split(separator: " ").first.map(String.init) ?? command, + command: command, + currentDirectory: currentDirectory ?? "\(NSHomeDirectory())/dev/example/\(project)", + resourceUsage: DevProcessResourceUsage(cpuPercent: cpu, residentMemoryBytes: memory, elapsedTime: "00:01") + ), + classification: DevProcessClassification( + kind: kind, + displayName: displayName, + projectHint: project, + tags: tags + ) + ) + } + + private func classifiedWithoutCurrentDirectory( + pid: Int32, + command: String + ) -> ClassifiedDevProcess { + ClassifiedDevProcess( + process: DevProcess( + pid: pid, + parentPID: 1, + executable: "node", + command: command, + resourceUsage: DevProcessResourceUsage(cpuPercent: 0, residentMemoryBytes: 0, elapsedTime: "00:01") + ), + classification: DevProcessClassification( + kind: .javascript, + displayName: "node", + projectHint: "fallback", + tags: [] + ) + ) + } +} diff --git a/Tests/DevScopeCoreTests/ProcessKillerTests.swift b/Tests/DevScopeCoreTests/ProcessKillerTests.swift new file mode 100644 index 0000000..9ec5dbd --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessKillerTests.swift @@ -0,0 +1,538 @@ +import Darwin +import Foundation +import XCTest +@testable import DevScopeCore + +final class ProcessKillerTests: XCTestCase { + func testTerminateSignalsWhenLiveIdentityMatchesExpectedBirth() throws { + let process = process(pid: 42, birthToken: token(100)) + let recorder = SignalRecorder() + let killer = ProcessKiller( + identityResolver: { _ in .identity(ProcessIdentity(process: process)) }, + signalSender: { pid, signal in recorder.record(pid: pid, signal: signal) } + ) + + let targets = try killer.terminate(classified(process), currentProcessID: 9_000) + + XCTAssertEqual(targets, [ProcessIdentity(process: process)]) + XCTAssertEqual(recorder.signals, [RecordedSignal(pid: 42, signal: SIGTERM)]) + } + + func testForceTerminateUsesKillSignalAfterIdentityValidation() throws { + let process = process(pid: 42, birthToken: token(100)) + let recorder = SignalRecorder() + let killer = matchingKiller(for: [process], recorder: recorder) + + let targets = try killer.forceTerminate( + classified(process), + currentProcessID: 9_000 + ) + + XCTAssertEqual(targets, [ProcessIdentity(process: process)]) + XCTAssertEqual(recorder.signals, [RecordedSignal(pid: 42, signal: SIGKILL)]) + } + + func testTerminateRejectsMissingExpectedBirthWithoutSendingSignal() { + let process = process(pid: 42, birthToken: nil) + let recorder = SignalRecorder() + let killer = ProcessKiller( + identityResolver: { _ in .identity(ProcessIdentity(process: process)) }, + signalSender: { pid, signal in recorder.record(pid: pid, signal: signal) } + ) + + XCTAssertThrowsError( + try killer.terminate(classified(process), currentProcessID: 9_000) + ) { error in + XCTAssertEqual(error as? ProcessKillError, .expectedIdentityUnavailable(pid: 42)) + } + XCTAssertTrue(recorder.signals.isEmpty) + } + + func testTerminateRejectsLiveIdentityWithMissingBirthWithoutSendingSignal() { + let process = process(pid: 42, birthToken: token(100)) + let liveWithoutBirth = self.process(pid: 42, birthToken: nil) + let recorder = SignalRecorder() + let killer = ProcessKiller( + identityResolver: { _ in .identity(ProcessIdentity(process: liveWithoutBirth)) }, + signalSender: { pid, signal in recorder.record(pid: pid, signal: signal) } + ) + + XCTAssertThrowsError( + try killer.terminate(classified(process), currentProcessID: 9_000) + ) { error in + XCTAssertEqual(error as? ProcessKillError, .liveIdentityUnavailable(pid: 42)) + } + XCTAssertTrue(recorder.signals.isEmpty) + } + + func testTerminateRejectsReusedPIDWithoutSendingSignal() { + let process = process(pid: 42, birthToken: token(100)) + let replacement = self.process(pid: 42, birthToken: token(101)) + let recorder = SignalRecorder() + let killer = ProcessKiller( + identityResolver: { _ in .identity(ProcessIdentity(process: replacement)) }, + signalSender: { pid, signal in recorder.record(pid: pid, signal: signal) } + ) + + XCTAssertThrowsError( + try killer.terminate(classified(process), currentProcessID: 9_000) + ) { error in + XCTAssertEqual(error as? ProcessKillError, .identityMismatch(pid: 42)) + } + XCTAssertTrue(recorder.signals.isEmpty) + } + + func testTerminateRejectsTargetThatEndedAfterConfirmationWithoutSendingSignal() { + let process = process(pid: 42, birthToken: token(100)) + let recorder = SignalRecorder() + let killer = ProcessKiller( + identityResolver: { _ in .notRunning }, + signalSender: { pid, signal in recorder.record(pid: pid, signal: signal) } + ) + + XCTAssertThrowsError( + try killer.terminate(classified(process), currentProcessID: 9_000) + ) { error in + XCTAssertEqual(error as? ProcessKillError, .targetNotRunning(pid: 42)) + } + XCTAssertTrue(recorder.signals.isEmpty) + } + + func testTerminateRechecksIdentityAfterGlobalPreflightBeforeSendingSignal() { + let process = process(pid: 42, birthToken: token(100)) + let replacement = self.process(pid: 42, birthToken: token(101)) + let resolver = IdentityResolutionScript(resolutions: [ + 42: [ + .identity(ProcessIdentity(process: process)), + .identity(ProcessIdentity(process: replacement)), + ] + ]) + let recorder = SignalRecorder() + let killer = ProcessKiller( + identityResolver: resolver.resolve, + signalSender: { pid, signal in recorder.record(pid: pid, signal: signal) } + ) + + XCTAssertThrowsError( + try killer.terminate(classified(process), currentProcessID: 9_000) + ) { error in + XCTAssertEqual(error as? ProcessKillError, .identityMismatch(pid: 42)) + } + XCTAssertTrue(recorder.signals.isEmpty) + } + + func testTerminateTreeSignalsDescendantsBeforeTheirAncestors() throws { + let root = process(pid: 10, parentPID: 1, birthToken: token(10)) + let child = process(pid: 11, parentPID: 10, birthToken: token(11)) + let grandchild = process(pid: 12, parentPID: 11, birthToken: token(12)) + let processes = [root, child, grandchild] + let liveByPID = Dictionary(uniqueKeysWithValues: processes.map { ($0.pid, $0) }) + let recorder = SignalRecorder() + let killer = ProcessKiller( + identityResolver: { pid in + liveByPID[pid].map { .identity(ProcessIdentity(process: $0)) } ?? .notRunning + }, + signalSender: { pid, signal in recorder.record(pid: pid, signal: signal) } + ) + + let targets = try killer.terminateTree( + root: classified(root), + processes: processes, + classifiedProcesses: processes.map { classified($0) }, + currentProcessID: 9_000 + ) + + XCTAssertEqual(targets.map(\.pid), [12, 11, 10]) + XCTAssertEqual( + recorder.signals, + [12, 11, 10].map { RecordedSignal(pid: $0, signal: SIGTERM) } + ) + } + + func testForceTerminateTreeSignalsDescendantsBeforeAncestorsWithKill() throws { + let root = process(pid: 10, parentPID: 1, birthToken: token(10)) + let child = process(pid: 11, parentPID: 10, birthToken: token(11)) + let grandchild = process(pid: 12, parentPID: 11, birthToken: token(12)) + let processes = [root, child, grandchild] + let recorder = SignalRecorder() + let killer = matchingKiller(for: processes, recorder: recorder) + + let targets = try killer.forceTerminateTree( + root: classified(root), + processes: processes, + classifiedProcesses: processes.map { classified($0) }, + currentProcessID: 9_000 + ) + + XCTAssertEqual(targets.map(\.pid), [12, 11, 10]) + XCTAssertEqual( + recorder.signals, + [12, 11, 10].map { RecordedSignal(pid: $0, signal: SIGKILL) } + ) + } + + func testTerminateTreeRejectsDevScopeDescendantWithoutSendingAnySignal() { + let root = process(pid: 10, parentPID: 1, birthToken: token(10)) + let devScope = process( + pid: 42, + parentPID: 10, + executable: "/Applications/DevScope.app/Contents/MacOS/DevScope", + birthToken: token(42) + ) + let processes = [root, devScope] + let recorder = SignalRecorder() + let killer = matchingKiller(for: processes, recorder: recorder) + + XCTAssertThrowsError( + try killer.terminateTree( + root: classified(root), + processes: processes, + classifiedProcesses: [classified(root), classified(devScope, kind: .macApp)], + currentProcessID: 42 + ) + ) { error in + XCTAssertEqual( + error as? ProcessKillError, + .protectedTarget(pid: 42, reason: "DevScope cannot terminate itself") + ) + } + XCTAssertTrue(recorder.signals.isEmpty) + } + + func testTerminateTreeRejectsCriticalSystemDescendantWithoutSendingAnySignal() { + let root = process(pid: 10, parentPID: 1, birthToken: token(10)) + let windowServer = process( + pid: 88, + parentPID: 10, + executable: "/System/Library/CoreServices/WindowServer", + birthToken: token(88) + ) + let processes = [root, windowServer] + let recorder = SignalRecorder() + let killer = matchingKiller(for: processes, recorder: recorder) + + XCTAssertThrowsError( + try killer.terminateTree( + root: classified(root), + processes: processes, + classifiedProcesses: [classified(root), classified(windowServer, kind: .systemService)], + currentProcessID: 9_000 + ) + ) { error in + XCTAssertEqual( + error as? ProcessKillError, + .protectedTarget( + pid: 88, + reason: "Critical macOS system infrastructure is protected" + ) + ) + } + XCTAssertTrue(recorder.signals.isEmpty) + } + + func testTerminateTreeRejectsUnclassifiedDescendantWithoutSendingAnySignal() { + let root = process(pid: 10, parentPID: 1, birthToken: token(10)) + let child = process(pid: 11, parentPID: 10, birthToken: token(11)) + let processes = [root, child] + let recorder = SignalRecorder() + let killer = matchingKiller(for: processes, recorder: recorder) + + XCTAssertThrowsError( + try killer.terminateTree( + root: classified(root), + processes: processes, + classifiedProcesses: [classified(root)], + currentProcessID: 9_000 + ) + ) { error in + XCTAssertEqual( + error as? ProcessKillError, + .targetClassificationUnavailable(pid: 11) + ) + } + XCTAssertTrue(recorder.signals.isEmpty) + } + + func testTerminateTreeRejectsDescendantWithUnknownBirthWithoutSendingAnySignal() { + let root = process(pid: 10, parentPID: 1, birthToken: token(10)) + let child = process(pid: 11, parentPID: 10, birthToken: nil) + let processes = [root, child] + let recorder = SignalRecorder() + let killer = matchingKiller(for: processes, recorder: recorder) + + XCTAssertThrowsError( + try killer.terminateTree( + root: classified(root), + processes: processes, + classifiedProcesses: processes.map { classified($0) }, + currentProcessID: 9_000 + ) + ) { error in + XCTAssertEqual( + error as? ProcessKillError, + .expectedIdentityUnavailable(pid: 11) + ) + } + XCTAssertTrue(recorder.signals.isEmpty) + } + + func testTerminateTreePreflightRejectsOneReusedDescendantWithoutSendingAnySignal() { + let root = process(pid: 10, parentPID: 1, birthToken: token(10)) + let firstChild = process(pid: 11, parentPID: 10, birthToken: token(11)) + let reusedChild = process(pid: 12, parentPID: 10, birthToken: token(12)) + let processes = [root, firstChild, reusedChild] + let replacement = process(pid: 11, parentPID: 10, birthToken: token(99)) + let liveByPID: [Int32: DevProcess] = [10: root, 11: replacement, 12: reusedChild] + let recorder = SignalRecorder() + let killer = ProcessKiller( + identityResolver: { pid in + liveByPID[pid].map { .identity(ProcessIdentity(process: $0)) } ?? .notRunning + }, + signalSender: { pid, signal in recorder.record(pid: pid, signal: signal) } + ) + + XCTAssertThrowsError( + try killer.terminateTree( + root: classified(root), + processes: processes, + classifiedProcesses: processes.map { classified($0) }, + currentProcessID: 9_000 + ) + ) { error in + XCTAssertEqual(error as? ProcessKillError, .identityMismatch(pid: 11)) + } + XCTAssertTrue(recorder.signals.isEmpty) + } + + func testTerminateTreeReportsAlreadySignaledDescendantWhenLaterIdentityChanges() { + let root = process(pid: 10, parentPID: 1, birthToken: token(10)) + let child = process(pid: 11, parentPID: 10, birthToken: token(11)) + let grandchild = process(pid: 12, parentPID: 11, birthToken: token(12)) + let replacement = process(pid: 11, parentPID: 10, birthToken: token(99)) + let processes = [root, child, grandchild] + let resolver = IdentityResolutionScript(resolutions: [ + 10: [.identity(ProcessIdentity(process: root))], + 11: [ + .identity(ProcessIdentity(process: child)), + .identity(ProcessIdentity(process: replacement)), + ], + 12: [ + .identity(ProcessIdentity(process: grandchild)), + .identity(ProcessIdentity(process: grandchild)), + ], + ]) + let recorder = SignalRecorder() + let killer = ProcessKiller( + identityResolver: resolver.resolve, + signalSender: { pid, signal in recorder.record(pid: pid, signal: signal) } + ) + + XCTAssertThrowsError( + try killer.terminateTree( + root: classified(root), + processes: processes, + classifiedProcesses: processes.map { classified($0) }, + currentProcessID: 9_000 + ) + ) { error in + guard let failure = error as? ProcessSignalExecutionFailure else { + return XCTFail("Expected structured partial execution failure, got \(error)") + } + XCTAssertEqual( + failure.signaledIdentities, + [ProcessIdentity(process: grandchild)] + ) + XCTAssertEqual(failure.failedTarget, ProcessIdentity(process: child)) + XCTAssertEqual( + failure.underlyingError as? ProcessKillError, + .identityMismatch(pid: 11) + ) + XCTAssertEqual( + failure.localizedDescription, + "Signaled 1 process before PID 11 failed: PID 11 no longer belongs to the selected process" + ) + } + XCTAssertEqual(recorder.signals, [RecordedSignal(pid: 12, signal: SIGTERM)]) + } + + func testTerminateTreeReportsAlreadySignaledDescendantWhenLaterSenderFails() { + let root = process(pid: 10, parentPID: 1, birthToken: token(10)) + let child = process(pid: 11, parentPID: 10, birthToken: token(11)) + let grandchild = process(pid: 12, parentPID: 11, birthToken: token(12)) + let processes = [root, child, grandchild] + let liveByPID = Dictionary(uniqueKeysWithValues: processes.map { ($0.pid, $0) }) + let recorder = SignalRecorder() + let senderFailure = ProcessKillError.signalFailed(pid: 11, errnoCode: EPERM) + let killer = ProcessKiller( + identityResolver: { pid in + liveByPID[pid].map { .identity(ProcessIdentity(process: $0)) } ?? .notRunning + }, + signalSender: { pid, signal in + guard pid != 11 else { throw senderFailure } + recorder.record(pid: pid, signal: signal) + } + ) + + XCTAssertThrowsError( + try killer.terminateTree( + root: classified(root), + processes: processes, + classifiedProcesses: processes.map { classified($0) }, + currentProcessID: 9_000 + ) + ) { error in + guard let failure = error as? ProcessSignalExecutionFailure else { + return XCTFail("Expected structured partial execution failure, got \(error)") + } + XCTAssertEqual( + failure.signaledIdentities, + [ProcessIdentity(process: grandchild)] + ) + XCTAssertEqual(failure.failedTarget, ProcessIdentity(process: child)) + XCTAssertEqual(failure.underlyingError as? ProcessKillError, senderFailure) + } + XCTAssertEqual(recorder.signals, [RecordedSignal(pid: 12, signal: SIGTERM)]) + } + + func testTerminateTreeFirstSenderFailureStaysOrdinaryAndStopsLaterTargets() { + let root = process(pid: 10, parentPID: 1, birthToken: token(10)) + let child = process(pid: 11, parentPID: 10, birthToken: token(11)) + let grandchild = process(pid: 12, parentPID: 11, birthToken: token(12)) + let processes = [root, child, grandchild] + let liveByPID = Dictionary(uniqueKeysWithValues: processes.map { ($0.pid, $0) }) + let recorder = SignalRecorder() + let senderFailure = ProcessKillError.signalFailed(pid: 12, errnoCode: EPERM) + let killer = ProcessKiller( + identityResolver: { pid in + liveByPID[pid].map { .identity(ProcessIdentity(process: $0)) } ?? .notRunning + }, + signalSender: { pid, signal in + guard pid != 12 else { throw senderFailure } + recorder.record(pid: pid, signal: signal) + } + ) + + XCTAssertThrowsError( + try killer.terminateTree( + root: classified(root), + processes: processes, + classifiedProcesses: processes.map { classified($0) }, + currentProcessID: 9_000 + ) + ) { error in + XCTAssertEqual(error as? ProcessKillError, senderFailure) + XCTAssertFalse(error is ProcessSignalExecutionFailure) + } + XCTAssertTrue(recorder.signals.isEmpty) + } + + func testTerminateTreeRejectsStaleDescendantClassificationWithoutSendingAnySignal() { + let root = process(pid: 10, parentPID: 1, birthToken: token(10)) + let child = process(pid: 11, parentPID: 10, birthToken: token(11)) + let staleClassifiedChild = process(pid: 11, parentPID: 10, birthToken: token(99)) + let processes = [root, child] + let recorder = SignalRecorder() + let killer = matchingKiller(for: processes, recorder: recorder) + + XCTAssertThrowsError( + try killer.terminateTree( + root: classified(root), + processes: processes, + classifiedProcesses: [classified(root), classified(staleClassifiedChild)], + currentProcessID: 9_000 + ) + ) { error in + XCTAssertEqual( + error as? ProcessKillError, + .targetClassificationMismatch(pid: 11) + ) + } + XCTAssertTrue(recorder.signals.isEmpty) + } + + private func process( + pid: Int32, + parentPID: Int32 = 1, + executable: String = "/opt/homebrew/bin/node", + birthToken: ProcessBirthToken? + ) -> DevProcess { + DevProcess( + pid: pid, + parentPID: parentPID, + executable: executable, + command: "node server.js", + birthToken: birthToken + ) + } + + private func classified( + _ process: DevProcess, + kind: DevRuntimeKind = .javascript + ) -> ClassifiedDevProcess { + ClassifiedDevProcess( + process: process, + classification: DevProcessClassification( + kind: kind, + displayName: "Node", + projectHint: nil + ) + ) + } + + private func token(_ microseconds: UInt64) -> ProcessBirthToken { + ProcessBirthToken(seconds: 1_000, microseconds: microseconds) + } + + private func matchingKiller( + for processes: [DevProcess], + recorder: SignalRecorder + ) -> ProcessKiller { + let liveByPID = Dictionary(uniqueKeysWithValues: processes.map { ($0.pid, $0) }) + return ProcessKiller( + identityResolver: { pid in + liveByPID[pid].map { .identity(ProcessIdentity(process: $0)) } ?? .notRunning + }, + signalSender: { pid, signal in recorder.record(pid: pid, signal: signal) } + ) + } +} + +private struct RecordedSignal: Equatable { + let pid: Int32 + let signal: Int32 +} + +private final class SignalRecorder: @unchecked Sendable { + private let lock = NSLock() + private var recordedSignals: [RecordedSignal] = [] + + var signals: [RecordedSignal] { + lock.withLock { recordedSignals } + } + + func record(pid: Int32, signal: Int32) { + lock.withLock { + recordedSignals.append(RecordedSignal(pid: pid, signal: signal)) + } + } +} + +private final class IdentityResolutionScript: @unchecked Sendable { + private let lock = NSLock() + private var resolutions: [Int32: [ProcessLiveIdentityResolution]] + + init(resolutions: [Int32: [ProcessLiveIdentityResolution]]) { + self.resolutions = resolutions + } + + func resolve(pid: Int32) -> ProcessLiveIdentityResolution { + lock.withLock { + guard var scripted = resolutions[pid], !scripted.isEmpty else { + return .notRunning + } + let resolution = scripted.removeFirst() + resolutions[pid] = scripted + return resolution + } + } +} diff --git a/Tests/DevScopeCoreTests/ProcessMetricHistoryStoreTests.swift b/Tests/DevScopeCoreTests/ProcessMetricHistoryStoreTests.swift new file mode 100644 index 0000000..c63a90d --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessMetricHistoryStoreTests.swift @@ -0,0 +1,213 @@ +import XCTest +@testable import DevScopeCore + +final class ProcessMetricHistoryStoreTests: XCTestCase { + func testRecordsMetricSamplesForAProcess() throws { + var store = ProcessMetricHistoryStore(limit: 3) + let timestamp = Date(timeIntervalSince1970: 100) + + store.record( + processes: [process(pid: 700, cpu: 12, memory: 256)], + gpuMetric: DevGPUMetric(utilizationPercent: 34), + timestamp: timestamp + ) + + let sample = try XCTUnwrap(store.history(for: 700).first) + XCTAssertEqual(sample.timestamp, timestamp) + XCTAssertEqual(sample.cpuPercent, 12) + XCTAssertEqual(sample.residentMemoryBytes, 256) + XCTAssertEqual(sample.gpuPercent, 34) + } + + func testCapsEachProcessHistoryAtTheConfiguredLimit() { + var store = ProcessMetricHistoryStore(limit: 2) + + for value in 1...3 { + store.record( + processes: [process(pid: 700, cpu: Double(value), memory: Int64(value))], + gpuMetric: nil, + timestamp: Date(timeIntervalSince1970: TimeInterval(value)) + ) + } + + XCTAssertEqual(store.history(for: 700).map(\.cpuPercent), [2, 3]) + } + + func testDropsHistoryForInactiveProcesses() { + var store = ProcessMetricHistoryStore(limit: 3) + store.record( + processes: [ + process(pid: 700, cpu: 1, memory: 100), + process(pid: 701, cpu: 2, memory: 200), + ], + gpuMetric: nil, + timestamp: Date(timeIntervalSince1970: 1) + ) + + store.record( + processes: [process(pid: 701, cpu: 3, memory: 300)], + gpuMetric: nil, + timestamp: Date(timeIntervalSince1970: 2) + ) + + XCTAssertTrue(store.history(for: 700).isEmpty) + XCTAssertEqual(store.history(for: 701).map(\.cpuPercent), [2, 3]) + } + + func testResetsHistoryWhenAPIDIsRecycled() { + var store = ProcessMetricHistoryStore(limit: 3) + store.record( + processes: [ + process( + pid: 700, + cpu: 1, + memory: 100, + birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 100) + ) + ], + gpuMetric: nil, + timestamp: Date(timeIntervalSince1970: 1) + ) + + store.record( + processes: [ + process( + pid: 700, + cpu: 2, + memory: 200, + birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 101) + ) + ], + gpuMetric: nil, + timestamp: Date(timeIntervalSince1970: 2) + ) + + XCTAssertEqual(store.history(for: 700).map(\.cpuPercent), [2]) + } + + func testResetsRecycledPIDBeforeNewMetricsArrive() { + var store = ProcessMetricHistoryStore(limit: 3) + store.record( + processes: [ + process( + pid: 700, + cpu: 1, + memory: 100, + birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 100) + ) + ], + gpuMetric: nil, + timestamp: Date(timeIntervalSince1970: 1) + ) + + store.record( + processes: [ + processWithoutMetrics( + pid: 700, + birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 101) + ) + ], + gpuMetric: nil, + timestamp: Date(timeIntervalSince1970: 2) + ) + + XCTAssertTrue(store.history(for: 700).isEmpty) + } + + func testResetsUnknownBirthHistoryWhenFallbackIdentityChanges() { + var store = ProcessMetricHistoryStore(limit: 3) + store.record( + processes: [process(pid: 700, cpu: 1, memory: 100)], + gpuMetric: nil, + timestamp: Date(timeIntervalSince1970: 1) + ) + + store.record( + processes: [ + process( + pid: 700, + cpu: 2, + memory: 200, + command: "worker --replacement" + ) + ], + gpuMetric: nil, + timestamp: Date(timeIntervalSince1970: 2) + ) + + XCTAssertEqual(store.history(for: 700).map(\.cpuPercent), [2]) + } + + func testRetainsHistoryWhenKnownBirthMetadataIsEnriched() { + var store = ProcessMetricHistoryStore(limit: 3) + let birthToken = ProcessBirthToken(seconds: 1_000, microseconds: 100) + store.record( + processes: [ + process( + pid: 700, + cpu: 1, + memory: 100, + executable: "node", + command: "node", + birthToken: birthToken + ) + ], + gpuMetric: nil, + timestamp: Date(timeIntervalSince1970: 1) + ) + + store.record( + processes: [ + process( + pid: 700, + cpu: 2, + memory: 200, + parentPID: 99, + executable: "/opt/homebrew/bin/node", + command: "/opt/homebrew/bin/node server.js", + birthToken: birthToken + ) + ], + gpuMetric: nil, + timestamp: Date(timeIntervalSince1970: 2) + ) + + XCTAssertEqual(store.history(for: 700).map(\.cpuPercent), [1, 2]) + } + + private func process( + pid: Int32, + cpu: Double, + memory: Int64, + parentPID: Int32 = 1, + executable: String = "worker", + command: String = "worker --serve", + birthToken: ProcessBirthToken? = nil + ) -> DevProcess { + DevProcess( + pid: pid, + parentPID: parentPID, + executable: executable, + command: command, + resourceUsage: DevProcessResourceUsage( + cpuPercent: cpu, + residentMemoryBytes: memory, + elapsedTime: "00:10" + ), + birthToken: birthToken + ) + } + + private func processWithoutMetrics( + pid: Int32, + birthToken: ProcessBirthToken + ) -> DevProcess { + DevProcess( + pid: pid, + parentPID: 1, + executable: "worker", + command: "worker --serve", + birthToken: birthToken + ) + } +} diff --git a/Tests/DevScopeCoreTests/ProcessPresentationTests.swift b/Tests/DevScopeCoreTests/ProcessPresentationTests.swift new file mode 100644 index 0000000..ce629bf --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessPresentationTests.swift @@ -0,0 +1,676 @@ +import XCTest + +@testable import DevScopeCore + +final class ProcessPresentationTests: XCTestCase { + func testFiltersByRuntimeAndSearchableFields() { + let items = sampleProcesses + + let filtered = ProcessPresentation.filtered( + items, + categoryID: DevRuntimeKind.javascript.id, + searchText: "web 3010" + ) + + XCTAssertEqual(filtered.map(\.process.pid), [101]) + } + + func testFiltersSavedCategoriesByStableIdentityKey() { + let items = sampleProcesses + let favoriteKey = ProcessPresentation.identityKey(for: items[1]) + let watchedKey = ProcessPresentation.identityKey(for: items[2]) + + XCTAssertEqual( + ProcessPresentation.filtered( + items, + categoryID: ProcessPresentation.favoritesCategoryID, + searchText: "", + favoriteKeys: [favoriteKey] + ).map(\.process.pid), + [102] + ) + XCTAssertEqual( + ProcessPresentation.filtered( + items, + categoryID: ProcessPresentation.watchedCategoryID, + searchText: "", + watchedKeys: [watchedKey] + ).map(\.process.pid), + [201] + ) + } + + func testEmptySavedKeysSkipIdentityHashing() { + var identityLookupCount = 0 + + let isSaved = ProcessPresentation.isSaved( + sampleProcesses[0], + in: [], + identityKeys: { _ in + identityLookupCount += 1 + return ["should-not-be-computed"] + } + ) + + XCTAssertFalse(isSaved) + XCTAssertEqual(identityLookupCount, 0) + } + + func testSavedCategorySurvivesWorkingDirectoryEnrichment() { + let initial = ClassifiedDevProcess( + process: DevProcess( + pid: 51963, + parentPID: 1, + executable: "/bin/sleep", + command: "/bin/sleep 317", + currentDirectory: nil + ), + classification: DevProcessClassification( + kind: .other, + displayName: "sleep", + projectHint: nil, + tags: [] + ) + ) + let enriched = ClassifiedDevProcess( + process: DevProcess( + pid: 51963, + parentPID: 1, + executable: "/bin/sleep", + command: "/bin/sleep 317", + currentDirectory: "/Users/example/dev/sample-app" + ), + classification: DevProcessClassification( + kind: .other, + displayName: "sleep", + projectHint: "devscope", + tags: [] + ) + ) + let savedKey = ProcessPresentation.identityKey(for: initial) + + let filtered = ProcessPresentation.filtered( + [enriched], + categoryID: ProcessPresentation.favoritesCategoryID, + searchText: "", + favoriteKeys: [savedKey] + ) + + XCTAssertEqual(filtered.map(\.process.pid), [51963]) + } + + func testSavedIdentityKeyDoesNotContainCommandOrWorkingDirectory() { + let item = ClassifiedDevProcess( + process: DevProcess( + pid: 98, + parentPID: 1, + executable: "/usr/local/bin/worker", + command: "worker --api-key sk-private-value", + currentDirectory: "/Users/example/Secret Project" + ), + classification: DevProcessClassification( + kind: .other, + displayName: "worker", + projectHint: "Secret Project", + tags: [] + ) + ) + + let key = ProcessPresentation.identityKey(for: item) + + XCTAssertTrue(key.hasPrefix("v2:")) + XCTAssertFalse(key.contains("sk-private-value")) + XCTAssertFalse(key.contains("Secret Project")) + XCTAssertFalse(key.contains("/Users/example")) + } + + func testSanitizedSavedIdentityKeysMigrateLegacyValuesWithoutRetainingRawData() { + let safeKey = "v2:" + String(repeating: "a", count: 64) + let legacyKey = + "worker\u{1F}/Users/example/Secret Project\u{1F}worker --api-key sk-private-value" + let keys = ProcessPresentation.sanitizedSavedIdentityKeys([ + safeKey, + legacyKey, + "v2:not-a-digest", + "malformed legacy value", + ]) + + XCTAssertEqual(keys.count, 2) + XCTAssertTrue(keys.contains(safeKey)) + XCTAssertTrue(keys.allSatisfy { $0.hasPrefix("v2:") }) + XCTAssertFalse(keys.joined().contains("sk-private-value")) + } + + func testRedactsSensitiveCommandValuesForExport() { + let command = """ + uvicorn app:app token=abc123 Authorization bearer secret-token password=hunter2 --api-key sk-test --client-secret client-value postgres://user:dbpass@localhost/db + """ + + let redacted = ProcessPresentation.redactedCommand(command) + + XCTAssertFalse(redacted.contains("abc123")) + XCTAssertFalse(redacted.contains("secret-token")) + XCTAssertFalse(redacted.contains("hunter2")) + XCTAssertFalse(redacted.contains("sk-test")) + XCTAssertFalse(redacted.contains("client-value")) + XCTAssertFalse(redacted.contains("dbpass")) + XCTAssertTrue(redacted.contains("token=")) + XCTAssertTrue(redacted.contains("bearer ")) + XCTAssertTrue(redacted.contains("password=")) + XCTAssertTrue(redacted.contains("--api-key ")) + XCTAssertTrue(redacted.contains("--client-secret ")) + XCTAssertTrue(redacted.contains("postgres://@localhost/db")) + } + + func testRedactsCommonShortCredentialFlags() { + let command = + "redis-cli -a hunter2; mysql -pdbsecret; curl -u user:curlsecret https://example.test" + + let redacted = ProcessPresentation.redactedCommand(command) + + XCTAssertFalse(redacted.contains("hunter2")) + XCTAssertFalse(redacted.contains("dbsecret")) + XCTAssertFalse(redacted.contains("curlsecret")) + } + + func testRedactsQuotedShortCredentialFlagValuesContainingSpaces() { + let command = + "redis-cli -a 'hunter 2'; mysql -p\"db secret\"; curl -u 'user:curl secret' https://example.test" + + let redacted = ProcessPresentation.redactedCommand(command) + + XCTAssertFalse(redacted.contains("hunter")) + XCTAssertFalse(redacted.contains("db secret")) + XCTAssertFalse(redacted.contains("curl secret")) + XCTAssertFalse(redacted.contains("2'")) + } + + func testRedactsReconstructedPSCredentialValuesThroughCommandBoundary() { + let commands = [ + "redis-cli -a hunter --tls-shaped secret;stillsecret", + "mysql -pdb --host-shaped secret;stillsecret", + "curl -u user:curl --header-shaped secret;stillsecret", + ] + + for command in commands { + let redacted = ProcessPresentation.redactedCommand(command) + + XCTAssertFalse(redacted.contains("hunter")) + XCTAssertFalse(redacted.contains("--tls-shaped")) + XCTAssertFalse(redacted.contains("--host-shaped")) + XCTAssertFalse(redacted.contains("--header-shaped")) + XCTAssertFalse(redacted.contains("stillsecret")) + XCTAssertEqual(redacted.components(separatedBy: "").count - 1, 1) + } + } + + func testRedactsCurlLongCredentialOptionsThroughEndOfRow() { + let commands = [ + "curl --user analyst:SYNTH_USER_SECRET --next-shaped value", + "curl --user=analyst:SYNTH_USER_EQUALS_SECRET;stillsecret", + "curl --proxy-user relay:SYNTH_PROXY_SECRET --proxy-shaped value", + "curl --proxy-user=relay:SYNTH_PROXY_EQUALS_SECRET;stillsecret", + ] + + for command in commands { + let redacted = ProcessPresentation.redactedCommand(command) + + XCTAssertFalse(redacted.contains("SYNTH_")) + XCTAssertFalse(redacted.contains("stillsecret")) + XCTAssertFalse(redacted.contains("--next-shaped")) + XCTAssertFalse(redacted.contains("--proxy-shaped")) + XCTAssertEqual(redacted.components(separatedBy: "").count - 1, 1) + } + } + + func testRedactsColonDelimitedCredentialHeaders() { + let cases = [ + ( + command: "curl -H 'X-API-Key: SYNTH_API_KEY' https://example.test/resource", + secret: "SYNTH_API_KEY", + expected: "X-API-Key: " + ), + ( + command: + "curl --header \"Authorization: Basic SYNTH_BASIC_TOKEN\" https://example.test/resource", + secret: "SYNTH_BASIC_TOKEN", + expected: "Authorization: Basic " + ), + ( + command: "curl -H 'x-auth-token : SYNTH_AUTH_TOKEN' https://example.test/resource", + secret: "SYNTH_AUTH_TOKEN", + expected: "x-auth-token : " + ), + ] + + for testCase in cases { + let redacted = ProcessPresentation.redactedCommand(testCase.command) + + XCTAssertFalse(redacted.contains(testCase.secret), testCase.command) + XCTAssertTrue(redacted.contains(testCase.expected), redacted) + XCTAssertTrue(redacted.contains("https://example.test/resource"), redacted) + } + + let nonSensitive = "curl -H 'Accept: application/json' https://example.test/resource" + XCTAssertEqual(ProcessPresentation.redactedCommand(nonSensitive), nonSensitive) + } + + func testRedactsCredentialHeaderAliasesInsteadOfOnlyAFixedHeaderList() { + let command = """ + curl -H 'PRIVATE-TOKEN: SYNTH_GITLAB_SECRET' -H 'X-Goog-Api-Key: SYNTH_GOOGLE_SECRET' -H 'X-Amz-Security-Token: SYNTH_AWS_SECRET' https://example.test + """ + + let redacted = ProcessPresentation.redactedCommand(command) + + XCTAssertFalse(redacted.contains("SYNTH_GITLAB_SECRET")) + XCTAssertFalse(redacted.contains("SYNTH_GOOGLE_SECRET")) + XCTAssertFalse(redacted.contains("SYNTH_AWS_SECRET")) + XCTAssertTrue(redacted.contains("PRIVATE-TOKEN: ")) + XCTAssertTrue(redacted.contains("X-Goog-Api-Key: ")) + XCTAssertTrue(redacted.contains("X-Amz-Security-Token: ")) + } + + func testRedactsCompleteSensitiveHeaderValuesAcrossAuthenticationSchemesAndCookies() { + let cases = [ + ( + command: + "curl -H 'Authorization: Digest username=alice, response=SYNTH_DIGEST_SECRET' https://example.test/digest", + secrets: ["alice", "SYNTH_DIGEST_SECRET"], + retainedURL: "https://example.test/digest" + ), + ( + command: + "curl --header \"Authorization: AWS4-HMAC-SHA256 Credential=fixture, Signature=SYNTH_AWS_SIGNATURE\" https://example.test/aws", + secrets: ["fixture", "SYNTH_AWS_SIGNATURE"], + retainedURL: "https://example.test/aws" + ), + ( + command: + "curl -H 'Proxy-Authorization: Negotiate SYNTH_PROXY_SECRET' -H 'Cookie: session=SYNTH_SESSION_COOKIE' https://example.test/cookie", + secrets: ["SYNTH_PROXY_SECRET", "SYNTH_SESSION_COOKIE"], + retainedURL: "https://example.test/cookie" + ), + ( + command: + "curl -H 'Authorization: Digest username=\"alice\", response=\"SYNTH_QUOTED_DIGEST\"' https://example.test/quoted-digest", + secrets: ["alice", "SYNTH_QUOTED_DIGEST"], + retainedURL: "https://example.test/quoted-digest" + ), + ( + command: + "curl -H 'Cookie: session=\"SYNTH_QUOTED_COOKIE\"; csrf=\"SYNTH_QUOTED_CSRF\"' https://example.test/quoted-cookie", + secrets: ["SYNTH_QUOTED_COOKIE", "SYNTH_QUOTED_CSRF"], + retainedURL: "https://example.test/quoted-cookie" + ), + ( + command: + #"curl -H "Authorization: Digest username=\"alice\", response=\"SYNTH_ESCAPED_DIGEST\"" https://example.test/escaped-digest"#, + secrets: ["alice", "SYNTH_ESCAPED_DIGEST"], + retainedURL: "https://example.test/escaped-digest" + ), + ( + command: + "curl -H X-API-Key:SYNTH_UNQUOTED_SECRET https://example.test/unquoted --verbose", + secrets: ["SYNTH_UNQUOTED_SECRET"], + retainedURL: "https://example.test/unquoted" + ), + ] + + for testCase in cases { + let redacted = ProcessPresentation.redactedCommand(testCase.command) + + for secret in testCase.secrets { + XCTAssertFalse(redacted.contains(secret), redacted) + } + XCTAssertTrue(redacted.contains(testCase.retainedURL), redacted) + XCTAssertTrue(redacted.contains(""), redacted) + } + } + + func testBulkExportRedactsColonDelimitedCredentialHeaders() { + let item = ClassifiedDevProcess( + process: DevProcess( + pid: 4_202, + parentPID: 1, + executable: "/usr/bin/curl", + command: "curl -H 'X-API-Key: SYNTH_EXPORT_HEADER_SECRET' https://example.test/private" + ), + classification: DevProcessClassification( + kind: .other, + displayName: "curl", + projectHint: nil, + tags: [] + ) + ) + + let export = ProcessPresentation.exportRows([item]) + + XCTAssertFalse(export.contains("SYNTH_EXPORT_HEADER_SECRET")) + XCTAssertTrue(export.contains("X-API-Key: ")) + } + + func testBulkExportRedactsCompleteCookieHeaderValue() { + let item = ClassifiedDevProcess( + process: DevProcess( + pid: 4_203, + parentPID: 1, + executable: "/usr/bin/curl", + command: + "curl -H 'Cookie: session=\"SYNTH_EXPORT_COOKIE\"; csrf=\"SYNTH_EXPORT_CSRF\"' https://example.test/private" + ), + classification: DevProcessClassification( + kind: .other, + displayName: "curl", + projectHint: nil, + tags: [] + ) + ) + + let export = ProcessPresentation.exportRows([item]) + + XCTAssertFalse(export.contains("SYNTH_EXPORT_COOKIE")) + XCTAssertFalse(export.contains("SYNTH_EXPORT_CSRF")) + XCTAssertTrue(export.contains("Cookie: ")) + XCTAssertTrue(export.contains("https://example.test/private")) + } + + func testBulkExportRedactsCurlLongCredentialOptions() { + let item = ClassifiedDevProcess( + process: DevProcess( + pid: 4_201, + parentPID: 1, + executable: "/usr/bin/curl", + command: "curl --user analyst:SYNTH_EXPORT_SECRET https://example.test/private" + ), + classification: DevProcessClassification( + kind: .other, + displayName: "curl", + projectHint: nil, + tags: [] + ) + ) + + let export = ProcessPresentation.exportRows([item]) + + XCTAssertFalse(export.contains("SYNTH_EXPORT_SECRET")) + XCTAssertTrue(export.contains("curl --user ")) + } + + func testExportRowsNeutralizesSpreadsheetFormulasAndCellBreaks() { + let item = ClassifiedDevProcess( + process: DevProcess( + pid: 1_234, + parentPID: 1, + executable: "/tmp/tool", + command: "=HYPERLINK(\"https://example.test\")\tspill\nrow" + ), + classification: DevProcessClassification( + kind: .other, + displayName: "+formula", + projectHint: "@project", + tags: [] + ) + ) + + let export = ProcessPresentation.exportRows([item]) + let rows = export.split(separator: "\n", omittingEmptySubsequences: false) + + XCTAssertEqual(rows.count, 2) + XCTAssertTrue(export.contains("'@project")) + XCTAssertTrue(export.contains("'+formula")) + XCTAssertTrue(export.contains("'=HYPERLINK")) + XCTAssertFalse(rows[1].contains("spill\trow")) + } + + func testContextLabelSuppressesTruncatedExecutableFragments() { + let systemItem = ClassifiedDevProcess( + process: DevProcess( + pid: 317, + parentPID: 1, + executable: "Library", + command: + "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/Support/fseventsd" + ), + classification: DevProcessClassification( + kind: .systemService, + displayName: "fseventsd", + projectHint: nil, + tags: [] + ) + ) + let shortPrefixItem = ClassifiedDevProcess( + process: DevProcess( + pid: 319, + parentPID: 1, + executable: "Use", + command: "/usr/libexec/UserEventAgent" + ), + classification: DevProcessClassification( + kind: .systemService, + displayName: "UserEventAgent", + projectHint: nil, + tags: [] + ) + ) + let projectItem = classified( + pid: 101, kind: .javascript, displayName: "next dev", project: "web", command: "node next dev" + ) + let regularItem = ClassifiedDevProcess( + process: DevProcess( + pid: 102, parentPID: 1, executable: "worker", command: "worker --foreground"), + classification: DevProcessClassification( + kind: .other, displayName: "worker", projectHint: nil, tags: []) + ) + + XCTAssertEqual(ProcessPresentation.contextLabel(for: systemItem), "System Service") + XCTAssertEqual(ProcessPresentation.contextLabel(for: shortPrefixItem), "System Service") + XCTAssertEqual(ProcessPresentation.contextLabel(for: projectItem), "web") + XCTAssertEqual(ProcessPresentation.contextLabel(for: regularItem), "worker") + } + + func testExecutablePathPrefersCommandPathForTruncatedExecutableFragments() { + let shortPrefixItem = ClassifiedDevProcess( + process: DevProcess( + pid: 319, + parentPID: 1, + executable: "/usr/libexec/Use", + command: "/usr/libexec/UserEventAgent (System)" + ), + classification: DevProcessClassification( + kind: .systemService, + displayName: "UserEventAgent", + projectHint: nil, + tags: [] + ) + ) + let regularItem = ClassifiedDevProcess( + process: DevProcess( + pid: 102, parentPID: 1, executable: "/opt/custom/bin/worker", + command: "/opt/custom/bin/worker --foreground"), + classification: DevProcessClassification( + kind: .other, displayName: "worker", projectHint: nil, tags: []) + ) + + XCTAssertEqual( + ProcessPresentation.executablePath(for: shortPrefixItem), "/usr/libexec/UserEventAgent") + XCTAssertEqual(ProcessPresentation.executablePath(for: regularItem), "/opt/custom/bin/worker") + } + + func testSummarizesVisibleProcessesByRuntimeAndProject() { + let summary = ProcessPresentation.summary(for: sampleProcesses) + + XCTAssertEqual(summary.totalCount, 4) + XCTAssertEqual(summary.count(for: .javascript), 2) + XCTAssertEqual(summary.count(for: .python), 1) + XCTAssertEqual(summary.count(for: .swift), 1) + XCTAssertEqual(summary.count(for: .shell), 0) + XCTAssertEqual(summary.projectCount, 3) + XCTAssertEqual(summary.primaryProject, "devscope") + } + + func testBuildsDynamicCategoriesOnlyForDetectedProcessTypes() { + let categories = ProcessPresentation.categories(for: sampleProcesses) + + XCTAssertEqual(categories.map(\.title), ["All", "JavaScript", "Python", "Swift"]) + XCTAssertEqual(categories.map(\.count), [4, 2, 1, 1]) + } + + func testSavedCategoryCountSkipsIdentityChecksWhenNoKeysAreConfigured() { + var identityCheckCount = 0 + + let count = ProcessPresentation.savedCount( + in: sampleProcesses, + keys: [] + ) { _, _ in + identityCheckCount += 1 + return true + } + + XCTAssertEqual(count, 0) + XCTAssertEqual(identityCheckCount, 0) + } + + func testSortsProcessesByActivityMonitorStyleMetrics() { + let items = [ + classified( + pid: 101, kind: .javascript, displayName: "next dev", project: "web", + command: "node next dev", cpu: 3, memory: 900), + classified( + pid: 102, kind: .python, displayName: "python worker", project: "api", + command: "python worker.py", cpu: 48, memory: 300), + classified( + pid: 103, kind: .swift, displayName: "swift build", project: "devscope", + command: "swift build", cpu: 12, memory: 1_600), + ] + + XCTAssertEqual( + ProcessPresentation.sorted(items, by: .cpuDescending).map(\.process.pid), [102, 103, 101]) + XCTAssertEqual( + ProcessPresentation.sorted(items, by: .memoryDescending).map(\.process.pid), [103, 101, 102]) + XCTAssertEqual( + ProcessPresentation.sorted(items, by: .processNameAscending).map(\.process.pid), + [101, 102, 103]) + } + + func testParsesElapsedTimeForChronologicalTableSorting() { + XCTAssertEqual(ProcessPresentation.elapsedSeconds("00:42"), 42) + XCTAssertEqual(ProcessPresentation.elapsedSeconds("02:03:04"), 7_384) + XCTAssertEqual(ProcessPresentation.elapsedSeconds("9-00:00:00"), 777_600) + XCTAssertEqual(ProcessPresentation.elapsedSeconds("10-00:00:00"), 864_000) + XCTAssertEqual(ProcessPresentation.elapsedSeconds("9223372036854775807-00:00:00"), -1) + XCTAssertEqual(ProcessPresentation.elapsedSeconds("-"), -1) + } + + func testSummarizesProcessFamilyForDetailPanels() { + let processes = [ + DevProcess(pid: 10, parentPID: 1, executable: "npm", command: "npm run dev"), + DevProcess(pid: 11, parentPID: 10, executable: "sh", command: "sh -c next dev"), + DevProcess(pid: 12, parentPID: 11, executable: "node", command: "node next dev"), + DevProcess(pid: 20, parentPID: 1, executable: "python", command: "python worker.py"), + ] + + let family = ProcessPresentation.familySummary(for: processes[0], in: processes) + + XCTAssertEqual(family.parentPID, 1) + XCTAssertEqual(family.childCount, 1) + XCTAssertEqual(family.descendantCount, 2) + } + + func testBuildsDashboardStatsForVisibleProcesses() { + let totalItems = [ + classified( + pid: 101, kind: .javascript, displayName: "next dev", project: "web", + command: "node next dev", cpu: 3, memory: 900), + classified( + pid: 102, kind: .python, displayName: "python train", project: "ml", + command: "python train.py", cpu: 48, memory: 300, tags: [.training]), + classified( + pid: 103, kind: .ai, displayName: "Ollama server", project: "llm", command: "ollama serve", + cpu: 12, memory: 1_600, tags: [.llmServer]), + classified( + pid: 104, kind: .swift, displayName: "swift build", project: "devscope", + command: "swift build", cpu: 80, memory: 2_400), + ] + let visibleItems = Array(totalItems.prefix(3)) + + let stats = ProcessPresentation.dashboardStats( + visibleItems: visibleItems, + totalItems: totalItems, + dashboardMetricHistory: [ + DevProcessMetricSample( + timestamp: Date(), + cpuPercent: 63, + residentMemoryBytes: 2_800, + gpuPercent: 42 + ) + ] + ) + + XCTAssertEqual(stats.visibleCount, 3) + XCTAssertEqual(stats.totalCount, 4) + XCTAssertEqual(stats.aiMLCount, 2) + XCTAssertEqual(stats.topCPU?.process.pid, 104) + XCTAssertEqual(stats.topMemory?.process.pid, 104) + XCTAssertEqual(stats.runtimeCounts[.python], 1) + XCTAssertEqual(stats.runtimeCounts[.swift], 1) + XCTAssertEqual(stats.latestGPUPercent, 42) + } + + func testParsesAppleAGXGPUUtilizationFromIORegistryOutput() { + let output = #""" + +-o AGXAcceleratorG15X + "PerformanceStatistics" = {"Renderer Utilization %"=82,"Device Utilization %"=83,"In use system memory"=3049406464} + "model" = "Apple M3 Max" + """# + + let metric = SystemGPUMetricProvider.parse(output) + + XCTAssertEqual(metric?.utilizationPercent, 83) + XCTAssertEqual(metric?.modelName, "Apple M3 Max") + } + + private var sampleProcesses: [ClassifiedDevProcess] { + [ + classified( + pid: 101, kind: .javascript, displayName: "next dev", project: "web", + command: "node next dev --port 3010"), + classified( + pid: 102, kind: .javascript, displayName: "vite", project: "devscope", command: "pnpm vite"), + classified( + pid: 201, kind: .python, displayName: "uvicorn", project: "api", + command: "uvicorn app.main:app --reload"), + classified( + pid: 301, kind: .swift, displayName: "swift build", project: "devscope", + command: "swift build"), + ] + } + + private func classified( + pid: Int32, + kind: DevRuntimeKind, + displayName: String, + project: String, + command: String, + cpu: Double = 0, + memory: Int64 = 0, + tags: [DevProcessTag] = [] + ) -> ClassifiedDevProcess { + ClassifiedDevProcess( + process: DevProcess( + pid: pid, + parentPID: 1, + executable: command.split(separator: " ").first.map(String.init) ?? command, + command: command, + currentDirectory: "\(NSHomeDirectory())/dev/example/\(project)", + resourceUsage: DevProcessResourceUsage( + cpuPercent: cpu, residentMemoryBytes: memory, elapsedTime: "00:01") + ), + classification: DevProcessClassification( + kind: kind, + displayName: displayName, + projectHint: project, + tags: tags + ) + ) + } +} diff --git a/Tests/DevScopeCoreTests/ProcessScopePresentationTests.swift b/Tests/DevScopeCoreTests/ProcessScopePresentationTests.swift new file mode 100644 index 0000000..20adc80 --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessScopePresentationTests.swift @@ -0,0 +1,209 @@ +import XCTest + +@testable import DevScopeCore + +final class ProcessScopePresentationTests: XCTestCase { + func testGroupsHostAppNestedHelperAndExternalWorkerWithoutOverlap() throws { + let items = [ + classified( + pid: 100, + parentPID: 1, + executable: "/Applications/ChatGPT.app/Contents/MacOS/ChatGPT", + cpu: 1, + memory: 100 + ), + classified( + pid: 101, + parentPID: 100, + executable: + "/Applications/ChatGPT.app/Contents/Frameworks/Codex Framework.framework/Helpers/Codex (Renderer).app/Contents/MacOS/Codex (Renderer)", + cpu: 2, + memory: 200 + ), + classified( + pid: 102, + parentPID: 101, + executable: "/usr/local/bin/npm", + command: "npm run dev", + cpu: 3, + memory: 300 + ), + classified( + pid: 200, + parentPID: 1, + executable: "/usr/bin/python3", + command: "python3 unrelated.py", + cpu: 50, + memory: 5_000 + ), + ] + + let family = try XCTUnwrap( + ProcessScopePresentation.applicationFamilies(for: items).first { + $0.bundlePath == "/Applications/ChatGPT.app" + } + ) + + XCTAssertEqual(family.title, "ChatGPT / Codex") + XCTAssertEqual(family.applicationCount, 1) + XCTAssertEqual(family.helperCount, 1) + XCTAssertEqual(family.workerCount, 1) + XCTAssertEqual(family.processIDs, [100, 101, 102]) + XCTAssertEqual(family.members.map(\.role), [.application, .helper, .worker]) + XCTAssertEqual(family.members.map(\.depth), [0, 1, 2]) + XCTAssertEqual(family.totalCPU, 6) + XCTAssertEqual(family.totalMemoryBytes, 600) + } + + func testApplicationFamiliesAreDeterministicAcrossInputOrder() { + let items = [ + classified( + pid: 10, + parentPID: 1, + executable: "/Applications/Alpha.app/Contents/MacOS/Alpha" + ), + classified( + pid: 11, + parentPID: 10, + executable: "/Applications/Alpha.app/Contents/Helpers/worker" + ), + classified( + pid: 20, + parentPID: 1, + executable: "/Applications/Beta.app/Contents/MacOS/Beta" + ), + ] + + XCTAssertEqual( + ProcessScopePresentation.applicationFamilies(for: items), + ProcessScopePresentation.applicationFamilies(for: items.reversed()) + ) + } + + func testApplicationOwnershipTerminatesAcrossMalformedParentCycle() throws { + let items = [ + classified( + pid: 300, + parentPID: 301, + executable: "/Applications/Cycle.app/Contents/Helpers/cycle-helper" + ), + classified( + pid: 301, + parentPID: 300, + executable: "/usr/local/bin/cycle-worker" + ), + classified( + pid: 302, + parentPID: 999, + executable: "/usr/local/bin/orphan" + ), + ] + + let family = try XCTUnwrap( + ProcessScopePresentation.applicationFamilies(for: items).first { + $0.bundlePath == "/Applications/Cycle.app" + } + ) + + XCTAssertEqual(family.processIDs, [300, 301]) + XCTAssertEqual(family.applicationCount, 0) + XCTAssertEqual(family.helperCount, 1) + XCTAssertEqual(family.workerCount, 1) + XCTAssertFalse(family.processIDs.contains(302)) + } + + func testDoesNotTruncateAppExtensionsIntoFakeApplicationBundles() { + let item = classified( + pid: 350, + parentPID: 1, + executable: + "/System/Library/ExtensionKit/Extensions/AccessibilitySettingsSearchExtension.appex/Contents/MacOS/AccessibilitySettingsSearchExtension" + ) + + XCTAssertTrue(ProcessScopePresentation.applicationFamilies(for: [item]).isEmpty) + } + + func testActivityScopeUsesExplicitUserFacingLabels() { + XCTAssertEqual(ProcessActivityScope.applications.title, "Applications") + XCTAssertEqual(ProcessActivityScope.processes.title, "Processes") + XCTAssertEqual(ProcessActivityScope.hierarchy.title, "Hierarchy") + XCTAssertEqual(ProcessActivityScope.workflows.title, "Workflows") + } + + func testHierarchySearchRetainsMatchingProcessAncestorPath() { + let items = [ + classified(pid: 100, parentPID: 1, executable: "/usr/bin/zsh"), + classified(pid: 101, parentPID: 100, executable: "/usr/local/bin/node"), + classified( + pid: 102, + parentPID: 101, + executable: "/usr/local/bin/node", + command: "node worker.js" + ), + classified(pid: 200, parentPID: 1, executable: "/usr/bin/python3"), + ] + + let filtered = ProcessScopePresentation.hierarchy(for: items, searchText: "worker.js") + + XCTAssertEqual(filtered.map(\.id), [100]) + XCTAssertEqual(filtered[0].children.map(\.id), [101]) + XCTAssertEqual(filtered[0].children[0].children.map(\.id), [102]) + XCTAssertEqual(ProcessScopePresentation.flattened(filtered).map(\.id), [100, 101, 102]) + } + + func testHierarchyKeepsMissingParentsAsRootsAndOrdersSiblingsByPID() { + let items = [ + classified(pid: 12, parentPID: 10, executable: "/usr/bin/child-b"), + classified(pid: 11, parentPID: 10, executable: "/usr/bin/child-a"), + classified(pid: 10, parentPID: 999, executable: "/usr/bin/root"), + ] + + let hierarchy = ProcessScopePresentation.hierarchy(for: items) + + XCTAssertEqual(hierarchy.map(\.id), [10]) + XCTAssertEqual(hierarchy[0].children.map(\.id), [11, 12]) + } + + func testHierarchyBreaksCyclesAndRepresentsEveryPIDOnce() { + let items = [ + classified(pid: 30, parentPID: 32, executable: "/usr/bin/thirty"), + classified(pid: 31, parentPID: 30, executable: "/usr/bin/thirty-one"), + classified(pid: 32, parentPID: 31, executable: "/usr/bin/thirty-two"), + classified(pid: 40, parentPID: 1, executable: "/usr/bin/forty"), + ] + + let hierarchy = ProcessScopePresentation.hierarchy(for: items) + let processIDs = ProcessScopePresentation.flattened(hierarchy).map(\.id) + + XCTAssertEqual(processIDs.sorted(), [30, 31, 32, 40]) + XCTAssertEqual(Set(processIDs).count, processIDs.count) + } + + private func classified( + pid: Int32, + parentPID: Int32, + executable: String, + command: String? = nil, + cpu: Double = 0, + memory: Int64 = 0 + ) -> ClassifiedDevProcess { + ClassifiedDevProcess( + process: DevProcess( + pid: pid, + parentPID: parentPID, + executable: executable, + command: command ?? executable, + resourceUsage: DevProcessResourceUsage( + cpuPercent: cpu, + residentMemoryBytes: memory, + elapsedTime: "00:01" + ) + ), + classification: DevProcessClassification( + kind: .macApp, + displayName: URL(fileURLWithPath: executable).lastPathComponent, + projectHint: nil + ) + ) + } +} diff --git a/Tests/DevScopeCoreTests/ProcessSignalObservationTests.swift b/Tests/DevScopeCoreTests/ProcessSignalObservationTests.swift new file mode 100644 index 0000000..096b77a --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessSignalObservationTests.swift @@ -0,0 +1,210 @@ +import XCTest +@testable import DevScopeCore + +final class ProcessSignalObservationTests: XCTestCase { + func testSameBirthWithMetadataEnrichmentIsStillRunning() { + let birthToken = ProcessBirthToken(seconds: 1_000, microseconds: 100) + let original = DevProcess( + pid: 42, + parentPID: 1, + executable: "node", + command: "node", + birthToken: birthToken + ) + let enriched = DevProcess( + pid: 42, + parentPID: 99, + executable: "/opt/homebrew/bin/node", + command: "/opt/homebrew/bin/node server.js", + currentDirectory: "/tmp/project", + birthToken: birthToken + ) + let target = ProcessIdentity(process: original) + + let summary = ProcessSignalObservation.observe( + targets: [target], + in: [enriched] + ) + + XCTAssertEqual( + summary.observations, + [ProcessSignalTargetObservation(target: target, state: .stillRunning)] + ) + XCTAssertFalse(summary.verifiesAllTargetsStopped) + } + + func testAbsentTargetIsExitedOrReplacedAndVerifiesStopped() { + let target = ProcessIdentity( + process: DevProcess( + pid: 42, + parentPID: 1, + executable: "node", + command: "node", + birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 100) + ) + ) + + let summary = ProcessSignalObservation.observe(targets: [target], in: []) + + XCTAssertEqual( + summary.observations, + [ProcessSignalTargetObservation(target: target, state: .exitedOrReplaced)] + ) + XCTAssertTrue(summary.verifiesAllTargetsStopped) + } + + func testReusedPIDIsExitedOrReplacedAndVerifiesOriginalStopped() { + let target = ProcessIdentity( + process: DevProcess( + pid: 42, + parentPID: 1, + executable: "node", + command: "node", + birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 100) + ) + ) + let replacement = DevProcess( + pid: 42, + parentPID: 1, + executable: "node", + command: "node", + birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 101) + ) + + let summary = ProcessSignalObservation.observe( + targets: [target], + in: [replacement] + ) + + XCTAssertEqual(summary.exitedOrReplaced, [target]) + XCTAssertTrue(summary.verifiesAllTargetsStopped) + } + + func testUnknownExpectedOrCurrentBirthIsUnverifiableAndNeverSuccess() { + let unknownExpected = ProcessIdentity( + process: DevProcess( + pid: 41, + parentPID: 1, + executable: "node", + command: "node" + ) + ) + let knownExpected = ProcessIdentity( + process: DevProcess( + pid: 42, + parentPID: 1, + executable: "node", + command: "node", + birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 100) + ) + ) + let currentProcesses = [ + DevProcess( + pid: 41, + parentPID: 1, + executable: "node", + command: "node", + birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 99) + ), + DevProcess( + pid: 42, + parentPID: 1, + executable: "node", + command: "node" + ), + ] + + let summary = ProcessSignalObservation.observe( + targets: [unknownExpected, knownExpected], + in: currentProcesses + ) + + XCTAssertEqual(summary.unverifiable, [unknownExpected, knownExpected]) + XCTAssertFalse(summary.verifiesAllTargetsStopped) + } + + func testPartialAllStoppedPresentationNeverClaimsFullCompletion() { + let signaled = ProcessIdentity( + process: DevProcess( + pid: 12, + parentPID: 11, + executable: "node", + command: "node worker.js", + birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 12) + ) + ) + let failed = ProcessIdentity( + process: DevProcess( + pid: 11, + parentPID: 10, + executable: "node", + command: "node server.js", + birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 11) + ) + ) + let observation = ProcessSignalObservation.observe(targets: [signaled], in: []) + + let presentation = ProcessPartialSignalVerificationPresentation.make( + signalName: "TERM", + observation: observation, + failedTarget: failed, + reason: "PID 11 was reused" + ) + + XCTAssertEqual(presentation.title, "Partial result verified") + XCTAssertEqual( + presentation.detail, + "1 signaled process stopped. PID 11 failed: PID 11 was reused. The failed target and all remaining tree targets were not signaled." + ) + XCTAssertFalse(presentation.title.contains("Process stopped")) + } + + func testPartialPendingPresentationRetainsStillRunningAndUnverifiableStates() { + let token = ProcessBirthToken(seconds: 1_000, microseconds: 1) + let stopped = identity(pid: 12, birthToken: token) + let stillRunning = identity(pid: 13, birthToken: token) + let unverifiable = identity(pid: 14, birthToken: token) + let failed = identity(pid: 11, birthToken: token) + let currentProcesses = [ + process(pid: 13, birthToken: token), + process(pid: 14, birthToken: nil), + ] + let observation = ProcessSignalObservation.observe( + targets: [stopped, stillRunning, unverifiable], + in: currentProcesses + ) + + let presentation = ProcessPartialSignalVerificationPresentation.make( + signalName: "TERM", + observation: observation, + failedTarget: failed, + reason: "permission denied" + ) + + XCTAssertEqual(presentation.title, "Partial result not fully verified") + XCTAssertEqual( + presentation.detail, + "1 signaled process stopped. Still running: 13. Could not verify: 14. PID 11 failed: permission denied. The failed target and all remaining tree targets were not signaled." + ) + } + + private func identity( + pid: Int32, + birthToken: ProcessBirthToken? + ) -> ProcessIdentity { + ProcessIdentity(process: process(pid: pid, birthToken: birthToken)) + } + + private func process( + pid: Int32, + birthToken: ProcessBirthToken? + ) -> DevProcess { + DevProcess( + pid: pid, + parentPID: 1, + executable: "node", + command: "node worker.js", + birthToken: birthToken + ) + } +} diff --git a/Tests/DevScopeCoreTests/ProcessSnapshotBuilderTests.swift b/Tests/DevScopeCoreTests/ProcessSnapshotBuilderTests.swift new file mode 100644 index 0000000..d849450 --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessSnapshotBuilderTests.swift @@ -0,0 +1,130 @@ +import XCTest +@testable import DevScopeCore + +final class ProcessSnapshotBuilderTests: XCTestCase { + func testInvalidatingWorkspaceFactsReclassifiesAnUnchangedProcess() throws { + var manifestExists = false + var probeCount = 0 + let workspaceFactsCache = WorkspaceFactsCache { path in + probeCount += 1 + return manifestExists && path.hasSuffix("/pubspec.yaml") + } + var builder = ProcessSnapshotBuilder(workspaceFactsCache: workspaceFactsCache) + let process = DevProcess( + pid: 799, + parentPID: 1, + executable: "workspace-runner", + command: "workspace-runner serve", + currentDirectory: NSHomeDirectory() + "/dev/example/app", + birthToken: ProcessBirthToken(seconds: 1_000, microseconds: 41) + ) + let start = Date(timeIntervalSince1970: 100) + + let beforeManifest = builder.build( + processes: [process], + now: start, + graceInterval: 2.5 + ) + manifestExists = true + builder.invalidateWorkspaceFacts() + let afterManifest = builder.build( + processes: [process], + now: start.addingTimeInterval(2), + graceInterval: 2.5 + ) + + XCTAssertEqual(try XCTUnwrap(beforeManifest.classified.first).classification.kind, .other) + XCTAssertEqual(try XCTUnwrap(afterManifest.classified.first).classification.kind, .flutter) + XCTAssertEqual(probeCount, 3) + } + + func testUpdatesMetricsWithoutLosingClassificationOrLiveIdentity() throws { + var builder = ProcessSnapshotBuilder() + let start = Date(timeIntervalSince1970: 100) + let birthToken = ProcessBirthToken(seconds: 1_000, microseconds: 42) + + let first = builder.build( + processes: [process(cpu: 1, birthToken: birthToken)], + now: start, + graceInterval: 2.5 + ) + let second = builder.build( + processes: [process(cpu: 90, birthToken: birthToken)], + now: start.addingTimeInterval(2), + graceInterval: 2.5 + ) + + XCTAssertEqual(first.classified.first?.classification.kind, .javascript) + XCTAssertEqual(second.classified.first?.classification.kind, .javascript) + XCTAssertEqual(second.classified.first?.process.resourceUsage?.cpuPercent, 90) + XCTAssertEqual(second.classified.first?.process.birthToken, birthToken) + XCTAssertEqual(second.liveProcessIDs, Set([800])) + XCTAssertEqual(second.processes, second.classified.map(\.process)) + } + + func testRehydratesCachedWorkflowMetricsWithoutRebuildingStructure() throws { + var builder = ProcessSnapshotBuilder() + let start = Date(timeIntervalSince1970: 100) + let birthToken = ProcessBirthToken(seconds: 1_000, microseconds: 42) + + let first = builder.build( + processes: [process(cpu: 1, birthToken: birthToken)], + now: start, + graceInterval: 2.5 + ) + let metricTick = builder.build( + processes: [process(cpu: 90, birthToken: birthToken)], + now: start.addingTimeInterval(2), + graceInterval: 2.5 + ) + let workflowTick = builder.build( + processes: [process(cpu: 90, birthToken: birthToken)], + now: start.addingTimeInterval(10), + graceInterval: 2.5 + ) + + let firstWorkflow = try XCTUnwrap(first.workflows.first) + XCTAssertEqual(metricTick.classified.first?.process.resourceUsage?.cpuPercent, 90) + XCTAssertEqual(firstWorkflow.totalCPU, 1) + XCTAssertEqual(metricTick.workflows.first?.totalCPU, 90) + XCTAssertEqual(workflowTick.workflows.first?.totalCPU, 90) + } + + func testInvalidatingWorkspaceFactsRefreshesCachedWorkflowMetrics() throws { + var builder = ProcessSnapshotBuilder() + let start = Date(timeIntervalSince1970: 100) + let birthToken = ProcessBirthToken(seconds: 1_000, microseconds: 42) + + let first = builder.build( + processes: [process(cpu: 1, birthToken: birthToken)], + now: start, + graceInterval: 2.5 + ) + builder.invalidateWorkspaceFacts() + let afterInvalidation = builder.build( + processes: [process(cpu: 90, birthToken: birthToken)], + now: start.addingTimeInterval(2), + graceInterval: 2.5 + ) + + XCTAssertEqual(try XCTUnwrap(first.workflows.first).totalCPU, 1) + XCTAssertEqual(try XCTUnwrap(afterInvalidation.workflows.first).totalCPU, 90) + } + + private func process(cpu: Double, birthToken: ProcessBirthToken) -> DevProcess { + let workspace = NSHomeDirectory() + "/dev/devscope-fixture" + return DevProcess( + pid: 800, + parentPID: 1, + executable: "node", + command: "node \(workspace)/node_modules/.bin/vite", + currentDirectory: workspace, + resourceUsage: DevProcessResourceUsage( + cpuPercent: cpu, + residentMemoryBytes: 100, + elapsedTime: "00:10" + ), + birthToken: birthToken + ) + } +} diff --git a/Tests/DevScopeCoreTests/ProcessSnapshotStabilizerTests.swift b/Tests/DevScopeCoreTests/ProcessSnapshotStabilizerTests.swift new file mode 100644 index 0000000..1efd769 --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessSnapshotStabilizerTests.swift @@ -0,0 +1,99 @@ +import XCTest +@testable import DevScopeCore + +final class ProcessSnapshotStabilizerTests: XCTestCase { + func testRetainsRecentlyMissingProcessesToReduceRefreshChurn() { + var stabilizer = ClassifiedProcessSnapshotStabilizer() + let start = Date(timeIntervalSince1970: 1_000) + + let first = stabilizer.merge( + liveItems: [ + classified(pid: 10, displayName: "api"), + classified(pid: 20, displayName: "worker") + ], + now: start, + graceInterval: 2 + ) + + XCTAssertEqual(first.map(\.process.pid), [10, 20]) + XCTAssertEqual(stabilizer.liveProcessIDs, [10, 20]) + + let second = stabilizer.merge( + liveItems: [ + classified(pid: 20, displayName: "worker") + ], + now: start.addingTimeInterval(1), + graceInterval: 2 + ) + + XCTAssertEqual(second.map(\.process.pid), [10, 20]) + XCTAssertEqual(stabilizer.liveProcessIDs, [20]) + } + + func testDropsMissingProcessesAfterGraceInterval() { + var stabilizer = ClassifiedProcessSnapshotStabilizer() + let start = Date(timeIntervalSince1970: 1_000) + + _ = stabilizer.merge( + liveItems: [ + classified(pid: 10, displayName: "api"), + classified(pid: 20, displayName: "worker") + ], + now: start, + graceInterval: 2 + ) + + let result = stabilizer.merge( + liveItems: [ + classified(pid: 20, displayName: "worker") + ], + now: start.addingTimeInterval(3), + graceInterval: 2 + ) + + XCTAssertEqual(result.map(\.process.pid), [20]) + XCTAssertEqual(stabilizer.liveProcessIDs, [20]) + } + + func testPreservesFirstSeenOrderWhenNewProcessesArrive() { + var stabilizer = ClassifiedProcessSnapshotStabilizer() + let start = Date(timeIntervalSince1970: 1_000) + + _ = stabilizer.merge( + liveItems: [ + classified(pid: 20, displayName: "worker") + ], + now: start, + graceInterval: 2 + ) + + let result = stabilizer.merge( + liveItems: [ + classified(pid: 10, displayName: "api"), + classified(pid: 20, displayName: "worker") + ], + now: start.addingTimeInterval(1), + graceInterval: 2 + ) + + XCTAssertEqual(result.map(\.process.pid), [20, 10]) + } + + private func classified(pid: Int32, displayName: String) -> ClassifiedDevProcess { + ClassifiedDevProcess( + process: DevProcess( + pid: pid, + parentPID: 1, + executable: displayName, + command: displayName, + currentDirectory: "\(NSHomeDirectory())/dev/example/\(displayName)", + resourceUsage: DevProcessResourceUsage(cpuPercent: 0, residentMemoryBytes: 1, elapsedTime: "00:01") + ), + classification: DevProcessClassification( + kind: .javascript, + displayName: displayName, + projectHint: displayName + ) + ) + } +} diff --git a/Tests/DevScopeCoreTests/ProcessTreeTests.swift b/Tests/DevScopeCoreTests/ProcessTreeTests.swift new file mode 100644 index 0000000..e9aa85f --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessTreeTests.swift @@ -0,0 +1,29 @@ +import XCTest +@testable import DevScopeCore + +final class ProcessTreeTests: XCTestCase { + func testFindsAllDescendantsWithoutIncludingRootOrUnrelatedProcesses() { + let processes = [ + DevProcess(pid: 10, parentPID: 1, executable: "npm", command: "npm run dev"), + DevProcess(pid: 11, parentPID: 10, executable: "sh", command: "sh -c next dev"), + DevProcess(pid: 12, parentPID: 11, executable: "node", command: "node next dev"), + DevProcess(pid: 20, parentPID: 1, executable: "python", command: "python other.py") + ] + + let descendants = ProcessTree.descendants(of: 10, in: processes) + + XCTAssertEqual(descendants.map(\.pid).sorted(), [11, 12]) + } + + func testIgnoresCyclesInMalformedSnapshots() { + let processes = [ + DevProcess(pid: 10, parentPID: 12, executable: "npm", command: "npm run dev"), + DevProcess(pid: 11, parentPID: 10, executable: "sh", command: "sh -c next dev"), + DevProcess(pid: 12, parentPID: 11, executable: "node", command: "node next dev") + ] + + let descendants = ProcessTree.descendants(of: 10, in: processes) + + XCTAssertEqual(descendants.map(\.pid).sorted(), [11, 12]) + } +} diff --git a/Tests/DevScopeCoreTests/ProcessWorkflowCacheTests.swift b/Tests/DevScopeCoreTests/ProcessWorkflowCacheTests.swift new file mode 100644 index 0000000..23c3d0f --- /dev/null +++ b/Tests/DevScopeCoreTests/ProcessWorkflowCacheTests.swift @@ -0,0 +1,324 @@ +import XCTest +@testable import DevScopeCore + +final class ProcessWorkflowCacheTests: XCTestCase { + func testMetricOnlyUpdateRehydratesWorkflowsWithoutRebuilding() throws { + let buildCount = Counter() + var cache = ProcessWorkflowCache { items in + buildCount.value += 1 + return ProcessIntelligence.uncappedWorkflows(for: items) + } + let start = Date(timeIntervalSince1970: 100) + + let first = cache.workflows(for: [item(cpu: 1)], now: start) + let second = cache.workflows( + for: [item(cpu: 90, memory: 900, elapsedTime: "00:12")], + now: start.addingTimeInterval(2) + ) + + XCTAssertEqual(buildCount.value, 1) + XCTAssertEqual(try XCTUnwrap(first.first).totalCPU, 1) + XCTAssertEqual(try XCTUnwrap(second.first).totalCPU, 90) + XCTAssertEqual(try XCTUnwrap(second.first).totalMemoryBytes, 900) + } + + func testStableFingerprintReusesWorkflowsIndefinitely() throws { + let buildCount = Counter() + var cache = ProcessWorkflowCache { items in + buildCount.value += 1 + return ProcessIntelligence.uncappedWorkflows(for: items) + } + let start = Date(timeIntervalSince1970: 100) + + let first = cache.workflows(for: [item(cpu: 1)], now: start) + let afterTwoMinutes = cache.workflows( + for: [item(cpu: 90, memory: 5_000_000_000, elapsedTime: "00:20")], + now: start.addingTimeInterval(120) + ) + let muchLater = cache.workflows( + for: [item(cpu: 45, memory: 2_000_000_000, elapsedTime: "12:34")], + now: start.addingTimeInterval(10_000) + ) + + let firstWorkflow = try XCTUnwrap(first.first) + let afterTwoMinutesWorkflow = try XCTUnwrap(afterTwoMinutes.first) + let muchLaterWorkflow = try XCTUnwrap(muchLater.first) + XCTAssertEqual(buildCount.value, 1) + XCTAssertEqual(firstWorkflow.totalCPU, 1) + XCTAssertEqual(afterTwoMinutesWorkflow.totalCPU, 90) + XCTAssertEqual(afterTwoMinutesWorkflow.totalMemoryBytes, 5_000_000_000) + XCTAssertEqual(afterTwoMinutesWorkflow.risk, .heavy) + XCTAssertEqual(muchLaterWorkflow.totalCPU, 45) + XCTAssertEqual(muchLaterWorkflow.totalMemoryBytes, 2_000_000_000) + XCTAssertEqual(muchLaterWorkflow.risk, .busy) + } + + func testWorkflowRelevantIdentityAndClassificationChangesRecomputeImmediately() { + let baseline = item(cpu: 1) + let variants = [ + item(cpu: 1, pid: 801), + item(cpu: 1, birthToken: ProcessBirthToken(seconds: 1_001, microseconds: 43)), + item(cpu: 1, parentPID: 2), + item(cpu: 1, executable: "bun"), + item(cpu: 1, command: "node worker.js"), + item(cpu: 1, currentDirectory: "/tmp/other"), + item(cpu: 1, kind: .python), + item(cpu: 1, displayName: "worker.js"), + item(cpu: 1, projectHint: "other"), + item(cpu: 1, tags: [.api, .experiment]), + ] + + for variant in variants { + let buildCount = Counter() + var cache = ProcessWorkflowCache { items in + buildCount.value += 1 + return ProcessIntelligence.uncappedWorkflows(for: items) + } + let start = Date(timeIntervalSince1970: 100) + + _ = cache.workflows(for: [baseline], now: start) + _ = cache.workflows(for: [variant], now: start.addingTimeInterval(2)) + + XCTAssertEqual(buildCount.value, 2, "Expected an immediate rebuild for \(variant)") + } + } + + func testAddingAndRemovingAProcessRecomputesImmediately() { + let buildCount = Counter() + var cache = ProcessWorkflowCache { items in + buildCount.value += 1 + return ProcessIntelligence.uncappedWorkflows(for: items) + } + let start = Date(timeIntervalSince1970: 100) + let first = item(cpu: 1) + let second = item(cpu: 1, pid: 801) + + _ = cache.workflows(for: [first], now: start) + _ = cache.workflows(for: [first, second], now: start.addingTimeInterval(2)) + _ = cache.workflows(for: [first], now: start.addingTimeInterval(4)) + + XCTAssertEqual(buildCount.value, 3) + } + + func testIrrelevantMachineProcessChurnDoesNotRebuildWorkflows() { + let buildCount = Counter() + var cache = ProcessWorkflowCache { items in + buildCount.value += 1 + return ProcessIntelligence.uncappedWorkflows(for: items) + } + let start = Date(timeIntervalSince1970: 100) + let relevant = item(cpu: 1) + let firstSystemProcess = item( + cpu: 0, + pid: 900, + executable: "/usr/libexec/distnoted", + command: "/usr/libexec/distnoted agent", + currentDirectory: "/", + kind: .systemService, + displayName: "distnoted", + projectHint: nil, + tags: [] + ) + let replacementSystemProcess = item( + cpu: 0, + pid: 901, + executable: "/usr/libexec/distnoted", + command: "/usr/libexec/distnoted agent", + currentDirectory: "/", + kind: .systemService, + displayName: "distnoted", + projectHint: nil, + tags: [] + ) + + let first = cache.workflows(for: [relevant, firstSystemProcess], now: start) + let afterChurn = cache.workflows( + for: [relevant, replacementSystemProcess], + now: start.addingTimeInterval(2) + ) + + XCTAssertEqual(buildCount.value, 1) + XCTAssertEqual(afterChurn, first) + } + + func testProcessOrderChangeRecomputesImmediately() { + let buildCount = Counter() + var cache = ProcessWorkflowCache { items in + buildCount.value += 1 + return ProcessIntelligence.uncappedWorkflows(for: items) + } + let start = Date(timeIntervalSince1970: 100) + let first = item(cpu: 1) + let second = item(cpu: 1, pid: 801) + + _ = cache.workflows(for: [first, second], now: start) + _ = cache.workflows(for: [second, first], now: start.addingTimeInterval(2)) + + XCTAssertEqual(buildCount.value, 2) + } + + func testMetricRiskChangeReordersCachedWorkflowsWithoutRebuilding() { + let buildCount = Counter() + var cache = ProcessWorkflowCache { items in + buildCount.value += 1 + return ProcessIntelligence.uncappedWorkflows(for: items) + } + let start = Date(timeIntervalSince1970: 100) + let alpha = item( + cpu: 1, + currentDirectory: "\(NSHomeDirectory())/dev/example/alpha", + projectHint: "alpha", + tags: [] + ) + let beta = item( + cpu: 1, + pid: 801, + currentDirectory: "\(NSHomeDirectory())/dev/example/beta", + projectHint: "beta", + tags: [] + ) + let heavyBeta = item( + cpu: 90, + pid: 801, + currentDirectory: "\(NSHomeDirectory())/dev/example/beta", + projectHint: "beta", + tags: [] + ) + + let initial = cache.workflows(for: [alpha, beta], now: start) + let reordered = cache.workflows( + for: [alpha, heavyBeta], + now: start.addingTimeInterval(2) + ) + + XCTAssertEqual(initial.map(\.title), ["Alpha Web", "Beta Web"]) + XCTAssertEqual(reordered.map(\.title), ["Beta Web", "Alpha Web"]) + XCTAssertEqual(buildCount.value, 1) + } + + func testMetricRiskChangePromotesThirteenthCachedWorkflowWithoutRebuilding() throws { + let buildCount = Counter() + var cache = ProcessWorkflowCache { items in + buildCount.value += 1 + return ProcessIntelligence.uncappedWorkflows(for: items) + } + let start = Date(timeIntervalSince1970: 100) + let items = (0..<13).map { index in + item( + cpu: 1, + pid: Int32(1_000 + index), + currentDirectory: "\(NSHomeDirectory())/dev/example/project-\(index)", + projectHint: "project-\(index)", + tags: [] + ) + } + + let initial = cache.workflows(for: items, now: start) + let initialProcessIDs = Set(initial.flatMap(\.processIDs)) + let omittedPID = try XCTUnwrap( + items.map(\.process.pid).first { !initialProcessIDs.contains($0) } + ) + let updatedItems = (0..<13).map { index in + let pid = Int32(1_000 + index) + return item( + cpu: pid == omittedPID ? 90 : 1, + pid: pid, + currentDirectory: "\(NSHomeDirectory())/dev/example/project-\(index)", + projectHint: "project-\(index)", + tags: [] + ) + } + + let promoted = cache.workflows( + for: updatedItems, + now: start.addingTimeInterval(2) + ) + + XCTAssertEqual(initial.count, 12) + XCTAssertEqual(promoted.count, 12) + XCTAssertTrue(promoted.contains { $0.processIDs.contains(omittedPID) }) + XCTAssertEqual(buildCount.value, 1) + } + + func testStable850RowFingerprintPerformsNoSecondWorkflowBuild() { + let buildCount = Counter() + var cache = ProcessWorkflowCache { items in + buildCount.value += 1 + return ProcessIntelligence.uncappedWorkflows(for: items) + } + let items = (0..<850).map { index in + item( + cpu: 1, + pid: Int32(1_000 + index), + currentDirectory: "\(NSHomeDirectory())/dev/example/project-\(index)", + projectHint: "project-\(index)", + tags: [] + ) + } + let start = Date(timeIntervalSince1970: 100) + + let first = cache.workflows(for: items, now: start) + let cached = cache.workflows(for: items, now: start.addingTimeInterval(10_000)) + + XCTAssertEqual(buildCount.value, 1) + XCTAssertEqual(cached, first) + } + + func testExplicitInvalidationRecomputesImmediately() { + let buildCount = Counter() + var cache = ProcessWorkflowCache { items in + buildCount.value += 1 + return ProcessIntelligence.uncappedWorkflows(for: items) + } + let start = Date(timeIntervalSince1970: 100) + let process = item(cpu: 1) + + _ = cache.workflows(for: [process], now: start) + cache.invalidate() + _ = cache.workflows(for: [process], now: start.addingTimeInterval(2)) + + XCTAssertEqual(buildCount.value, 2) + } + + private func item( + cpu: Double, + memory: Int64 = 100, + elapsedTime: String = "00:10", + pid: Int32 = 800, + birthToken: ProcessBirthToken = ProcessBirthToken(seconds: 1_000, microseconds: 42), + parentPID: Int32 = 1, + executable: String = "node", + command: String = "node server.js", + currentDirectory: String = "/tmp/example", + kind: DevRuntimeKind = .javascript, + displayName: String = "server.js", + projectHint: String? = "example", + tags: [DevProcessTag] = [.api] + ) -> ClassifiedDevProcess { + ClassifiedDevProcess( + process: DevProcess( + pid: pid, + parentPID: parentPID, + executable: executable, + command: command, + currentDirectory: currentDirectory, + resourceUsage: DevProcessResourceUsage( + cpuPercent: cpu, + residentMemoryBytes: memory, + elapsedTime: elapsedTime + ), + birthToken: birthToken + ), + classification: DevProcessClassification( + kind: kind, + displayName: displayName, + projectHint: projectHint, + tags: tags + ) + ) + } +} + +private final class Counter: @unchecked Sendable { + var value = 0 +} diff --git a/Tests/DevScopeCoreTests/ReleaseScriptTests.swift b/Tests/DevScopeCoreTests/ReleaseScriptTests.swift new file mode 100644 index 0000000..d8fb04a --- /dev/null +++ b/Tests/DevScopeCoreTests/ReleaseScriptTests.swift @@ -0,0 +1,1851 @@ +import AppKit +import Foundation +import XCTest + +final class ReleaseScriptTests: XCTestCase { + func testMainInterfaceRetainsAutomationsAndTruthfulProcessScopes() throws { + let content = try appSource(at: "Sources/DevScope/Views/ContentView.swift") + let picker = try appSource(at: "Sources/DevScope/Views/ProcessScopePicker.swift") + let family = try appSource(at: "Sources/DevScope/Views/ApplicationFamilyListView.swift") + let hierarchy = try appSource(at: "Sources/DevScope/Views/ProcessHierarchyView.swift") + + XCTAssertTrue(content.contains("Text(\"Processes\").tag(DevScopeWorkspaceMode.processes)")) + XCTAssertTrue(content.contains("Text(\"Automations\").tag(DevScopeWorkspaceMode.automations)")) + XCTAssertTrue(content.contains("ProcessScopePicker(selection:")) + XCTAssertTrue(content.contains("ApplicationFamilyListView(")) + XCTAssertTrue(content.contains("ProcessHierarchyView(")) + XCTAssertTrue(picker.contains("ProcessActivityScope.allCases")) + XCTAssertTrue(family.contains("OS processes")) + XCTAssertTrue(hierarchy.contains("ProcessHierarchyNode")) + } + + func testAutomationWorkspaceUsesTheProcessProjectedInventoryWithItsLinks() throws { + let content = try appSource(at: "Sources/DevScope/Views/ContentView.swift") + let workspace = try appSource(at: "Sources/DevScope/Views/AutomationWorkspaceView.swift") + + XCTAssertTrue(content.contains("inventory: store.liveSnapshot.automationInventory")) + XCTAssertTrue(workspace.contains("AutomationPresentation.filtered(\n inventory.records,")) + XCTAssertFalse(workspace.contains("AutomationPresentation.filtered(\n store.snapshot.records,")) + } + + func testMainWorkspaceRetainsOnlyTheAutomationLayerAcrossWorkspaceSwitches() throws { + let content = try appSource(at: "Sources/DevScope/Views/ContentView.swift") + let sanitized = sanitizedSwiftSource(content) + let mainWorkspace = try sourceBlock(in: sanitized, startingWith: "private var mainWorkspace") + let workspaceLayers = try sourceBlock(in: mainWorkspace, startingWith: "ZStack") + let processConditional = try sourceBlock( + in: workspaceLayers, + startingWith: "if workspaceMode == .processes" + ) + let layerModifier = try sourceBlock(in: sanitized, startingWith: "func workspaceLayer") + let normalizedWorkspace = workspaceLayers.filter { !$0.isWhitespace } + let normalizedModifier = layerModifier.filter { !$0.isWhitespace } + let automationInitializer = try balancedRegion( + in: normalizedWorkspace, + startingWith: "AutomationWorkspaceView", + opening: "(", + closing: ")" + ) + let automationLayer = "AutomationWorkspaceView\(automationInitializer)" + + ".workspaceLayer(isActive:workspaceMode==.automations)" + + XCTAssertEqual(occurrenceCount(of: "ZStack", in: mainWorkspace), 1) + XCTAssertEqual(occurrenceCount(of: "processModeWorkspace", in: workspaceLayers), 1) + XCTAssertTrue(processConditional.filter { !$0.isWhitespace }.contains("processModeWorkspace")) + XCTAssertEqual( + topLevelOccurrenceCount(of: "AutomationWorkspaceView", in: workspaceLayers), + 1 + ) + XCTAssertEqual( + occurrenceCount(of: ".workspaceLayer(isActive:workspaceMode==.processes)", in: normalizedWorkspace), + 0 + ) + XCTAssertEqual(topLevelOccurrenceCount(of: automationLayer, in: normalizedWorkspace), 1) + XCTAssertEqual( + occurrenceCount( + of: ".workspaceLayer(isActive:workspaceMode==.automations)", + in: normalizedWorkspace + ), + 1 + ) + XCTAssertFalse(try containsRegex(#"\bswitch\b|\?"#, in: workspaceLayers)) + XCTAssertTrue(normalizedModifier.contains("opacity(isActive?1:0)")) + XCTAssertTrue(normalizedModifier.contains(".disabled(!isActive)")) + XCTAssertTrue(normalizedModifier.contains(".allowsHitTesting(isActive)")) + XCTAssertTrue(normalizedModifier.contains(".accessibilityHidden(!isActive)")) + } + + func testAutomationWorkspaceUsesAdaptiveNativePanesAndHorizontalContainment() throws { + let workspace = try appSource(at: "Sources/DevScope/Views/AutomationWorkspaceView.swift") + let table = try appSource(at: "Sources/DevScope/Views/AutomationTableView.swift") + let processTable = try appSource(at: "Sources/DevScope/Views/ProcessTableView.swift") + let workspaceCode = sanitizedSwiftSource(workspace) + let tableCode = sanitizedSwiftSource(table) + let processTableCode = sanitizedSwiftSource(processTable) + let geometryRegion = try sourceBlock(in: workspaceCode, startingWith: "GeometryReader") + let splitRegion = try sourceBlock(in: geometryRegion, startingWith: "HSplitView") + let horizontalScrollRegion = try sourceBlock( + in: tableCode, + startingWith: "ScrollView(.horizontal)" + ) + let normalizedGeometryCode = geometryRegion.filter { !$0.isWhitespace } + let normalizedSplitCode = splitRegion.filter { !$0.isWhitespace } + let normalizedHorizontalScrollCode = horizontalScrollRegion.filter { !$0.isWhitespace } + let processHorizontalScrollRegion = try sourceBlock( + in: processTableCode, + startingWith: "ScrollView(.horizontal)" + ) + let normalizedProcessHorizontalScrollCode = processHorizontalScrollRegion.filter { !$0.isWhitespace } + let paneFrameSignatures = [ + ".frame(minWidth:pane.railMinimum,idealWidth:pane.railPreferred)", + ".frame(minWidth:pane.tableMinimum,idealWidth:pane.tablePreferred)", + ".frame(minWidth:pane.detailMinimum,idealWidth:pane.detailPreferred)", + ] + let panePrioritySignatures = [ + ".layoutPriority(pane.tablePriority)", + ".layoutPriority(pane.detailPriority)", + ] + + XCTAssertTrue(normalizedGeometryCode.contains("AutomationWorkspaceLayoutPolicy.constraints")) + XCTAssertTrue(normalizedHorizontalScrollCode.contains("header")) + for scrollCode in [normalizedHorizontalScrollCode, normalizedProcessHorizontalScrollCode] { + XCTAssertTrue(scrollCode.contains(".scrollTargetLayout()")) + XCTAssertTrue(scrollCode.contains( + ".scrollPosition(id:$verticalScrollTargetID,anchor:.center)" + )) + XCTAssertFalse(scrollCode.contains("ScrollViewReader")) + XCTAssertFalse(scrollCode.contains(".focusable()")) + XCTAssertFalse(scrollCode.contains(".focused(")) + } + XCTAssertTrue(normalizedHorizontalScrollCode.contains("ScrollView(.vertical)")) + XCTAssertTrue(normalizedHorizontalScrollCode.contains( + "ForEach(Array(rows.enumerated()),id:\\.element.id)" + )) + XCTAssertTrue(normalizedHorizontalScrollCode.contains(".id(row.id)")) + for signature in paneFrameSignatures { + XCTAssertEqual(occurrenceCount(of: signature, in: normalizedSplitCode), 1) + } + XCTAssertEqual( + paneFrameSignatures.reduce(0) { + $0 + occurrenceCount(of: $1, in: normalizedSplitCode) + }, + 3 + ) + for signature in panePrioritySignatures { + XCTAssertEqual(occurrenceCount(of: signature, in: normalizedSplitCode), 1) + } + XCTAssertEqual( + panePrioritySignatures.reduce(0) { + $0 + occurrenceCount(of: $1, in: normalizedSplitCode) + }, + 2 + ) + XCTAssertFalse(normalizedSplitCode.contains("maxWidth:")) + XCTAssertFalse(try containsRegex( + #"idealWidth\s*:\s*[-+]?(?:\d+(?:\.\d*)?|\.\d+)"#, + in: splitRegion + )) + XCTAssertFalse(try containsRegex( + #"@State(?:\s*\([^)]*\))?\s+(?:private\s+)?var\s+\w*(?:divider|split(?:ter)?Position|pane(?:s)?Width|railWidth|tableWidth|detailWidth)\w*"#, + in: workspaceCode, + options: [.caseInsensitive] + )) + XCTAssertFalse(try containsRegex( + #"\.onChange\s*\(\s*of\s*:\s*(?:(?:\w+\.)*size\.width|proxy\.size(?!\.)|(?:\w+\.)*\w*(?:width|divider|split(?:ter)?position|paneposition)\w*)\s*\)"#, + in: workspaceCode, + options: [.caseInsensitive] + )) + XCTAssertFalse(try containsRegex( + #"\b(?:\w*(?:divider|split(?:ter)?position)\w*|(?:pane|rail|table|detail)\w*width\w*)\s*="#, + in: workspaceCode, + options: [.caseInsensitive] + )) + } + + func testProcessTerminationMenuKeepsForceActionsInOneNativeMenu() throws { + let source = try appSource(at: "Sources/DevScope/Views/ProcessCommandBar.swift") + + XCTAssertTrue(source.contains("Label(\"Force Kill Process…\", systemImage: \"bolt.fill\")")) + XCTAssertTrue(source.contains("Label(\"Force Kill Process Tree…\", systemImage: \"bolt.horizontal.fill\")")) + XCTAssertFalse(source.contains("Menu(\"Force Kill\")")) + } + + func testProcessTableCommandCopyUsesTheDurableRecoveryPath() throws { + let table = try appSource(at: "Sources/DevScope/Views/ProcessTableView.swift") + let content = try appSource(at: "Sources/DevScope/Views/ContentView.swift") + + XCTAssertTrue(table.contains("let copyCommand: (ClassifiedDevProcess) -> Void")) + XCTAssertTrue(table.contains("copyCommand(item)")) + XCTAssertFalse(table.contains("copy(item.process.command)")) + XCTAssertTrue(content.contains("copyCommand: copyCommand")) + } + + func testPartialMutationRecoveryLocationsOfferCopyAndFinderActions() throws { + let detail = try appSource(at: "Sources/DevScope/Views/AutomationDetailView.swift") + + XCTAssertTrue(detail.contains("Button(\"Reveal in Finder\")")) + XCTAssertTrue(detail.contains("Button(\"Copy Path\")")) + XCTAssertTrue(detail.contains("recoveryHandles")) + } + + func testSwiftSourceSanitizerPreservesStructureAroundLineCommentsAndEscapedStrings() { + let source = ###""" + GeometryReader { + let url = "https://example.com/{ignored}" + let escaped = "quote: \" }" + // HSplitView { decoy } + HSplitView { pane } + } + """### + + let sanitized = sanitizedSwiftSource(source) + + XCTAssertEqual(sanitized.count, source.count) + XCTAssertEqual( + sanitized.enumerated().compactMap { $0.element.isNewline ? $0.offset : nil }, + source.enumerated().compactMap { $0.element.isNewline ? $0.offset : nil } + ) + XCTAssertFalse(sanitized.contains("https://example.com/{ignored}")) + XCTAssertFalse(sanitized.contains("HSplitView { decoy }")) + XCTAssertTrue(sanitized.contains("HSplitView { pane }")) + XCTAssertEqual(sanitized.filter { $0 == "{" }.count, 2) + XCTAssertEqual(sanitized.filter { $0 == "}" }.count, 2) + } + + func testSwiftSourceSanitizerIgnoresNestedCommentsAndMultilineRawStrings() { + let source = ####""" + Root { + /* outer { HSplitView { decoy } /* nested { } */ still ignored } */ + let multiline = """ + HSplitView { multilineDecoy } + /* string content is not a comment */ + """ + let raw = ##"ScrollView(.horizontal) { rawDecoy } \##" escapedRawQuote { }"## + let rawMultiline = #""" + GeometryReader { rawMultilineDecoy } + """# + HSplitView { live } + } + """#### + + let sanitized = sanitizedSwiftSource(source) + + XCTAssertEqual(sanitized.count, source.count) + XCTAssertFalse(sanitized.contains("decoy")) + XCTAssertFalse(sanitized.contains("multilineDecoy")) + XCTAssertFalse(sanitized.contains("rawDecoy")) + XCTAssertFalse(sanitized.contains("rawMultilineDecoy")) + XCTAssertTrue(sanitized.contains("HSplitView { live }")) + XCTAssertEqual(sanitized.filter { $0 == "{" }.count, 2) + XCTAssertEqual(sanitized.filter { $0 == "}" }.count, 2) + } + + func testSwiftSourceSanitizerIgnoresNormalAndMultilineNestedInterpolationStrings() throws { + let source = ####""" + Root { + let value = "prefix \(flag + ? "HSplitView { nestedMarker /* unmatched-looking }" + : "ScrollView(.horizontal) { pane.tableMinimum }") suffix" + let multiline = """ + prefix \(flag + ? """ + ScrollView(.horizontal) { multilineNestedMarker /* unmatched-looking } */ + """ + : "pane.detailPreferred { multilineFallbackMarker }") suffix + """ + HSplitView { live } + } + """#### + + let sanitized = sanitizedSwiftSource(source) + let block = try sanitizedSourceBlock(in: source, startingWith: "Root") + + XCTAssertEqual(sanitized.count, source.count) + XCTAssertEqual( + sanitized.enumerated().compactMap { $0.element.isNewline ? $0.offset : nil }, + source.enumerated().compactMap { $0.element.isNewline ? $0.offset : nil } + ) + XCTAssertFalse(sanitized.contains("nestedMarker")) + XCTAssertFalse(sanitized.contains("multilineNestedMarker")) + XCTAssertFalse(sanitized.contains("multilineFallbackMarker")) + XCTAssertFalse(sanitized.contains("ScrollView(.horizontal)")) + XCTAssertFalse(sanitized.contains("pane.tableMinimum")) + XCTAssertFalse(sanitized.contains("pane.detailPreferred")) + XCTAssertTrue(sanitized.contains("HSplitView { live }")) + XCTAssertEqual(sanitized.filter { $0 == "{" }.count, 2) + XCTAssertEqual(sanitized.filter { $0 == "}" }.count, 2) + XCTAssertTrue(block.contains("HSplitView { live }")) + XCTAssertEqual(block.filter { $0 == "{" }.count, 2) + XCTAssertEqual(block.filter { $0 == "}" }.count, 2) + } + + func testSwiftSourceSanitizerIgnoresRawHashNestedInterpolationsAndComments() throws { + let source = ######""" + Root { + let value = ###""" + prefix \###( + flag + ? ###""" + HSplitView { rawNestedMarker /* unmatched-looking } */ + \###("ScrollView(.horizontal) { nestedInterpolationMarker }") + """### + : { + // ScrollView(.horizontal) { lineCommentMarker } + /* pane.detailPreferred { outer /* HSplitView { nestedCommentMarker } */ } */ + return "pane.tableMinimum { ordinaryNestedMarker }" + }() + ) suffix + """### + HSplitView { live } + } + """###### + + let sanitized = sanitizedSwiftSource(source) + let block = try sanitizedSourceBlock(in: source, startingWith: "Root") + + XCTAssertEqual(sanitized.count, source.count) + XCTAssertEqual( + sanitized.enumerated().compactMap { $0.element.isNewline ? $0.offset : nil }, + source.enumerated().compactMap { $0.element.isNewline ? $0.offset : nil } + ) + for leakedMarker in [ + "rawNestedMarker", + "nestedInterpolationMarker", + "lineCommentMarker", + "nestedCommentMarker", + "pane.detailPreferred", + "pane.tableMinimum", + "ordinaryNestedMarker", + "ScrollView(.horizontal)", + ] { + XCTAssertFalse(sanitized.contains(leakedMarker), "Leaked \(leakedMarker)") + } + XCTAssertTrue(sanitized.contains("HSplitView { live }")) + XCTAssertEqual(sanitized.filter { $0 == "{" }.count, 2) + XCTAssertEqual(sanitized.filter { $0 == "}" }.count, 2) + XCTAssertTrue(block.contains("HSplitView { live }")) + XCTAssertEqual(block.filter { $0 == "{" }.count, 2) + XCTAssertEqual(block.filter { $0 == "}" }.count, 2) + } + + func testSanitizedSourceBlockFindsLiveMarkerAfterCommentAndStringDecoys() throws { + let source = ####""" + // GeometryReader { commentedLineDecoy } + let text = "GeometryReader { stringDecoy }" + /* GeometryReader { commentedBlockDecoy /* nested { } */ } */ + GeometryReader { + let closingBraceLiteral = "} stringInsideDecoy" + HSplitView { live } + } + trailing { unrelated } + """#### + + let block = try sanitizedSourceBlock(in: source, startingWith: "GeometryReader") + + XCTAssertTrue(block.contains("HSplitView { live }")) + XCTAssertFalse(block.contains("commentedLineDecoy")) + XCTAssertFalse(block.contains("commentedBlockDecoy")) + XCTAssertFalse(block.contains("stringInsideDecoy")) + XCTAssertFalse(block.contains("trailing")) + XCTAssertEqual(block.filter { $0 == "{" }.count, 2) + XCTAssertEqual(block.filter { $0 == "}" }.count, 2) + } + + func testBalancedRegionIgnoresSanitizedDelimiterDecoysAndStopsBeforeTrailingOverride() throws { + let source = ####""" + static func make() { + let sources: [any AutomationSource] = [ + /* ] BackgroundTaskAutomationSource(diagnosticPolicy: .available) */ + "string ] ) BackgroundTaskAutomationSource(diagnosticPolicy: .available)", + BackgroundTaskAutomationSource( + runner: runner, + metadata: nestedCall(values: [one, two]) + ), + ] + BackgroundTaskAutomationSource(runner: runner, diagnosticPolicy: .available) + } + """#### + let sanitized = sanitizedSwiftSource(source) + let make = try sourceBlock(in: sanitized, startingWith: "static func make()") + let sources = try balancedRegion( + in: make, + startingWith: "let sources: [any AutomationSource] =", + opening: "[", + closing: "]" + ) + let initializer = try balancedRegion( + in: sources, + startingWith: "BackgroundTaskAutomationSource", + opening: "(", + closing: ")" + ) + + XCTAssertEqual(occurrenceCount(of: "BackgroundTaskAutomationSource", in: sources), 1) + XCTAssertTrue(initializer.contains("nestedCall(values: [one, two])")) + XCTAssertFalse(sources.contains("diagnosticPolicy: .available")) + } + + func testAutomationSourceCompositionAcceptsValidCurrentArray() throws { + let source = """ + [ + LaunchdAutomationSource( + fileSystem: fileSystem + ), + BackgroundTaskAutomationSource( + runner: runner, + diagnosticPolicy: .currentSystem + ), + LegacyLoginItemAutomationSource(adapter: adapter, currentUID: 501), + CronAutomationSource(commandRunner: runner), + ] + """ + + XCTAssertTrue(try isSafeAutomationSourceComposition(sanitizedSwiftSource(source))) + let omittedPolicy = source.replacingOccurrences( + of: ",\n diagnosticPolicy: .currentSystem", + with: "" + ) + XCTAssertTrue(try isSafeAutomationSourceComposition(sanitizedSwiftSource(omittedPolicy))) + } + + func testAutomationSourceCompositionRejectsTernaryNestedBackgroundConstructor() throws { + let source = """ + [ + LaunchdAutomationSource(fileSystem: fileSystem), + useDiagnostic + ? BackgroundTaskAutomationSource(runner: runner) + : backgroundSource, + LegacyLoginItemAutomationSource(adapter: adapter, currentUID: 501), + CronAutomationSource(commandRunner: runner), + ] + """ + + XCTAssertFalse(try isSafeAutomationSourceComposition(sanitizedSwiftSource(source))) + } + + func testAutomationSourceCompositionRejectsClosureAndWrapperNestedBackgroundConstructors() throws { + let source = """ + [ + LaunchdAutomationSource(fileSystem: fileSystem), + makeSource { BackgroundTaskAutomationSource(runner: runner) }, + LegacyLoginItemAutomationSource(adapter: adapter, currentUID: 501), + CronAutomationSource(commandRunner: runner), + ] + """ + + XCTAssertFalse(try isSafeAutomationSourceComposition(sanitizedSwiftSource(source))) + + let wrapped = source.replacingOccurrences( + of: "makeSource { BackgroundTaskAutomationSource(runner: runner) }", + with: "wrapper(BackgroundTaskAutomationSource(runner: runner))" + ) + XCTAssertFalse(try isSafeAutomationSourceComposition(sanitizedSwiftSource(wrapped))) + } + + func testAutomationSourceCompositionRejectsParenthesizedAlias() throws { + let source = """ + [ + LaunchdAutomationSource(fileSystem: fileSystem), + (backgroundSource), + LegacyLoginItemAutomationSource(adapter: adapter, currentUID: 501), + CronAutomationSource(commandRunner: runner), + ] + """ + + XCTAssertFalse(try isSafeAutomationSourceComposition(sanitizedSwiftSource(source))) + } + + func testAutomationSourceCompositionRejectsFunctionCallAlias() throws { + let source = """ + [ + LaunchdAutomationSource(fileSystem: fileSystem), + unsafeBackgroundSource(), + LegacyLoginItemAutomationSource(adapter: adapter, currentUID: 501), + CronAutomationSource(commandRunner: runner), + ] + """ + + XCTAssertFalse(try isSafeAutomationSourceComposition(sanitizedSwiftSource(source))) + } + + func testAutomationSourceCompositionRejectsExtraFifthElement() throws { + let source = """ + [ + LaunchdAutomationSource(fileSystem: fileSystem), + BackgroundTaskAutomationSource(runner: runner), + LegacyLoginItemAutomationSource(adapter: adapter, currentUID: 501), + CronAutomationSource(commandRunner: runner), + unsafeBackgroundSource(), + ] + """ + + XCTAssertFalse(try isSafeAutomationSourceComposition(sanitizedSwiftSource(source))) + } + + func testAutomationSourceCompositionRejectsAvailableAndIgnoresLexicalDecoys() throws { + let source = ####""" + [ + /* BackgroundTaskAutomationSource(runner: runner) */ + "BackgroundTaskAutomationSource(runner: runner)", + LaunchdAutomationSource(fileSystem: fileSystem), + BackgroundTaskAutomationSource( + runner: runner, + diagnosticPolicy: .available + ), + LegacyLoginItemAutomationSource(adapter: adapter, currentUID: 501), + CronAutomationSource(commandRunner: runner), + ] + """#### + + XCTAssertFalse(try isSafeAutomationSourceComposition(sanitizedSwiftSource(source))) + } + + func testTopLevelOccurrenceCountRejectsNestedClosureAndArgumentDecoys() throws { + let source = """ + ZStack { + // processModeWorkspace + processModeWorkspace + Group { processModeWorkspace } + wrapper(AutomationWorkspaceView()) + [AutomationWorkspaceView()] + AutomationWorkspaceView() + } + """ + let sanitized = sanitizedSwiftSource(source) + let block = try sourceBlock(in: sanitized, startingWith: "ZStack") + + XCTAssertEqual(topLevelOccurrenceCount(of: "processModeWorkspace", in: block), 1) + XCTAssertEqual(topLevelOccurrenceCount(of: "AutomationWorkspaceView", in: block), 1) + } + + func testAutomationCompositionCreatesBothTransactionAndRecoveryRoots() throws { + let source = try appSource(at: "Sources/DevScope/App/AutomationComposition.swift") + + XCTAssertTrue(source.contains( + "try? fileSystem.createDirectory(transactionRoot, permissions: 0o700)" + )) + XCTAssertTrue(source.contains( + "try? fileSystem.createDirectory(recoveryRoot, permissions: 0o700)" + )) + } + + func testAutomationCompositionUsesSafeCurrentSystemBackgroundDiagnosticPolicy() throws { + let source = try appSource(at: "Sources/DevScope/App/AutomationComposition.swift") + let sanitized = sanitizedSwiftSource(source) + let make = try sourceBlock(in: sanitized, startingWith: "static func make()") + let sources = try balancedRegion( + in: make, + startingWith: "let sources: [any AutomationSource] =", + opening: "[", + closing: "]" + ) + XCTAssertTrue(try isSafeAutomationSourceComposition(sources)) + } + + func testAccessSettingsExposeExactManualFullDiskAccessWorkflow() throws { + let repositoryRoot = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + let source = try String( + contentsOf: repositoryRoot.appendingPathComponent( + "Sources/DevScope/Views/SettingsView.swift"), + encoding: .utf8 + ) + + XCTAssertTrue(source.contains("FullDiskAccessGuidance(")) + XCTAssertTrue(source.contains("Bundle.main.bundleURL")) + XCTAssertTrue(source.contains("Reveal DevScope in Finder")) + XCTAssertTrue(source.contains("activateFileViewerSelecting([Bundle.main.bundleURL])")) + XCTAssertTrue(source.contains("Array(fullDiskAccessGuidance.steps.enumerated())")) + XCTAssertTrue(source.contains("Text(\"\\(index + 1).\")")) + XCTAssertTrue(source.contains("FullDiskAccessSettingsRoute.open(")) + XCTAssertTrue(source.contains("using: NSWorkspace.shared.open")) + XCTAssertTrue(source.contains("fallback: openPrivacyAndSecurity")) + XCTAssertTrue(source.contains("if !fullDiskAccessGuidance.isSandboxed")) + XCTAssertFalse(source.contains("return [.fullDiskAccess, .privacySecurity]")) + XCTAssertTrue(source.contains("if shouldShowFullDiskAccessGuidance")) + XCTAssertTrue(source.contains("ProcessAccessStatus.isSandboxed")) + XCTAssertTrue(source.contains("neededAccessActions.contains(.fullDiskAccess)")) + let accessPanel = try XCTUnwrap(source.range(of: "SettingsPanel(title: \"Process Access\")")) + let accessPrefix = source[..$BUNDLE_ID")) + XCTAssertFalse(source.contains("cat >\"$INFO_PLIST\" <\"$INFO_PLIST\" </dev/null"#)) + } + + func testTransientActionFeedbackRequestsAnAccessibilityAnnouncement() throws { + let repositoryRoot = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + let source = try String( + contentsOf: repositoryRoot.appendingPathComponent("Sources/DevScope/Views/ContentView.swift"), + encoding: .utf8 + ) + + XCTAssertTrue(source.contains("notification: .announcementRequested")) + XCTAssertTrue(source.contains(".announcement: announcement")) + } + + func testSettingsTabsProvideScrollableContent() throws { + let repositoryRoot = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + let source = try String( + contentsOf: repositoryRoot.appendingPathComponent( + "Sources/DevScope/Views/SettingsView.swift"), + encoding: .utf8 + ) + let scrollViewCount = source.components(separatedBy: "ScrollView {").count - 1 + + XCTAssertGreaterThanOrEqual(scrollViewCount, 3) + } + + func testAppUsesSingleWindowSceneToAvoidDuplicateProcessScanners() throws { + let repositoryRoot = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + let appSource = try String( + contentsOf: repositoryRoot.appendingPathComponent("Sources/DevScope/App/DevScopeApp.swift"), + encoding: .utf8 + ) + + XCTAssertFalse(appSource.contains("WindowGroup(")) + XCTAssertTrue(appSource.contains("Window(\"DevScope\", id: \"main\")")) + } + + func testGeneratedAppIconRepresentationsMatchFilenameDimensions() throws { + let repositoryRoot = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + let outputDirectory = FileManager.default.temporaryDirectory + .appendingPathComponent(UUID().uuidString, isDirectory: true) + defer { try? FileManager.default.removeItem(at: outputDirectory) } + + let process = Process() + let output = Pipe() + process.executableURL = URL(fileURLWithPath: "/usr/bin/xcrun") + process.arguments = [ + "swift", + repositoryRoot.appendingPathComponent("script/generate_app_icon.swift").path, + outputDirectory.path, + ] + process.standardOutput = output + process.standardError = output + try process.run() + let capturedOutput = output.fileHandleForReading.readDataToEndOfFile() + process.waitUntilExit() + XCTAssertEqual( + process.terminationStatus, + 0, + String(decoding: capturedOutput, as: UTF8.self) + ) + + let expectedDimensions = [ + "icon_16x16.png": 16, + "icon_16x16@2x.png": 32, + "icon_32x32.png": 32, + "icon_32x32@2x.png": 64, + "icon_128x128.png": 128, + "icon_128x128@2x.png": 256, + "icon_256x256.png": 256, + "icon_256x256@2x.png": 512, + "icon_512x512.png": 512, + "icon_512x512@2x.png": 1024, + ] + + for (filename, expectedPixels) in expectedDimensions { + let data = try Data(contentsOf: outputDirectory.appendingPathComponent(filename)) + let representation = try XCTUnwrap(NSBitmapImageRep(data: data), filename) + XCTAssertEqual(representation.pixelsWide, expectedPixels, filename) + XCTAssertEqual(representation.pixelsHigh, expectedPixels, filename) + } + } + + func testReleaseScriptsReferenceTrackedEntitlementsDirectoryWithExactCase() throws { + let repositoryRoot = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + let scriptPaths = [ + "script/build_release_bundle.sh", + "script/install_local_full_build.sh", + "script/package_developer_id.sh", + ] + + for scriptPath in scriptPaths { + let contents = try String( + contentsOf: repositoryRoot.appendingPathComponent(scriptPath), + encoding: .utf8 + ) + XCTAssertFalse( + contents.contains("$ROOT_DIR/Config/"), + "\(scriptPath) must use the tracked lowercase config directory" + ) + XCTAssertTrue( + contents.contains("$ROOT_DIR/config/"), + "\(scriptPath) must reference the tracked entitlement path" + ) + } + } + + func testNonSandboxValidationRejectsSandboxedBundle() throws { + let temporaryRoot = FileManager.default.temporaryDirectory + .appendingPathComponent(UUID().uuidString, isDirectory: true) + defer { try? FileManager.default.removeItem(at: temporaryRoot) } + + let appBundle = try makeSignedTestBundle(at: temporaryRoot, sandboxed: true) + let result = try runValidation(for: appBundle, requiresSandbox: false) + + XCTAssertNotEqual(result.status, 0, result.output) + XCTAssertTrue(result.output.contains("App Sandbox entitlement must be absent"), result.output) + } + + func testSandboxValidationRejectsFalseSandboxValueBesideTrueEntitlement() throws { + let temporaryRoot = FileManager.default.temporaryDirectory + .appendingPathComponent(UUID().uuidString, isDirectory: true) + defer { try? FileManager.default.removeItem(at: temporaryRoot) } + + let appBundle = try makeSignedTestBundle( + at: temporaryRoot, + entitlementBody: """ + com.apple.security.app-sandbox + com.apple.security.network.client + """ + ) + let result = try runValidation(for: appBundle, requiresSandbox: true) + + XCTAssertNotEqual(result.status, 0, result.output) + XCTAssertTrue(result.output.contains("App Sandbox entitlement is not true"), result.output) + + let source = try appSource(at: "script/validate_release_bundle.sh") + XCTAssertTrue(source.contains("plutil -extract 'com\\.apple\\.security\\.app-sandbox' raw")) + XCTAssertFalse(source.contains("grep -A3 \"com.apple.security.app-sandbox\"")) + } + + func testSandboxValidationRejectsMissingUserSelectedReadWriteEntitlement() throws { + let temporaryRoot = FileManager.default.temporaryDirectory + .appendingPathComponent(UUID().uuidString, isDirectory: true) + defer { try? FileManager.default.removeItem(at: temporaryRoot) } + + let appBundle = try makeSignedTestBundle( + at: temporaryRoot, + entitlementBody: """ + com.apple.security.app-sandbox + """ + ) + let result = try runValidation(for: appBundle, requiresSandbox: true) + + XCTAssertNotEqual(result.status, 0, result.output) + XCTAssertTrue( + result.output.contains("user-selected read-write entitlement missing"), + result.output + ) + } + + func testAppStorePackagingPinsSandboxEntitlementsAndValidation() throws { + let source = try appSource(at: "script/package_app_store.sh") + + XCTAssertTrue(source.contains("security cms -D -i")) + XCTAssertTrue(source.contains("ApplicationIdentifierPrefix.0")) + XCTAssertTrue(source.contains("$PROFILE_PREFIX.$DEVSCOPE_BUNDLE_ID")) + XCTAssertFalse(source.contains("$PROFILE_TEAM.$DEVSCOPE_BUNDLE_ID")) + XCTAssertTrue(source.contains("com.apple.application-identifier")) + XCTAssertTrue(source.contains("com.apple.developer.team-identifier")) + XCTAssertTrue(source.contains("DEVSCOPE_ENTITLEMENTS=\"$APP_STORE_ENTITLEMENTS\"")) + XCTAssertTrue(source.contains("DEVSCOPE_REQUIRE_SANDBOX=1")) + } + + func testReleaseWorkflowPinsThirdPartyActionsByCommit() throws { + let source = try appSource(at: ".github/workflows/release-gates.yml") + + XCTAssertTrue( + source.contains("actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd") + ) + XCTAssertFalse(source.contains("actions/checkout@v6")) + } + + func testOpenSourceReadinessCheckerRejectsRepositoryWithoutLicense() throws { + let temporaryRoot = try makeOpenSourceReadinessFixture() + defer { try? FileManager.default.removeItem(at: temporaryRoot) } + try FileManager.default.removeItem(at: temporaryRoot.appendingPathComponent("LICENSE")) + + let result = try runOpenSourceReadinessChecker(root: temporaryRoot) + + XCTAssertNotEqual(result.status, 0, result.output) + XCTAssertTrue(result.output.contains("LICENSE"), result.output) + } + + func testOpenSourceReadinessCheckerRejectsMissingPublicIdentity() throws { + let temporaryRoot = try makeOpenSourceReadinessFixture() + defer { try? FileManager.default.removeItem(at: temporaryRoot) } + try Data("DevScope\nCopyright 2026 unknown\n".utf8).write( + to: temporaryRoot.appendingPathComponent("NOTICE") + ) + + let result = try runOpenSourceReadinessChecker(root: temporaryRoot) + + XCTAssertNotEqual(result.status, 0, result.output) + XCTAssertTrue(result.output.contains("public identity"), result.output) + } + + func testOpenSourceReadinessCheckerRejectsInternalPublicationMaterial() throws { + let temporaryRoot = try makeOpenSourceReadinessFixture() + defer { try? FileManager.default.removeItem(at: temporaryRoot) } + let internalDirectory = temporaryRoot.appendingPathComponent( + "docs/monetization", + isDirectory: true + ) + try FileManager.default.createDirectory( + at: internalDirectory, + withIntermediateDirectories: true + ) + try Data("private economics".utf8).write( + to: internalDirectory.appendingPathComponent("strategy.md") + ) + + let result = try runOpenSourceReadinessChecker(root: temporaryRoot) + + XCTAssertNotEqual(result.status, 0, result.output) + XCTAssertTrue(result.output.contains("internal publication material"), result.output) + XCTAssertTrue(result.output.contains("docs/monetization"), result.output) + } + + func testOpenSourceReadinessCheckerRejectsUnpinnedGitHubAction() throws { + let temporaryRoot = try makeOpenSourceReadinessFixture() + defer { try? FileManager.default.removeItem(at: temporaryRoot) } + let workflows = temporaryRoot.appendingPathComponent( + ".github/workflows", isDirectory: true + ) + try Data( + """ + name: Unsafe fixture + on: [push] + permissions: + contents: read + jobs: + fixture: + runs-on: macos-latest + steps: + - uses: actions/checkout@v6 + """.utf8 + ).write(to: workflows.appendingPathComponent("unpinned-fixture.yml")) + + let result = try runOpenSourceReadinessChecker(root: temporaryRoot) + + XCTAssertNotEqual(result.status, 0, result.output) + XCTAssertTrue(result.output.contains("not pinned to a full commit SHA"), result.output) + XCTAssertTrue(result.output.contains("actions/checkout@v6"), result.output) + } + + func testOpenSourceReadinessCheckerAcceptsCurrentPublicSurface() throws { + let temporaryRoot = try makeOpenSourceReadinessFixture() + defer { try? FileManager.default.removeItem(at: temporaryRoot) } + + let result = try runOpenSourceReadinessChecker(root: temporaryRoot) + + XCTAssertEqual(result.status, 0, result.output) + XCTAssertTrue(result.output.contains("Open-source readiness check passed."), result.output) + } + + func testOpenSourceReadinessCheckerRejectsWorkflowWriteAllPermission() throws { + let temporaryRoot = try makeOpenSourceReadinessFixture() + defer { try? FileManager.default.removeItem(at: temporaryRoot) } + let workflow = temporaryRoot.appendingPathComponent( + ".github/workflows/release-gates.yml" + ) + try Data( + """ + name: Unsafe permissions fixture + on: [push] + permissions: write-all + jobs: + fixture: + runs-on: macos-latest + steps: [] + """.utf8 + ).write(to: workflow) + + let result = try runOpenSourceReadinessChecker(root: temporaryRoot) + + XCTAssertNotEqual(result.status, 0, result.output) + XCTAssertTrue(result.output.contains("permission"), result.output) + } + + func testOpenSourceReadinessCheckerRejectsJobWritePermissionOverride() throws { + let temporaryRoot = try makeOpenSourceReadinessFixture() + defer { try? FileManager.default.removeItem(at: temporaryRoot) } + let workflow = temporaryRoot.appendingPathComponent( + ".github/workflows/release-gates.yml" + ) + try Data( + """ + name: Unsafe job permissions fixture + on: [push] + permissions: + contents: read + jobs: + fixture: + permissions: + contents: write + runs-on: macos-latest + steps: [] + """.utf8 + ).write(to: workflow) + + let result = try runOpenSourceReadinessChecker(root: temporaryRoot) + + XCTAssertNotEqual(result.status, 0, result.output) + XCTAssertTrue(result.output.contains("unexpected write permission"), result.output) + XCTAssertTrue(result.output.contains("job fixture"), result.output) + } + + func testProvisioningProfileValidationChecksReleaseSemantics() throws { + let source = try appSource(at: "script/validate_release_bundle.sh") + + XCTAssertTrue(source.contains("security cms -D -i")) + XCTAssertTrue(source.contains("ExpirationDate")) + XCTAssertTrue(source.contains("application-identifier")) + XCTAssertTrue(source.contains("ApplicationIdentifierPrefix.0")) + XCTAssertTrue(source.contains("$PROFILE_PREFIX.$BUNDLE_ID")) + XCTAssertFalse(source.contains("$PROFILE_TEAM.$BUNDLE_ID")) + XCTAssertTrue(source.contains("Entitlements.com\\.apple\\.application-identifier")) + XCTAssertTrue(source.contains("TeamIdentifier")) + XCTAssertTrue(source.contains("DeveloperCertificates")) + XCTAssertTrue(source.contains("--extract-certificates")) + XCTAssertTrue(source.contains("profile App Sandbox entitlement is not true")) + } + + func testDeveloperIDPackagingRequiresNotarizationUnlessExplicitlyOverridden() throws { + let source = try appSource(at: "script/package_developer_id.sh") + + XCTAssertTrue(source.contains("DEVSCOPE_ALLOW_UNNOTARIZED")) + XCTAssertTrue(source.contains("notary profile is required for a release artifact")) + XCTAssertEqual(source.components(separatedBy: "--norsrc --noextattr").count - 1, 2) + } + + func testCommunityPreviewPackagingIsExplicitlyUnnotarizedAndChecksummed() throws { + let source = try appSource(at: "script/package_community_preview.sh") + + XCTAssertTrue(source.contains("DEVSCOPE_ACKNOWLEDGE_UNNOTARIZED_PREVIEW")) + XCTAssertTrue(source.contains("UNNOTARIZED COMMUNITY PREVIEW")) + XCTAssertTrue(source.contains("Do not disable Gatekeeper")) + XCTAssertTrue(source.contains("DEVSCOPE_SIGN_IDENTITY=\"-\"")) + XCTAssertTrue(source.contains("DEVSCOPE_ENTITLEMENTS=\"$ROOT_DIR/config/DevScopeDeveloperID.entitlements\"")) + XCTAssertTrue(source.contains("DEVSCOPE_REQUIRE_SANDBOX=0")) + XCTAssertTrue(source.contains("DEVSCOPE_REQUIRE_APPLE_EVENTS=1")) + XCTAssertTrue(source.contains("--norsrc --noextattr")) + XCTAssertTrue(source.contains("shasum -a 256")) + XCTAssertTrue(source.contains("zipinfo -1")) + } + + func testValidationRejectsBundleMissingRequiredArchitecture() throws { + let temporaryRoot = FileManager.default.temporaryDirectory + .appendingPathComponent(UUID().uuidString, isDirectory: true) + defer { try? FileManager.default.removeItem(at: temporaryRoot) } + + let appBundle = try makeSignedTestBundle(at: temporaryRoot, sandboxed: false) + let result = try runValidation( + for: appBundle, + requiresSandbox: false, + requiredArchitectures: "not-a-real-architecture" + ) + + XCTAssertNotEqual(result.status, 0, result.output) + XCTAssertTrue(result.output.contains("required architecture missing"), result.output) + } + + func testReleaseValidationRejectsMissingPublicLicenseNotice() throws { + let temporaryRoot = FileManager.default.temporaryDirectory + .appendingPathComponent(UUID().uuidString, isDirectory: true) + defer { try? FileManager.default.removeItem(at: temporaryRoot) } + + let appBundle = try makeSignedTestBundle(at: temporaryRoot, sandboxed: false) + try FileManager.default.removeItem( + at: appBundle.appendingPathComponent("Contents/Resources/LICENSE") + ) + + let result = try runValidation(for: appBundle, requiresSandbox: false) + + XCTAssertNotEqual(result.status, 0, result.output) + XCTAssertTrue(result.output.contains("LICENSE missing"), result.output) + } + + func testDeveloperIDPackageRebuildsArchiveAfterStapling() throws { + let fileManager = FileManager.default + let repositoryRoot = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + let temporaryRoot = fileManager.temporaryDirectory + .appendingPathComponent(UUID().uuidString, isDirectory: true) + defer { try? fileManager.removeItem(at: temporaryRoot) } + + let scriptDirectory = temporaryRoot.appendingPathComponent("script", isDirectory: true) + let fakeBinDirectory = temporaryRoot.appendingPathComponent("bin", isDirectory: true) + let distDirectory = temporaryRoot.appendingPathComponent("dist", isDirectory: true) + try fileManager.createDirectory(at: scriptDirectory, withIntermediateDirectories: true) + try fileManager.createDirectory(at: fakeBinDirectory, withIntermediateDirectories: true) + + try fileManager.copyItem( + at: repositoryRoot.appendingPathComponent("script/package_developer_id.sh"), + to: scriptDirectory.appendingPathComponent("package_developer_id.sh") + ) + try writeExecutable( + """ + #!/usr/bin/env bash + set -euo pipefail + app="$DEVSCOPE_DIST_DIR/DevScope.app" + mkdir -p "$app" + echo "$app" + """, + to: scriptDirectory.appendingPathComponent("build_release_bundle.sh") + ) + try writeExecutable( + "#!/usr/bin/env bash\nexit 0\n", + to: scriptDirectory.appendingPathComponent("validate_release_bundle.sh")) + try writeExecutable( + """ + #!/usr/bin/env bash + echo "$DEVSCOPE_DEVELOPER_ID_SIGN_IDENTITY" + """, + to: fakeBinDirectory.appendingPathComponent("security") + ) + try writeExecutable( + """ + #!/usr/bin/env bash + set -euo pipefail + app="${@: -2:1}" + archive="${@: -1}" + if [[ -f "$app/stapled-ticket" ]]; then + echo stapled >> "$MOCK_DITTO_LOG" + else + echo unstapled >> "$MOCK_DITTO_LOG" + fi + touch "$archive" + """, + to: fakeBinDirectory.appendingPathComponent("ditto") + ) + try writeExecutable( + """ + #!/usr/bin/env bash + set -euo pipefail + if [[ "${1:-}" == "stapler" && "${2:-}" == "staple" ]]; then + touch "$3/stapled-ticket" + fi + """, + to: fakeBinDirectory.appendingPathComponent("xcrun") + ) + try writeExecutable( + "#!/usr/bin/env bash\nexit 0\n", to: fakeBinDirectory.appendingPathComponent("spctl")) + + let logURL = temporaryRoot.appendingPathComponent("ditto.log") + let process = Process() + let output = Pipe() + process.executableURL = URL(fileURLWithPath: "/bin/bash") + process.arguments = [scriptDirectory.appendingPathComponent("package_developer_id.sh").path] + process.standardOutput = output + process.standardError = output + process.environment = ProcessInfo.processInfo.environment.merging([ + "PATH": "\(fakeBinDirectory.path):/usr/bin:/bin", + "DEVSCOPE_DEVELOPER_ID_SIGN_IDENTITY": "Developer ID Application: Test", + "DEVSCOPE_NOTARY_KEYCHAIN_PROFILE": "test-profile", + "DEVSCOPE_DIST_DIR": distDirectory.path, + "MOCK_DITTO_LOG": logURL.path, + ]) { _, replacement in replacement } + + try process.run() + let capturedOutput = output.fileHandleForReading.readDataToEndOfFile() + process.waitUntilExit() + + XCTAssertEqual( + process.terminationStatus, + 0, + String(decoding: capturedOutput, as: UTF8.self) + ) + let archiveStates = try String(contentsOf: logURL, encoding: .utf8) + .split(whereSeparator: \.isNewline) + .map(String.init) + XCTAssertEqual(archiveStates, ["unstapled", "stapled"]) + } + + private func writeExecutable(_ contents: String, to url: URL) throws { + try Data(contents.utf8).write(to: url) + try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: url.path) + } + + private func makeOpenSourceReadinessFixture() throws -> URL { + let fileManager = FileManager.default + let repositoryRoot = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + let temporaryRoot = fileManager.temporaryDirectory + .appendingPathComponent(UUID().uuidString, isDirectory: true) + try fileManager.createDirectory(at: temporaryRoot, withIntermediateDirectories: true) + + let paths = [ + ".github", ".gitleaks.toml", "CHANGELOG.md", "CODE_OF_CONDUCT.md", + "CONTRIBUTING.md", "GOVERNANCE.md", "LICENSE", "NOTICE", "PRIVACY.md", + "README.md", "SECURITY.md", "SUPPORT.md", "THIRD_PARTY_NOTICES.md", + "TRADEMARKS.md", "docs", "script", + ] + for path in paths { + try fileManager.copyItem( + at: repositoryRoot.appendingPathComponent(path), + to: temporaryRoot.appendingPathComponent(path) + ) + } + return temporaryRoot + } + + private func runOpenSourceReadinessChecker( + root: URL, + skipDependencyCheck: Bool = true + ) throws -> ( + status: Int32, output: String + ) { + let repositoryRoot = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + let checker = repositoryRoot.appendingPathComponent( + "script/check_open_source_readiness.sh" + ) + let process = Process() + let output = Pipe() + process.executableURL = URL(fileURLWithPath: "/bin/bash") + process.arguments = [checker.path, "--root", root.path] + if skipDependencyCheck { + process.arguments?.append("--skip-dependency-check") + } + process.standardOutput = output + process.standardError = output + try process.run() + let capturedOutput = output.fileHandleForReading.readDataToEndOfFile() + process.waitUntilExit() + return ( + process.terminationStatus, + String(decoding: capturedOutput, as: UTF8.self) + ) + } + + private func appSource(at path: String) throws -> String { + let repositoryRoot = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + return try String(contentsOf: repositoryRoot.appendingPathComponent(path), encoding: .utf8) + } + + private func entitlementDictionary(at path: String) throws -> [String: Any] { + let data = try Data(appSource(at: path).utf8) + return try XCTUnwrap( + PropertyListSerialization.propertyList(from: data, format: nil) as? [String: Any] + ) + } + + private func sanitizedSwiftSource(_ source: String) -> String { + var sanitizer = SwiftSourceLexicalSanitizer(source: source) + return sanitizer.sanitizedSource() + } + + private func sourceBlock(in source: String, startingWith marker: String) throws -> String { + let markerRange = try XCTUnwrap(source.range(of: marker)) + let openingBrace = try XCTUnwrap(source[markerRange.upperBound...].firstIndex(of: "{")) + var depth = 0 + + for index in source.indices[openingBrace...] { + switch source[index] { + case "{": + depth += 1 + case "}": + depth -= 1 + if depth == 0 { + return String(source[openingBrace...index]) + } + default: + break + } + } + + throw NSError( + domain: "ReleaseScriptTests", + code: 1, + userInfo: [NSLocalizedDescriptionKey: "Unterminated source block after \(marker)"] + ) + } + + private func balancedRegion( + in source: String, + startingWith marker: String, + opening: Character, + closing: Character + ) throws -> String { + let markerRange = try XCTUnwrap(source.range(of: marker)) + let openingIndex = try XCTUnwrap(source[markerRange.upperBound...].firstIndex(of: opening)) + var depth = 0 + + for index in source.indices[openingIndex...] { + if source[index] == opening { + depth += 1 + } else if source[index] == closing { + depth -= 1 + if depth == 0 { + return String(source[openingIndex...index]) + } + } + } + + throw NSError( + domain: "ReleaseScriptTests", + code: 2, + userInfo: [NSLocalizedDescriptionKey: "Unterminated balanced region after \(marker)"] + ) + } + + private func isSafeAutomationSourceComposition( + _ sanitizedArray: String + ) throws -> Bool { + let elements = try topLevelArrayElements(in: sanitizedArray) + let normalizedElements = elements.map { $0.filter { !$0.isWhitespace } } + guard normalizedElements.count == 4 else { return false } + let safeBackgroundExpressions: Set = [ + "BackgroundTaskAutomationSource(runner:runner)", + "BackgroundTaskAutomationSource(runner:runner,diagnosticPolicy:.currentSystem)", + ] + + return normalizedElements.filter { + isDirectInitializer($0, named: "LaunchdAutomationSource") + }.count == 1 + && normalizedElements.filter(safeBackgroundExpressions.contains).count == 1 + && normalizedElements.filter { + isDirectInitializer($0, named: "LegacyLoginItemAutomationSource") + }.count == 1 + && normalizedElements.filter { + isDirectInitializer($0, named: "CronAutomationSource") + }.count == 1 + } + + private func isDirectInitializer(_ expression: String, named typeName: String) -> Bool { + guard expression.hasPrefix("\(typeName)(") else { return false } + let opening = expression.index(expression.startIndex, offsetBy: typeName.count) + var depth = 0 + + for index in expression.indices[opening...] { + if expression[index] == "(" { + depth += 1 + } else if expression[index] == ")" { + depth -= 1 + if depth == 0 { + return index == expression.index(before: expression.endIndex) + } + } + if depth < 0 { return false } + } + return false + } + + private func topLevelArrayElements(in sanitizedArray: String) throws -> [String] { + let trimmed = sanitizedArray.trimmingCharacters(in: .whitespacesAndNewlines) + guard trimmed.first == "[", trimmed.last == "]" else { + throw NSError( + domain: "ReleaseScriptTests", + code: 3, + userInfo: [NSLocalizedDescriptionKey: "Expected one balanced array expression"] + ) + } + + let contentStart = trimmed.index(after: trimmed.startIndex) + let contentEnd = trimmed.index(before: trimmed.endIndex) + let content = trimmed[contentStart..= 0, parenthesisDepth >= 0, bracketDepth >= 0 else { + throw NSError( + domain: "ReleaseScriptTests", + code: 4, + userInfo: [NSLocalizedDescriptionKey: "Unbalanced top-level array element"] + ) + } + } + + guard braceDepth == 0, parenthesisDepth == 0, bracketDepth == 0 else { + throw NSError( + domain: "ReleaseScriptTests", + code: 5, + userInfo: [NSLocalizedDescriptionKey: "Unterminated top-level array element"] + ) + } + appendElement(endingAt: content.endIndex) + return elements + } + + private func topLevelOccurrenceCount(of needle: String, in block: String) -> Int { + guard !needle.isEmpty else { return 0 } + var braceDepth = 0 + var parenthesisDepth = 0 + var bracketDepth = 0 + var count = 0 + var index = block.startIndex + + while index < block.endIndex { + switch block[index] { + case "{": + braceDepth += 1 + case "}": + braceDepth -= 1 + case "(": + parenthesisDepth += 1 + case ")": + parenthesisDepth -= 1 + case "[": + bracketDepth += 1 + case "]": + bracketDepth -= 1 + default: + if braceDepth == 1, + parenthesisDepth == 0, + bracketDepth == 0, + block[index...].hasPrefix(needle) + { + count += 1 + } + } + index = block.index(after: index) + } + + return count + } + + private func sanitizedSourceBlock(in source: String, startingWith marker: String) throws -> String { + try sourceBlock(in: sanitizedSwiftSource(source), startingWith: marker) + } + + private func occurrenceCount(of needle: String, in source: String) -> Int { + guard !needle.isEmpty else { return 0 } + var count = 0 + var searchStart = source.startIndex + while let range = source.range(of: needle, range: searchStart.. Bool { + let expression = try NSRegularExpression(pattern: pattern, options: options) + return expression.firstMatch( + in: source, + range: NSRange(source.startIndex..., in: source) + ) != nil + } + + private func makeSignedTestBundle(at root: URL, sandboxed: Bool) throws -> URL { + let entitlementBody = + sandboxed + ? """ + com.apple.security.app-sandbox + com.apple.security.files.user-selected.read-write + """ + : "" + return try makeSignedTestBundle(at: root, entitlementBody: entitlementBody) + } + + private func makeSignedTestBundle(at root: URL, entitlementBody: String) throws -> URL { + let fileManager = FileManager.default + let appBundle = root.appendingPathComponent("DevScope.app", isDirectory: true) + let contents = appBundle.appendingPathComponent("Contents", isDirectory: true) + let macOS = contents.appendingPathComponent("MacOS", isDirectory: true) + let resources = contents.appendingPathComponent("Resources", isDirectory: true) + try fileManager.createDirectory(at: macOS, withIntermediateDirectories: true) + try fileManager.createDirectory(at: resources, withIntermediateDirectories: true) + + try fileManager.copyItem( + at: URL(fileURLWithPath: "/usr/bin/true"), + to: macOS.appendingPathComponent("DevScope") + ) + try Data("icon".utf8).write(to: resources.appendingPathComponent("AppIcon.icns")) + for filename in ["LICENSE", "NOTICE", "THIRD_PARTY_NOTICES.md"] { + try Data("test notice".utf8).write(to: resources.appendingPathComponent(filename)) + } + try Data( + """ + + + + NSPrivacyAccessedAPITypes + NSPrivacyAccessedAPITypeNSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasonsCA92.1 + + + """.utf8 + ).write(to: resources.appendingPathComponent("PrivacyInfo.xcprivacy")) + try Data( + """ + + + + CFBundleIdentifiercom.example.DevScopeTests + CFBundleShortVersionString1.0 + CFBundleVersion1 + CFBundleExecutableDevScope + + """.utf8 + ).write(to: contents.appendingPathComponent("Info.plist")) + + let entitlements = root.appendingPathComponent("entitlements.plist") + try Data( + """ + + + \(entitlementBody) + """.utf8 + ).write(to: entitlements) + + let signing = Process() + signing.executableURL = URL(fileURLWithPath: "/usr/bin/codesign") + signing.arguments = [ + "--force", "--sign", "-", "--entitlements", entitlements.path, appBundle.path, + ] + try signing.run() + signing.waitUntilExit() + XCTAssertEqual(signing.terminationStatus, 0) + return appBundle + } + + private func runValidation( + for appBundle: URL, + requiresSandbox: Bool, + requiredArchitectures: String = "" + ) throws -> ( + status: Int32, output: String + ) { + let repositoryRoot = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + let process = Process() + let output = Pipe() + process.executableURL = URL(fileURLWithPath: "/bin/bash") + process.arguments = [ + repositoryRoot.appendingPathComponent("script/validate_release_bundle.sh").path, + appBundle.path, + ] + process.environment = ProcessInfo.processInfo.environment.merging([ + "DEVSCOPE_REQUIRE_SANDBOX": requiresSandbox ? "1" : "0", + "DEVSCOPE_REQUIRE_GATEKEEPER": "0", + "DEVSCOPE_REQUIRED_ARCHITECTURES": requiredArchitectures, + ]) { _, replacement in replacement } + process.standardOutput = output + process.standardError = output + try process.run() + let data = output.fileHandleForReading.readDataToEndOfFile() + process.waitUntilExit() + return (process.terminationStatus, String(decoding: data, as: UTF8.self)) + } +} + +private struct SwiftSourceLexicalSanitizer { + private struct StringDelimiter { + let hashCount: Int + let quoteCount: Int + + var length: Int { hashCount + quoteCount } + } + + private let characters: [Character] + private var sanitized: [Character] + private var index = 0 + + init(source: String) { + characters = Array(source) + sanitized = Array(source) + } + + mutating func sanitizedSource() -> String { + scanCode() + return String(sanitized) + } + + private mutating func scanCode() { + while index < characters.count { + if hasPair("/", "/") { + scanLineComment() + } else if hasPair("/", "*") { + scanBlockComment() + } else if let delimiter = openingStringDelimiter() { + scanString(delimiter) + } else { + index += 1 + } + } + } + + private mutating func scanString(_ delimiter: StringDelimiter) { + eraseCurrentCharacters(delimiter.length) + + while index < characters.count { + if isClosingStringDelimiter(delimiter) { + eraseCurrentCharacters(delimiter.length) + return + } + + if let interpolationLength = interpolationStartLength(for: delimiter) { + eraseCurrentCharacters(interpolationLength) + scanInterpolation() + } else if delimiter.hashCount == 0, + characters[index] == "\\", + index + 1 < characters.count { + eraseCurrentCharacters(2) + } else { + eraseCurrentCharacter() + } + } + } + + private mutating func scanInterpolation() { + var parenthesisDepth = 1 + + while index < characters.count, parenthesisDepth > 0 { + if hasPair("/", "/") { + scanLineComment() + } else if hasPair("/", "*") { + scanBlockComment() + } else if let delimiter = openingStringDelimiter() { + scanString(delimiter) + } else if characters[index] == "(" { + parenthesisDepth += 1 + eraseCurrentCharacter() + } else if characters[index] == ")" { + parenthesisDepth -= 1 + eraseCurrentCharacter() + } else { + eraseCurrentCharacter() + } + } + } + + private mutating func scanLineComment() { + while index < characters.count, !characters[index].isNewline { + eraseCurrentCharacter() + } + } + + private mutating func scanBlockComment() { + var depth = 1 + eraseCurrentCharacters(2) + + while index < characters.count, depth > 0 { + if hasPair("/", "*") { + depth += 1 + eraseCurrentCharacters(2) + } else if hasPair("*", "/") { + depth -= 1 + eraseCurrentCharacters(2) + } else { + eraseCurrentCharacter() + } + } + } + + private func openingStringDelimiter() -> StringDelimiter? { + var cursor = index + while cursor < characters.count, characters[cursor] == "#" { + cursor += 1 + } + guard cursor < characters.count, characters[cursor] == "\"" else { return nil } + let hasThreeQuotes = cursor + 2 < characters.count + && characters[cursor + 1] == "\"" + && characters[cursor + 2] == "\"" + return StringDelimiter( + hashCount: cursor - index, + quoteCount: hasThreeQuotes ? 3 : 1 + ) + } + + private func isClosingStringDelimiter(_ delimiter: StringDelimiter) -> Bool { + guard index + delimiter.length <= characters.count else { return false } + for offset in 0.. Int? { + guard characters[index] == "\\" else { return nil } + var cursor = index + 1 + for _ in 0.. Bool { + index + 1 < characters.count + && characters[index] == first + && characters[index + 1] == second + } + + private mutating func eraseCurrentCharacters(_ count: Int) { + for _ in 0.. [UInt8] { + var count = count + return withUnsafeBytes(of: &count) { Array($0) } + } + + XCTAssertNil(KernelProcessArgumentVectorProvider.parse( + buffer: header(1) + [0, 0] + Array("/bin/tool".utf8) + [0] + )) + XCTAssertNil(KernelProcessArgumentVectorProvider.parse( + buffer: header(1) + [0xFF, 0, 0] + Array("/bin/tool".utf8) + [0] + )) + XCTAssertNil(KernelProcessArgumentVectorProvider.parse( + buffer: header(1) + Array("relative-tool".utf8) + [0, 0] + Array("relative-tool".utf8) + [0] + )) + XCTAssertNil(KernelProcessArgumentVectorProvider.parse( + buffer: header(0) + Array("/bin/tool".utf8) + [0, 0] + )) + XCTAssertNil(KernelProcessArgumentVectorProvider.parse( + buffer: header(4_097) + Array("/bin/tool".utf8) + [0, 0] + )) + XCTAssertNil(KernelProcessArgumentVectorProvider.parse( + buffer: header(1) + Array("/bin/tool".utf8) + [0, 0, 0xFF, 0] + )) + XCTAssertNil(KernelProcessArgumentVectorProvider.parse( + buffer: header(1) + Array("/bin/tool".utf8) + [0, 0] + Array("/bin/tool".utf8) + )) + XCTAssertNil(KernelProcessArgumentVectorProvider.parse( + buffer: [UInt8](repeating: 0, count: 1_048_577) + )) + } + + func testNativeScannerFailsClosedWhenInjectedArgumentProviderFails() { + let scanner = NativeProcessScanner( + bundleIdentityProvider: FakeBundleIdentityProvider(identifiers: [:]), + argumentVectorProvider: FakeArgumentVectorProvider(vectors: [:]) + ) + + XCTAssertNil(scanner.argumentVector(for: getpid())) + } + + func testNativeMetadataDropsCollectedArgumentsAndBundleWhenFinalBirthChanges() throws { + let processID = getpid() + let calls = OrderedProcessEvidenceCallRecorder() + let initialBirth = try XCTUnwrap(NativeProcessScanner().birthToken(for: processID)) + let recycledBirth = ProcessBirthToken( + seconds: initialBirth.seconds + 1, + microseconds: initialBirth.microseconds + ) + let bundleProvider = ConstantBundleIdentityProvider( + identifier: "com.example.raced", recorder: calls + ) + let argumentProvider = RecordingArgumentVectorProvider( + vectors: [processID: ["/opt/jobs/exact", "--mode=value"]], recorder: calls + ) + let scanner = NativeProcessScanner( + bundleIdentityProvider: bundleProvider, + argumentVectorProvider: argumentProvider, + finalBirthTokenProvider: FakeFinalBirthTokenProvider(token: recycledBirth, recorder: calls) + ) + + XCTAssertNil(scanner.metadata(for: processID)) + XCTAssertEqual(argumentProvider.requestedProcessIDs, [processID]) + XCTAssertFalse(bundleProvider.requestedPaths.isEmpty, "Bundle metadata must precede final birth") + XCTAssertEqual(calls.evidenceKinds(for: processID), [ + .argumentVector, .bundleIdentity, .finalBirth, + ]) + XCTAssertEqual(calls.count(.finalBirth, for: processID), 1) + } + + func testNativeMetadataKeepsStableBirthAfterCollectedEvidence() throws { + let processID = getpid() + let calls = OrderedProcessEvidenceCallRecorder() + let birthToken = try XCTUnwrap(NativeProcessScanner().birthToken(for: processID)) + let argumentVector = ["/opt/jobs/exact", "--mode=value"] + let scanner = NativeProcessScanner( + bundleIdentityProvider: ConstantBundleIdentityProvider( + identifier: "com.example.stable", recorder: calls + ), + argumentVectorProvider: FakeArgumentVectorProvider( + vectors: [processID: argumentVector], recorder: calls + ), + finalBirthTokenProvider: FakeFinalBirthTokenProvider(token: birthToken, recorder: calls) + ) + + let metadata = try XCTUnwrap(scanner.metadata(for: processID)) + XCTAssertEqual(metadata.birthToken, birthToken) + XCTAssertEqual(metadata.argumentVector, argumentVector) + XCTAssertEqual(metadata.bundleIdentifier, "com.example.stable") + XCTAssertEqual(calls.evidenceKinds(for: processID), [ + .argumentVector, .bundleIdentity, .finalBirth, + ]) + } + + func testNativeSnapshotFinalBirthForCurrentProcessFollowsCollectedEvidence() throws { + let processID = getpid() + let baseline = try XCTUnwrap(NativeProcessScanner().metadata(for: processID)) + let birthToken = try XCTUnwrap(baseline.birthToken) + let calls = OrderedProcessEvidenceCallRecorder() + let argumentVector = ["/opt/jobs/exact", "--mode=value"] + let scanner = NativeProcessScanner( + bundleIdentityProvider: ConstantBundleIdentityProvider( + identifier: "com.example.snapshot", recorder: calls + ), + argumentVectorProvider: FakeArgumentVectorProvider( + vectors: [processID: argumentVector], recorder: calls + ), + finalBirthTokenProvider: FakeFinalBirthTokenProvider(token: birthToken, recorder: calls) + ) + + let snapshotProcess = try XCTUnwrap(scanner.snapshot().first { $0.pid == processID }) + + XCTAssertEqual(snapshotProcess.birthToken, birthToken) + XCTAssertEqual(snapshotProcess.argumentVector, argumentVector) + XCTAssertEqual(snapshotProcess.bundleIdentifier, "com.example.snapshot") + XCTAssertEqual(calls.evidenceKinds(for: processID), [ + .argumentVector, .bundleIdentity, .finalBirth, + ]) + } + + func testNativeMetadataDropsRowWhenFinalBirthReadIsUnavailable() { + let processID = getpid() + let scanner = NativeProcessScanner( + bundleIdentityProvider: ConstantBundleIdentityProvider(identifier: "com.example.unavailable"), + argumentVectorProvider: FakeArgumentVectorProvider( + vectors: [processID: ["/opt/jobs/exact", "--mode=value"]] + ), + finalBirthTokenProvider: FakeFinalBirthTokenProvider(token: nil) + ) + + XCTAssertNil(scanner.metadata(for: processID)) + } + + func testDoesNotDowngradeFullPSExecutableToNativeBasename() { + let psProcess = DevProcess( + pid: 103, + parentPID: 1, + executable: "/usr/libexec/configd", + command: "/usr/libexec/configd", + resourceUsage: DevProcessResourceUsage(cpuPercent: 0.2, residentMemoryBytes: 128, elapsedTime: "03:04") + ) + let nativeProcess = DevProcess( + pid: 103, + parentPID: 1, + executable: "configd", + command: "configd" + ) + + let merged = ProcessScanner.mergedProcess( + psProcess: psProcess, + nativeProcess: nativeProcess, + currentDirectory: nil, + birthToken: nil + ) + + XCTAssertEqual(merged.executable, "/usr/libexec/configd") + XCTAssertEqual(merged.command, "/usr/libexec/configd") + XCTAssertEqual(merged.resourceUsage, psProcess.resourceUsage) + } + + func testExcludesOnlyTheExplicitScannerHelperPID() { + let scannerHelper = DevProcess( + pid: 201, + parentPID: 200, + executable: "/bin/ps", + command: "/bin/ps -axo pid=,ppid=,%cpu=,rss=,etime=,comm=,args=" + ) + let unrelatedPSProcess = DevProcess( + pid: 202, + parentPID: 1, + executable: "/bin/ps", + command: "/bin/ps aux" + ) + let unrelatedProcess = DevProcess( + pid: 203, + parentPID: 1, + executable: "/usr/bin/swift", + command: "swift test" + ) + + let filtered = ProcessScanner.excludingScannerHelper( + from: [scannerHelper, unrelatedPSProcess, unrelatedProcess], + helperPID: scannerHelper.pid + ) + + XCTAssertEqual(filtered.map(\.pid), [unrelatedPSProcess.pid, unrelatedProcess.pid]) + } + + func testSnapshotCompletesAndIncludesTheCurrentTestProcess() throws { + let scanner = SystemProcessScanner() + + let processes = try scanner.snapshot() + + XCTAssertFalse(processes.isEmpty) + XCTAssertTrue( + processes.contains { $0.pid == getpid() }, + "Expected snapshot to include the current XCTest process" + ) + } + + func testNativeSnapshotCompletesAndIncludesTheCurrentProcess() throws { + let scanner = NativeProcessScanner() + + let processes = try scanner.snapshot() + let currentProcess = try XCTUnwrap(processes.first { $0.pid == getpid() }) + + XCTAssertFalse(processes.isEmpty) + XCTAssertNotNil(currentProcess.birthToken) + XCTAssertNotNil(currentProcess.argumentVector) + XCTAssertNil(currentProcess.launchLabel, "Native scanning must not infer a launch label") + } + + private func psProcess( + pid: Int32, + executable: String = "/usr/bin/node", + command: String = "/usr/bin/node worker.js" + ) -> DevProcess { + DevProcess(pid: pid, parentPID: 1, executable: executable, command: command) + } + + private func nativeProcess(pid: Int32, birthToken: ProcessBirthToken) -> DevProcess { + DevProcess( + pid: pid, + parentPID: 1, + executable: "node", + command: "/usr/bin/node", + birthToken: birthToken + ) + } + + private func makeBundle(at url: URL, executable: String, identifier: String) throws -> URL { + let executableURL = url.appendingPathComponent("Contents/MacOS/\(executable)") + try FileManager.default.createDirectory( + at: executableURL.deletingLastPathComponent(), withIntermediateDirectories: true + ) + XCTAssertTrue(FileManager.default.createFile(atPath: executableURL.path, contents: Data())) + let plist: [String: Any] = [ + "CFBundleExecutable": executable, + "CFBundleIdentifier": identifier, + "CFBundlePackageType": "APPL", + ] + let data = try PropertyListSerialization.data( + fromPropertyList: plist, format: .xml, options: 0 + ) + try data.write(to: url.appendingPathComponent("Contents/Info.plist")) + return executableURL + } +} + +private enum ProcessEvidenceKind: Equatable, Sendable { + case argumentVector + case bundleIdentity + case finalBirth +} + +private struct ProcessEvidenceCall: Equatable, Sendable { + let kind: ProcessEvidenceKind + let processID: Int32? +} + +private final class OrderedProcessEvidenceCallRecorder: @unchecked Sendable { + private let lock = NSLock() + private var recordedCalls: [ProcessEvidenceCall] = [] + + func record(_ kind: ProcessEvidenceKind, processID: Int32?) { + lock.withLock { + recordedCalls.append(ProcessEvidenceCall(kind: kind, processID: processID)) + } + } + + func evidenceKinds(for processID: Int32) -> [ProcessEvidenceKind] { + lock.withLock { + recordedCalls.filter { $0.processID == processID }.map(\.kind) + } + } + + func count(_ kind: ProcessEvidenceKind, for processID: Int32) -> Int { + lock.withLock { + recordedCalls.count { $0.processID == processID && $0.kind == kind } + } + } + + func processIDs(for kind: ProcessEvidenceKind) -> [Int32?] { + lock.withLock { + recordedCalls.filter { $0.kind == kind }.map(\.processID) + } + } +} + +private final class FakeNativeMetadataProvider: @unchecked Sendable, NativeProcessMetadataProviding { + private let lock = NSLock() + private var processes: [Int32: DevProcess] + private var requestedIDs: [Int32] = [] + + init(processes: [Int32: DevProcess]) { + self.processes = processes + } + + var requestedProcessIDs: [Int32] { + lock.withLock { requestedIDs } + } + + func setProcess(_ process: DevProcess, for processID: Int32) { + lock.withLock { + processes[processID] = process + } + } + + func metadata(for processID: Int32) -> DevProcess? { + lock.withLock { + requestedIDs.append(processID) + return processes[processID] + } + } +} + +private final class FakeBundleIdentityProvider: @unchecked Sendable, ProcessBundleIdentityProviding { + private let lock = NSLock() + private let identifiers: [String: String] + private var paths: [String] = [] + + init(identifiers: [String: String]) { + self.identifiers = identifiers + } + + var requestedPaths: [String] { + lock.withLock { paths } + } + + func bundleIdentifier(forExecutableURL executableURL: URL, processID _: Int32) -> String? { + lock.withLock { + paths.append(executableURL.path) + return identifiers[executableURL.path] + } + } +} + +private struct FakeArgumentVectorProvider: ProcessArgumentVectorProviding { + let vectors: [Int32: [String]] + let recorder: OrderedProcessEvidenceCallRecorder? + + init( + vectors: [Int32: [String]], + recorder: OrderedProcessEvidenceCallRecorder? = nil + ) { + self.vectors = vectors + self.recorder = recorder + } + + func argumentVector(for processID: Int32) -> [String]? { + recorder?.record(.argumentVector, processID: processID) + return vectors[processID] + } +} + +private final class RecordingArgumentVectorProvider: @unchecked Sendable, ProcessArgumentVectorProviding { + private let lock = NSLock() + private let vectors: [Int32: [String]] + private let recorder: OrderedProcessEvidenceCallRecorder? + private var requestedIDs: [Int32] = [] + + init( + vectors: [Int32: [String]], + recorder: OrderedProcessEvidenceCallRecorder? = nil + ) { + self.vectors = vectors + self.recorder = recorder + } + + var requestedProcessIDs: [Int32] { + lock.withLock { requestedIDs } + } + + func argumentVector(for processID: Int32) -> [String]? { + recorder?.record(.argumentVector, processID: processID) + return lock.withLock { + requestedIDs.append(processID) + return vectors[processID] + } + } +} + +private struct FakeFinalBirthTokenProvider: ProcessBirthTokenProviding { + let token: ProcessBirthToken? + let recorder: OrderedProcessEvidenceCallRecorder? + + init( + token: ProcessBirthToken?, + recorder: OrderedProcessEvidenceCallRecorder? = nil + ) { + self.token = token + self.recorder = recorder + } + + func birthToken(for processID: Int32) -> ProcessBirthToken? { + recorder?.record(.finalBirth, processID: processID) + return token + } +} + +private final class ConstantBundleIdentityProvider: @unchecked Sendable, ProcessBundleIdentityProviding { + private let lock = NSLock() + private let identifier: String + private let recorder: OrderedProcessEvidenceCallRecorder? + private var paths: [String] = [] + + init( + identifier: String, + recorder: OrderedProcessEvidenceCallRecorder? = nil + ) { + self.identifier = identifier + self.recorder = recorder + } + + var requestedPaths: [String] { + lock.withLock { paths } + } + + func bundleIdentifier(forExecutableURL executableURL: URL, processID: Int32) -> String? { + recorder?.record(.bundleIdentity, processID: processID) + return lock.withLock { + paths.append(executableURL.path) + return identifier + } + } +} diff --git a/Tests/DevScopeCoreTests/WorkspaceFactsCacheTests.swift b/Tests/DevScopeCoreTests/WorkspaceFactsCacheTests.swift new file mode 100644 index 0000000..d11b406 --- /dev/null +++ b/Tests/DevScopeCoreTests/WorkspaceFactsCacheTests.swift @@ -0,0 +1,159 @@ +import XCTest +@testable import DevScopeCore + +final class WorkspaceFactsCacheTests: XCTestCase { + func testBudgetedCycleRevalidatesOnlyItsStaleEntryBudget() { + var probedDirectories: [String] = [] + let cache = WorkspaceFactsCache { path in + if path.hasSuffix("/pubspec.yaml") { + probedDirectories.append(String(path.dropLast("/pubspec.yaml".count))) + } + return false + } + let directories = (1...3).map { "/workspace/\($0)" } + directories.forEach { _ = cache.facts(for: $0) } + probedDirectories.removeAll() + cache.invalidateAll() + + cache.withRevalidationCycle(maximumStaleEntries: 2) { + directories.forEach { _ = cache.facts(for: $0) } + } + + XCTAssertEqual(probedDirectories, Array(directories.prefix(2))) + } + + func testLaterBudgetedCycleContinuesRevalidatingRemainingStaleEntries() { + var probedDirectories: [String] = [] + let cache = WorkspaceFactsCache { path in + if path.hasSuffix("/pubspec.yaml") { + probedDirectories.append(String(path.dropLast("/pubspec.yaml".count))) + } + return false + } + let directories = (1...3).map { "/workspace/\($0)" } + directories.forEach { _ = cache.facts(for: $0) } + cache.invalidateAll() + + cache.withRevalidationCycle(maximumStaleEntries: 2) { + directories.forEach { _ = cache.facts(for: $0) } + } + probedDirectories.removeAll() + cache.withRevalidationCycle(maximumStaleEntries: 2) { + directories.forEach { _ = cache.facts(for: $0) } + } + + XCTAssertEqual(probedDirectories, [directories[2]]) + } + + func testDuplicateDirectoryUsesOneStaleEntrySlot() { + var probedDirectories: [String] = [] + let cache = WorkspaceFactsCache { path in + if path.hasSuffix("/pubspec.yaml") { + probedDirectories.append(String(path.dropLast("/pubspec.yaml".count))) + } + return false + } + let directories = ["/workspace/shared", "/workspace/second", "/workspace/third"] + directories.forEach { _ = cache.facts(for: $0) } + cache.invalidateAll() + probedDirectories.removeAll() + + cache.withRevalidationCycle(maximumStaleEntries: 2) { + _ = cache.facts(for: directories[0]) + _ = cache.facts(for: directories[0]) + _ = cache.facts(for: directories[1]) + _ = cache.facts(for: directories[2]) + } + + XCTAssertEqual(probedDirectories, Array(directories.prefix(2))) + } + + func testDirectLookupAfterInvalidationRevalidatesImmediately() { + var manifestExists = false + let cache = WorkspaceFactsCache { path in + manifestExists && path.hasSuffix("/pubspec.yaml") + } + let directory = "/workspace/direct" + + XCTAssertFalse(cache.facts(for: directory).isFlutterWorkspace) + manifestExists = true + cache.invalidateAll() + + XCTAssertTrue(cache.facts(for: directory).isFlutterWorkspace) + } + + func testUnseenDirectoryProbesAfterStaleBudgetIsExhausted() { + var manifests: Set = [] + let cache = WorkspaceFactsCache { path in + manifests.contains(path) + } + let staleDirectory = "/workspace/stale" + let newDirectory = "/workspace/new" + _ = cache.facts(for: staleDirectory) + cache.invalidateAll() + manifests.insert(newDirectory + "/pubspec.yaml") + + let newFacts = cache.withRevalidationCycle(maximumStaleEntries: 0) { + cache.facts(for: newDirectory) + } + + XCTAssertTrue(newFacts.isFlutterWorkspace) + } + + func testLaterInvalidationPrunesDirectoryNotAccessedSincePreviousInvalidation() { + var manifests: Set = [] + let cache = WorkspaceFactsCache { path in + manifests.contains(path) + } + let activeDirectory = "/workspace/active" + let inactiveDirectory = "/workspace/inactive" + _ = cache.facts(for: activeDirectory) + _ = cache.facts(for: inactiveDirectory) + cache.invalidateAll() + cache.withRevalidationCycle(maximumStaleEntries: 0) { + _ = cache.facts(for: activeDirectory) + } + cache.invalidateAll() + manifests.insert(inactiveDirectory + "/pubspec.yaml") + + let inactiveFacts = cache.withRevalidationCycle(maximumStaleEntries: 0) { + cache.facts(for: inactiveDirectory) + } + + XCTAssertTrue(inactiveFacts.isFlutterWorkspace) + } + + func testProbeStartedBeforeConcurrentInvalidationCannotInstallItsResult() { + let firstProbeStarted = DispatchSemaphore(value: 0) + let allowFirstProbeToFinish = DispatchSemaphore(value: 0) + let probeLock = NSLock() + var pubspecProbeCount = 0 + let cache = WorkspaceFactsCache { path in + guard path.hasSuffix("/pubspec.yaml") else { + return false + } + let probeNumber = probeLock.withLock { + pubspecProbeCount += 1 + return pubspecProbeCount + } + if probeNumber == 1 { + firstProbeStarted.signal() + _ = allowFirstProbeToFinish.wait(timeout: .now() + 2) + return false + } + return true + } + let completed = expectation(description: "fresh generation returned") + + DispatchQueue.global().async { + XCTAssertTrue(cache.facts(for: "/workspace/concurrent").isFlutterWorkspace) + completed.fulfill() + } + XCTAssertEqual(firstProbeStarted.wait(timeout: .now() + 2), .success) + cache.invalidateAll() + allowFirstProbeToFinish.signal() + + wait(for: [completed], timeout: 3) + XCTAssertEqual(probeLock.withLock { pubspecProbeCount }, 2) + } +} diff --git a/Tests/DevScopeTests/AutomationApplicationIconStoreTests.swift b/Tests/DevScopeTests/AutomationApplicationIconStoreTests.swift new file mode 100644 index 0000000..ca28d21 --- /dev/null +++ b/Tests/DevScopeTests/AutomationApplicationIconStoreTests.swift @@ -0,0 +1,423 @@ +import AppKit +import Combine +import DevScopeCore +import Foundation +import XCTest +@testable import DevScope + +final class AutomationApplicationIconStoreTests: XCTestCase { + @MainActor + func testBundleIdentifierLookupPrecedesSourceAndExecutableCandidates() async { + let locator = FakeAutomationApplicationLocator( + bundleURLs: ["com.example.App": URL(fileURLWithPath: "/Applications/Example.app")], + iconsByPath: [ + "/Applications/Example.app": NSImage(size: .init(width: 16, height: 16)) + ] + ) + let store = AutomationApplicationIconStore(locator: locator, capacity: 8) + let record = makeRecord( + providerBundleIdentifier: "com.example.App", + sourceURL: URL(fileURLWithPath: "/tmp/fallback.app"), + executable: "/tmp/fallback.app/Contents/MacOS/fallback" + ) + + await store.resolve(record) + + XCTAssertNotNil(store.icon(for: record)) + XCTAssertEqual(locator.requestedBundleIdentifiers, ["com.example.App"]) + XCTAssertEqual(locator.requestedIconPaths, ["/Applications/Example.app"]) + } + + @MainActor + func testMissingBundleLookupFallsThroughToSourceBeforeExecutable() async { + let sourceURL = URL(fileURLWithPath: "/Applications/SourceFallback.app") + let executableURL = URL(fileURLWithPath: "/Applications/ExecutableFallback.app") + let locator = FakeAutomationApplicationLocator( + iconsByPath: [ + sourceURL.path: NSImage(size: .init(width: 16, height: 16)), + executableURL.path: NSImage(size: .init(width: 16, height: 16)) + ] + ) + let store = AutomationApplicationIconStore(locator: locator, capacity: 8) + let record = makeRecord( + providerBundleIdentifier: "com.example.Missing", + sourceURL: sourceURL, + executable: executableURL.appendingPathComponent("Contents/MacOS/Executable").path + ) + + await store.resolve(record) + + XCTAssertNotNil(store.icon(for: record)) + XCTAssertEqual(locator.requestedBundleIdentifiers, ["com.example.Missing"]) + XCTAssertEqual(locator.requestedIconPaths, [sourceURL.path]) + XCTAssertEqual(locator.totalLookupCount, 2) + } + + @MainActor + func testMissingBundleIconFallsThroughToSourceBeforeExecutable() async { + let bundleURL = URL(fileURLWithPath: "/Applications/BundleWithoutIcon.app") + let sourceURL = URL(fileURLWithPath: "/Applications/SourceAfterBundleIcon.app") + let executableURL = URL(fileURLWithPath: "/Applications/ExecutableMustNotLoad.app") + let locator = FakeAutomationApplicationLocator( + bundleURLs: ["com.example.NoIcon": bundleURL], + iconsByPath: [ + sourceURL.path: NSImage(size: .init(width: 16, height: 16)), + executableURL.path: NSImage(size: .init(width: 16, height: 16)) + ] + ) + let store = AutomationApplicationIconStore(locator: locator, capacity: 8) + let record = makeRecord( + providerBundleIdentifier: "com.example.NoIcon", + sourceURL: sourceURL, + executable: executableURL.appendingPathComponent("Contents/MacOS/Executable").path + ) + + await store.resolve(record) + + XCTAssertNotNil(store.icon(for: record)) + XCTAssertEqual(locator.requestedBundleIdentifiers, ["com.example.NoIcon"]) + XCTAssertEqual(locator.requestedIconPaths, [bundleURL.path, sourceURL.path]) + XCTAssertEqual(locator.totalLookupCount, 3) + } + + @MainActor + func testExactApplicationSourceURLProvidesFallbackIcon() async { + let sourceURL = URL(fileURLWithPath: "/Applications/Source.app") + let locator = FakeAutomationApplicationLocator( + iconsByPath: [sourceURL.path: NSImage(size: .init(width: 16, height: 16))] + ) + let store = AutomationApplicationIconStore(locator: locator, capacity: 8) + let record = makeRecord(sourceURL: sourceURL) + + await store.resolve(record) + + XCTAssertNotNil(store.icon(for: record)) + XCTAssertEqual(locator.requestedIconPaths, [sourceURL.path]) + } + + @MainActor + func testExecutableInsideApplicationUsesExactContainingBundle() async { + let applicationURL = URL(fileURLWithPath: "/Applications/Foo.app") + let locator = FakeAutomationApplicationLocator( + iconsByPath: [applicationURL.path: NSImage(size: .init(width: 16, height: 16))] + ) + let store = AutomationApplicationIconStore(locator: locator, capacity: 8) + let record = makeRecord(executable: "/Applications/Foo.app/Contents/MacOS/Foo") + + await store.resolve(record) + + XCTAssertNotNil(store.icon(for: record)) + XCTAssertEqual(locator.requestedIconPaths, [applicationURL.path]) + } + + @MainActor + func testNonApplicationSourcesAndExecutableParentsAreRejected() async { + let locator = FakeAutomationApplicationLocator( + iconsByPath: [ + "/Applications/Unrelated": NSImage(size: .init(width: 16, height: 16)), + "/usr/local": NSImage(size: .init(width: 16, height: 16)) + ] + ) + let store = AutomationApplicationIconStore(locator: locator, capacity: 8) + let record = makeRecord( + sourceURL: URL(fileURLWithPath: "/Applications/Unrelated/Contents/Info.plist"), + executable: "/usr/local/bin/Tool" + ) + + await store.resolve(record) + + XCTAssertNil(store.icon(for: record)) + XCTAssertTrue(locator.requestedIconPaths.isEmpty) + } + + @MainActor + func testPositiveResolutionIsCached() async { + let sourceURL = URL(fileURLWithPath: "/Applications/Cached.app") + let locator = FakeAutomationApplicationLocator( + iconsByPath: [sourceURL.path: NSImage(size: .init(width: 16, height: 16))] + ) + let store = AutomationApplicationIconStore(locator: locator, capacity: 8) + let record = makeRecord(sourceURL: sourceURL) + + await store.resolve(record) + await store.resolve(record) + + XCTAssertNotNil(store.icon(for: record)) + XCTAssertEqual(locator.requestedIconPaths, [sourceURL.path]) + XCTAssertEqual(store.revision, 1) + } + + @MainActor + func testStandardizedSourceAndExecutablePathsReuseOneCachedResolution() async { + let canonicalSourceURL = URL(fileURLWithPath: "/Applications/Normalized.app") + let locator = FakeAutomationApplicationLocator( + iconsByPath: [ + canonicalSourceURL.path: NSImage(size: .init(width: 16, height: 16)) + ] + ) + let store = AutomationApplicationIconStore(locator: locator, capacity: 8) + let canonicalRecord = makeRecord( + id: "canonical", + sourceURL: canonicalSourceURL, + executable: "/Applications/Normalized.app/Contents/MacOS/Normalized" + ) + let equivalentRecord = makeRecord( + id: "equivalent", + sourceURL: URL( + fileURLWithPath: "/Applications/Elsewhere/../Normalized.app" + ), + executable: "/Applications/Normalized.app/Contents/Helpers/../MacOS/Normalized" + ) + + XCTAssertNotEqual(canonicalRecord.sourceURL?.path, equivalentRecord.sourceURL?.path) + XCTAssertNotEqual(canonicalRecord.executable, equivalentRecord.executable) + XCTAssertEqual(store.key(for: canonicalRecord), store.key(for: equivalentRecord)) + + await store.resolve(canonicalRecord) + await store.resolve(equivalentRecord) + + XCTAssertNotNil(store.icon(for: equivalentRecord)) + XCTAssertEqual(locator.requestedIconPaths, [canonicalSourceURL.path]) + XCTAssertEqual(locator.totalLookupCount, 1) + XCTAssertEqual(store.revision, 1) + } + + @MainActor + func testNegativeResolutionIsCached() async { + let locator = FakeAutomationApplicationLocator() + let store = AutomationApplicationIconStore(locator: locator, capacity: 8) + let record = makeRecord( + id: "missing", + providerBundleIdentifier: "com.example.Missing" + ) + + await store.resolve(record) + await store.resolve(record) + + XCTAssertNil(store.icon(for: record)) + XCTAssertEqual(locator.requestedBundleIdentifiers, ["com.example.Missing"]) + XCTAssertEqual(store.revision, 1) + } + + @MainActor + func testConcurrentRequestsForOneKeyCoalesce() async { + let sourceURL = URL(fileURLWithPath: "/Applications/Coalesced.app") + let locator = FakeAutomationApplicationLocator( + iconsByPath: [sourceURL.path: NSImage(size: .init(width: 16, height: 16))] + ) + let store = AutomationApplicationIconStore(locator: locator, capacity: 8) + let record = makeRecord(sourceURL: sourceURL) + + let receivedIconCount = await withTaskGroup(of: Bool.self) { group in + for _ in 0..<10 { + group.addTask { + await store.resolve(record) + return await store.icon(for: record) != nil + } + } + var count = 0 + for await receivedIcon in group { + if receivedIcon { + count += 1 + } + } + return count + } + + XCTAssertEqual(receivedIconCount, 10) + XCTAssertNotNil(store.icon(for: record)) + XCTAssertEqual(locator.requestedIconPaths, [sourceURL.path]) + XCTAssertEqual(store.revision, 1) + } + + @MainActor + func testCancellingOneWaiterDoesNotCancelSharedLookupOrSurvivingWaiter() async { + let sourceURL = URL(fileURLWithPath: "/Applications/Cancellation.app") + let gate = SynchronousIconLookupGate() + let locator = FakeAutomationApplicationLocator( + iconsByPath: [sourceURL.path: NSImage(size: .init(width: 16, height: 16))] + ) + locator.iconLookupGate = gate + let store = AutomationApplicationIconStore(locator: locator, capacity: 8) + let record = makeRecord(sourceURL: sourceURL) + + let cancelledWaiter = Task { @MainActor in + _ = await store.resolve(record) + } + let survivingWaiter = Task { @MainActor in + await store.resolve(record) != nil + } + let cancellation = Task.detached { + let lookupDidStart = gate.waitUntilLookupStarts() + cancelledWaiter.cancel() + gate.releaseLookup() + return lookupDidStart + } + + let survivingWaiterReceivedIcon = await survivingWaiter.value + let lookupDidStart = await cancellation.value + await cancelledWaiter.value + + XCTAssertTrue(lookupDidStart) + XCTAssertTrue(cancelledWaiter.isCancelled) + XCTAssertTrue(survivingWaiterReceivedIcon) + XCTAssertNotNil(store.icon(for: record)) + XCTAssertEqual(locator.requestedIconPaths, [sourceURL.path]) + XCTAssertEqual(locator.totalLookupCount, 1) + XCTAssertEqual(store.revision, 1) + } + + @MainActor + func testPositiveAndNegativeResolutionsDoNotPublishStoreWideInvalidations() async { + let sourceURL = URL(fileURLWithPath: "/Applications/Localized.app") + let locator = FakeAutomationApplicationLocator( + iconsByPath: [sourceURL.path: NSImage(size: .init(width: 16, height: 16))] + ) + let store = AutomationApplicationIconStore(locator: locator, capacity: 8) + let resolved = makeRecord(id: "resolved", sourceURL: sourceURL) + let missing = makeRecord( + id: "missing", + providerBundleIdentifier: "com.example.Missing" + ) + var invalidationCount = 0 + let observation = store.objectWillChange.sink { + invalidationCount += 1 + } + + await store.resolve(resolved) + await store.resolve(missing) + + withExtendedLifetime(observation) {} + XCTAssertEqual(invalidationCount, 0) + XCTAssertNotNil(store.icon(for: resolved)) + XCTAssertNil(store.icon(for: missing)) + } + + @MainActor + func testPositiveAndNegativeEntriesShareBoundedEviction() async { + let firstURL = URL(fileURLWithPath: "/Applications/First.app") + let thirdURL = URL(fileURLWithPath: "/Applications/Third.app") + let locator = FakeAutomationApplicationLocator( + iconsByPath: [ + firstURL.path: NSImage(size: .init(width: 16, height: 16)), + thirdURL.path: NSImage(size: .init(width: 16, height: 16)) + ] + ) + let store = AutomationApplicationIconStore(locator: locator, capacity: 2) + let first = makeRecord(id: "first", sourceURL: firstURL) + let missing = makeRecord( + id: "missing", + providerBundleIdentifier: "com.example.Missing" + ) + let third = makeRecord(id: "third", sourceURL: thirdURL) + + await store.resolve(first) + await store.resolve(missing) + await store.resolve(third) + await store.resolve(missing) + await store.resolve(first) + + XCTAssertEqual( + locator.requestedIconPaths, + [firstURL.path, thirdURL.path, firstURL.path] + ) + XCTAssertEqual(locator.requestedBundleIdentifiers, ["com.example.Missing"]) + XCTAssertEqual(store.revision, 4) + } +} + +@MainActor +private final class FakeAutomationApplicationLocator: AutomationApplicationLocating { + var bundleURLs: [String: URL] + var iconsByPath: [String: NSImage] + var iconLookupGate: SynchronousIconLookupGate? + private(set) var requestedBundleIdentifiers: [String] = [] + private(set) var requestedIconPaths: [String] = [] + + init(bundleURLs: [String: URL] = [:], iconsByPath: [String: NSImage] = [:]) { + self.bundleURLs = bundleURLs + self.iconsByPath = iconsByPath + } + + func applicationURL(forBundleIdentifier identifier: String) -> URL? { + requestedBundleIdentifiers.append(identifier) + return bundleURLs[identifier] + } + + func icon(forApplicationAt url: URL) -> NSImage? { + requestedIconPaths.append(url.path) + iconLookupGate?.pauseLookupUntilReleased() + return iconsByPath[url.path] + } + + var totalLookupCount: Int { + requestedBundleIdentifiers.count + requestedIconPaths.count + } +} + +private final class SynchronousIconLookupGate: @unchecked Sendable { + private let condition = NSCondition() + private var lookupStarted = false + private var lookupReleased = false + + func pauseLookupUntilReleased() { + condition.lock() + lookupStarted = true + condition.broadcast() + while !lookupReleased { + condition.wait() + } + condition.unlock() + } + + func waitUntilLookupStarts(timeout: TimeInterval = 5) -> Bool { + condition.lock() + defer { condition.unlock() } + let deadline = Date().addingTimeInterval(timeout) + while !lookupStarted { + guard condition.wait(until: deadline) else { + return false + } + } + return true + } + + func releaseLookup() { + condition.lock() + lookupReleased = true + condition.broadcast() + condition.unlock() + } +} + +private func makeRecord( + id: String = "icon-record", + providerBundleIdentifier: String? = nil, + sourceURL: URL? = nil, + executable: String? = nil +) -> AutomationRecord { + AutomationRecord( + id: .init(rawValue: id), + kind: .backgroundItem, + sourceKind: .serviceManagement, + label: id, + displayName: "Icon Record", + providerBundleIdentifier: providerBundleIdentifier, + ownerUID: 501, + ownership: .thirdPartySystem, + executable: executable, + arguments: [], + environment: [:], + workingDirectory: nil, + schedule: .init(triggers: [.demand], summary: "On demand"), + sourceURL: sourceURL, + sourceChecksum: nil, + enabledState: .enabled, + loadState: .unknown, + approvalState: .unknown, + state: .idle, + evidence: [], + capabilities: [], + validationFindings: [] + ) +} diff --git a/Tests/DevScopeTests/AutomationDocumentPanelTests.swift b/Tests/DevScopeTests/AutomationDocumentPanelTests.swift new file mode 100644 index 0000000..c7e9e35 --- /dev/null +++ b/Tests/DevScopeTests/AutomationDocumentPanelTests.swift @@ -0,0 +1,85 @@ +import Foundation +import XCTest +import DevScopeCore +@testable import DevScope + +@MainActor +final class AutomationDocumentPanelTests: XCTestCase { + func testValidatedImportDataRejectsContentBeyondLimitAfterRead() throws { + let url = FileManager.default.temporaryDirectory + .appendingPathComponent("devscope-import-\(UUID().uuidString)") + let oversized = Data(repeating: 0x41, count: AutomationDocumentPanel.maximumImportBytes + 1) + try oversized.write(to: url) + defer { try? FileManager.default.removeItem(at: url) } + + XCTAssertThrowsError(try AutomationDocumentPanel.validatedImportData(at: url)) { error in + XCTAssertEqual((error as? CocoaError)?.code, .fileReadTooLarge) + } + } + + func testValidatedImportDataReturnsContentAtLimit() throws { + let url = FileManager.default.temporaryDirectory + .appendingPathComponent("devscope-import-\(UUID().uuidString)") + let content = Data(repeating: 0x42, count: AutomationDocumentPanel.maximumImportBytes) + try content.write(to: url) + defer { try? FileManager.default.removeItem(at: url) } + + XCTAssertEqual(try AutomationDocumentPanel.validatedImportData(at: url), content) + } + + func testValidatedImportDataRefusesSymbolicLinksAndNonRegularFiles() throws { + let directory = FileManager.default.temporaryDirectory + .appendingPathComponent("devscope-import-types-\(UUID().uuidString)", isDirectory: true) + try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) + defer { try? FileManager.default.removeItem(at: directory) } + let source = directory.appendingPathComponent("source") + let link = directory.appendingPathComponent("link") + let fifo = directory.appendingPathComponent("fifo") + try Data("safe".utf8).write(to: source) + try FileManager.default.createSymbolicLink(at: link, withDestinationURL: source) + XCTAssertEqual(mkfifo(fifo.path, 0o600), 0) + + XCTAssertThrowsError(try AutomationDocumentPanel.validatedImportData(at: link)) + XCTAssertThrowsError(try AutomationDocumentPanel.validatedImportData(at: fifo)) + } + + func testExportWriterRestrictsSecretBearingFileToCurrentUser() throws { + let url = FileManager.default.temporaryDirectory + .appendingPathComponent("devscope-export-\(UUID().uuidString)") + defer { try? FileManager.default.removeItem(at: url) } + let artifact = AutomationExportArtifact( + suggestedFilename: "automation.plist", + mediaType: "application/x-plist", + format: "source.plist", + data: Data("TOKEN=private".utf8), + isRedacted: false + ) + + try AutomationDocumentPanel.writeExport(artifact, to: url) + + XCTAssertEqual(try Data(contentsOf: url), artifact.data) + let attributes = try FileManager.default.attributesOfItem(atPath: url.path) + XCTAssertEqual((attributes[.posixPermissions] as? NSNumber)?.intValue, 0o600) + } + + func testExportWriterAtomicallyReplacesExistingFileWithoutKeepingBroadPermissions() throws { + let url = FileManager.default.temporaryDirectory + .appendingPathComponent("devscope-export-existing-\(UUID().uuidString)") + try Data("old".utf8).write(to: url) + try FileManager.default.setAttributes([.posixPermissions: 0o644], ofItemAtPath: url.path) + defer { try? FileManager.default.removeItem(at: url) } + let artifact = AutomationExportArtifact( + suggestedFilename: "automation.plist", + mediaType: "application/x-plist", + format: "source.plist", + data: Data("new-secret".utf8), + isRedacted: false + ) + + try AutomationDocumentPanel.writeExport(artifact, to: url) + + XCTAssertEqual(try Data(contentsOf: url), artifact.data) + let attributes = try FileManager.default.attributesOfItem(atPath: url.path) + XCTAssertEqual((attributes[.posixPermissions] as? NSNumber)?.intValue, 0o600) + } +} diff --git a/Tests/DevScopeTests/AutomationNotifierTests.swift b/Tests/DevScopeTests/AutomationNotifierTests.swift new file mode 100644 index 0000000..9838bc4 --- /dev/null +++ b/Tests/DevScopeTests/AutomationNotifierTests.swift @@ -0,0 +1,238 @@ +import DevScopeCore +import XCTest +@testable import DevScope + +@MainActor +final class AutomationNotifierTests: XCTestCase { + func testDirectOptInRequestsOnceAndDuplicateVerifiedEventDeliversOnce() async { + let center = RecordingNotificationCenter(granted: true) + let notifier = AutomationNotifier(notificationCenter: center) + let event = AutomationEvent.crossedLongRunningThreshold( + process: ProcessIdentity( + pid: 7_001, + birthToken: ProcessBirthToken(seconds: 123, microseconds: 4) + ), + recordID: AutomationRecord.ID(rawValue: "notification-record") + ) + + await notifier.consume([event]) + let deliveryCountBeforeOptIn = await center.deliveryCount() + XCTAssertEqual(deliveryCountBeforeOptIn, 0) + + await notifier.setPreference(.crossedLongRunningThreshold, isEnabled: true) + await notifier.setPreference(.unexpectedExit, isEnabled: true) + await notifier.consume([event, event]) + + let authorizationRequests = await center.authorizationRequestCount() + let deliveryCount = await center.deliveryCount() + let lastDelivery = await center.lastDelivery() + XCTAssertEqual(authorizationRequests, 1) + XCTAssertEqual(deliveryCount, 1) + let delivered = try! XCTUnwrap(lastDelivery) + XCTAssertEqual(delivered.title, "Long-running process detected") + XCTAssertFalse(delivered.body.contains("7_001")) + XCTAssertFalse(delivered.body.contains("notification-record")) + } + + func testDisablingPreferenceNeverRequestsAuthorizationOrDelivers() async { + let center = RecordingNotificationCenter(granted: true) + let notifier = AutomationNotifier(notificationCenter: center) + await notifier.setPreference(.repeatedFailure, isEnabled: false) + await notifier.consume([.repeatedFailure( + recordID: AutomationRecord.ID(rawValue: "private-record"), + observedExitCount: 3 + )]) + + let authorizationRequests = await center.authorizationRequestCount() + let deliveryCount = await center.deliveryCount() + XCTAssertEqual(authorizationRequests, 0) + XCTAssertEqual(deliveryCount, 0) + } + + func testDeniedAuthorizationPublishesBlockedDeliveryWithoutClearingOptIn() async { + let center = RecordingNotificationCenter(granted: false) + let notifier = AutomationNotifier(notificationCenter: center) + + await notifier.synchronize(AutomationNotificationPreferences( + crossedLongRunningThreshold: true, + unexpectedExit: true, + repeatedFailure: true + )) + + XCTAssertEqual(notifier.deliveryState, .denied) + XCTAssertEqual(notifier.preferences, AutomationNotificationPreferences( + crossedLongRunningThreshold: true, + unexpectedExit: true, + repeatedFailure: true + )) + let requests = await center.authorizationRequestCount() + XCTAssertEqual(requests, 1) + } + + func testAuthorizationRequestErrorIsNotReportedAsUserDenial() async { + let notifier = AutomationNotifier(notificationCenter: FailingNotificationCenter()) + + await notifier.setPreference(.unexpectedExit, isEnabled: true) + + XCTAssertEqual(notifier.deliveryState, .authorizationFailed) + XCTAssertTrue(notifier.preferences.unexpectedExit) + } + + func testDeliveryRetriesOnceInsideTheProductionConsumePathAndDeduplicatesSuccess() async { + let center = FlakyNotificationCenter(failingAttempts: [1]) + let notifier = AutomationNotifier( + notificationCenter: center, + retryDelay: .zero, + sleep: { _ in } + ) + let event = AutomationEvent.repeatedFailure( + recordID: AutomationRecord.ID(rawValue: "retryable-record"), + observedExitCount: 3 + ) + await notifier.setPreference(.repeatedFailure, isEnabled: true) + + await notifier.consume([event]) + XCTAssertEqual(notifier.deliveryState, .enabled) + + await notifier.consume([event]) + XCTAssertEqual(notifier.deliveryState, .enabled) + let attempts = await center.deliveryAttempts() + XCTAssertEqual(attempts, 2) + } + + func testOneFailureKeepsTheWholeDeliveryBatchVisibleEvenIfALaterEventSucceeds() async { + let center = FlakyNotificationCenter(failingAttempts: [1, 2]) + let notifier = AutomationNotifier( + notificationCenter: center, + retryDelay: .zero, + sleep: { _ in } + ) + await notifier.setPreference(.repeatedFailure, isEnabled: true) + + await notifier.consume([ + .repeatedFailure( + recordID: AutomationRecord.ID(rawValue: "failed-record"), + observedExitCount: 3 + ), + .repeatedFailure( + recordID: AutomationRecord.ID(rawValue: "successful-record"), + observedExitCount: 3 + ), + ]) + + XCTAssertEqual(notifier.deliveryState, .deliveryFailed) + let attempts = await center.deliveryAttempts() + XCTAssertEqual(attempts, 3) + } + + func testConcurrentConsumeCallsSerializeAndCannotHideAnExhaustedRetry() async { + let center = FlakyNotificationCenter(failingAttempts: [1, 2]) + let sleeper = GatedRetrySleeper() + let notifier = AutomationNotifier( + notificationCenter: center, + retryDelay: .seconds(2), + sleep: { _ in try await sleeper.sleep() } + ) + await notifier.setPreference(.repeatedFailure, isEnabled: true) + let firstEvent = AutomationEvent.repeatedFailure( + recordID: AutomationRecord.ID(rawValue: "first-concurrent-record"), + observedExitCount: 3 + ) + let secondEvent = AutomationEvent.repeatedFailure( + recordID: AutomationRecord.ID(rawValue: "second-concurrent-record"), + observedExitCount: 3 + ) + + let first = Task { await notifier.consume([firstEvent]) } + await sleeper.waitUntilCalled() + let second = Task { await notifier.consume([secondEvent]) } + + let deadline = ContinuousClock.now + .milliseconds(100) + while await center.deliveryAttempts() == 1, ContinuousClock.now < deadline { + try? await Task.sleep(for: .milliseconds(1)) + } + let attemptsWhileFirstRetryWasSleeping = await center.deliveryAttempts() + XCTAssertEqual(attemptsWhileFirstRetryWasSleeping, 1) + + await sleeper.releaseAll() + await first.value + await second.value + + XCTAssertEqual(notifier.deliveryState, .deliveryFailed) + let finalAttempts = await center.deliveryAttempts() + XCTAssertEqual(finalAttempts, 3) + } +} + +private struct FailingNotificationCenter: AutomationNotificationCenterClient { + struct Failure: Error {} + func requestAuthorization() async throws -> Bool { throw Failure() } + func deliver(_ content: AutomationNotificationContent) async throws {} +} + +private actor RecordingNotificationCenter: AutomationNotificationCenterClient { + private let granted: Bool + private var requestCount = 0 + private var deliveries: [AutomationNotificationContent] = [] + + init(granted: Bool) { + self.granted = granted + } + + func requestAuthorization() async throws -> Bool { + requestCount += 1 + return granted + } + + func deliver(_ content: AutomationNotificationContent) async throws { + deliveries.append(content) + } + + func authorizationRequestCount() -> Int { requestCount } + func deliveryCount() -> Int { deliveries.count } + func lastDelivery() -> AutomationNotificationContent? { deliveries.last } +} + +private actor FlakyNotificationCenter: AutomationNotificationCenterClient { + struct Failure: Error {} + private let failingAttempts: Set + private var attempts = 0 + + init(failingAttempts: Set) { + self.failingAttempts = failingAttempts + } + + func requestAuthorization() async throws -> Bool { true } + + func deliver(_ content: AutomationNotificationContent) async throws { + attempts += 1 + if failingAttempts.contains(attempts) { throw Failure() } + } + + func deliveryAttempts() -> Int { attempts } +} + +private actor GatedRetrySleeper { + private var wasCalled = false + private var callWaiters: [CheckedContinuation] = [] + private var sleepWaiters: [CheckedContinuation] = [] + + func sleep() async throws { + wasCalled = true + let waiters = callWaiters + callWaiters.removeAll() + for waiter in waiters { waiter.resume() } + await withCheckedContinuation { sleepWaiters.append($0) } + } + + func waitUntilCalled() async { + guard !wasCalled else { return } + await withCheckedContinuation { callWaiters.append($0) } + } + + func releaseAll() { + let waiters = sleepWaiters + sleepWaiters.removeAll() + for waiter in waiters { waiter.resume() } + } +} diff --git a/Tests/DevScopeTests/AutomationStoreTests.swift b/Tests/DevScopeTests/AutomationStoreTests.swift new file mode 100644 index 0000000..ee802ea --- /dev/null +++ b/Tests/DevScopeTests/AutomationStoreTests.swift @@ -0,0 +1,416 @@ +import DevScopeCore +import XCTest +@testable import DevScope + +@MainActor +final class AutomationStoreTests: XCTestCase { + func testRefreshPublishesCapabilityDecisionsWithTheSameInventoryGeneration() async { + let record = automationRecord(capabilities: [.exportRecord]) + let expected = AutomationCapabilityDecision( + capabilities: [.startNow, .disable, .exportRecord], + reason: "Some lifecycle operations are unavailable." + ) + let inventory = ImmediateInventoryService(snapshot: AutomationInventorySnapshot( + generation: 17, + records: [record], + health: [:], + refreshedAt: Date(timeIntervalSince1970: 17) + )) + let store = AutomationStore( + inventoryService: inventory, + manager: ImmediateAutomationManager(), + capabilityDecisionProvider: FixedCapabilityDecisionProvider( + decisions: [record.id: expected] + ) + ) + + store.refresh() + await eventually { !store.isRefreshing && store.snapshot.generation == 17 } + + XCTAssertEqual(store.capabilitySnapshot.inventoryGeneration, 17) + XCTAssertEqual(store.capabilitySnapshot.decisionsByRecordID[record.id], expected) + } + + func testMissingCapabilityDecisionFailsClosedToPublishedInspectionOnlyExport() async { + let record = automationRecord(capabilities: [.disable, .exportRecord]) + let store = AutomationStore( + inventoryService: ImmediateInventoryService(snapshot: AutomationInventorySnapshot( + generation: 21, + records: [record], + health: [:], + refreshedAt: Date(timeIntervalSince1970: 21) + )), + manager: ImmediateAutomationManager(), + capabilityDecisionProvider: FixedCapabilityDecisionProvider(decisions: [:]) + ) + + store.refresh() + await eventually { !store.isRefreshing && store.snapshot.generation == 21 } + + let decision = store.capabilitySnapshot.decisionsByRecordID[record.id] + XCTAssertEqual(decision?.capabilities, [.exportRecord]) + XCTAssertEqual( + decision?.reason, + "DevScope could not verify source ownership and path safety for management." + ) + } + + func testImportDestinationIsTheSelectedChecksumBoundSourceWhileDuplicateUsesSibling() { + let record = automationRecord( + capabilities: [.importRecord, .duplicate] + ) + let provider = AutomationManagementDestinationProvider( + transactionRoot: URL(fileURLWithPath: "/tmp/transactions") + ) + + XCTAssertEqual( + provider.importDestination(for: record, suggestedFilename: "other.plist"), + record.sourceURL + ) + XCTAssertEqual( + provider.duplicateDestination(for: record, label: "Store.Copy"), + record.sourceURL?.deletingLastPathComponent().appendingPathComponent("Store.Copy.plist") + ) + XCTAssertNil(provider.duplicateDestination(for: record, label: "../escape")) + } + + func testCompletedSnapshotPublishesBeforeQueuedOperationRefreshCanFinish() async { + let inventory = GatedInventoryService() + let manager = ImmediateAutomationManager() + let store = AutomationStore(inventoryService: inventory, manager: manager) + let record = automationRecord() + + store.refresh() + await inventory.waitUntilCall(1) + store.perform(.disable, record: record, expectedChecksum: nil, linkedProcesses: []) + await inventory.release(call: 1, generation: 1) + await inventory.waitUntilCall(2) + + XCTAssertEqual(store.snapshot.generation, 1) + XCTAssertNotNil(store.pendingOperation) + + await inventory.release(call: 2, generation: 2) + await eventually { store.pendingOperation == nil } + XCTAssertEqual(store.snapshot.generation, 2) + } + + func testOperationCompletionReadsTheManagerRefreshedCacheWithoutForcingAnotherGeneration() async { + let inventory = GatedInventoryService() + let store = AutomationStore( + inventoryService: inventory, + manager: ImmediateAutomationManager() + ) + + store.perform( + .disable, + record: automationRecord(), + expectedChecksum: nil, + linkedProcesses: [] + ) + await inventory.waitUntilCall(1) + + let callKinds = await inventory.callKinds() + XCTAssertEqual(callKinds, ["automatic"]) + await inventory.release(call: 1, generation: 1) + await eventually { store.pendingOperation == nil } + } + + func testRefreshQueueIsBoundedToOneTrailingRequest() async { + let inventory = GatedInventoryService() + let store = AutomationStore( + inventoryService: inventory, + manager: ImmediateAutomationManager() + ) + + store.refresh() + await inventory.waitUntilCall(1) + for _ in 0..<20 { store.refresh() } + await inventory.release(call: 1, generation: 1) + await inventory.waitUntilCall(2) + let callsWhileTrailing = await inventory.callCount() + XCTAssertEqual(callsWhileTrailing, 2) + await inventory.release(call: 2, generation: 2) + await eventually { !store.isRefreshing } + let finalCalls = await inventory.callCount() + XCTAssertEqual(finalCalls, 2) + } + + func testStopInvalidatesOldCompletionAndRestartCanPublishNewGeneration() async { + let inventory = GatedInventoryService() + let store = AutomationStore( + inventoryService: inventory, + manager: ImmediateAutomationManager() + ) + + store.refresh() + await inventory.waitUntilCall(1) + store.stop() + store.refresh() + await inventory.waitUntilCall(2) + await inventory.release(call: 1, generation: 41) + await Task.yield() + XCTAssertNotEqual(store.snapshot.generation, 41) + await inventory.release(call: 2, generation: 42) + await eventually { store.snapshot.generation == 42 } + } + + func testStopPreservesAuthorizedOperationUntilResultAndEvidenceArePublished() async { + let manager = GatedAutomationManager() + let store = AutomationStore( + inventoryService: ImmediateInventoryService( + snapshot: ProcessStoreLiveSnapshot.emptyAutomationInventory + ), + manager: manager + ) + store.perform(.disable, record: automationRecord(), expectedChecksum: nil, linkedProcesses: []) + await manager.waitUntilCalled() + XCTAssertNotNil(store.pendingOperation) + + store.stop() + XCTAssertNotNil(store.pendingOperation) + await manager.release() + await eventually { store.pendingOperation == nil } + XCTAssertEqual(store.operationResult?.status, .succeeded) + XCTAssertEqual(store.operationResultRecordID, automationRecord().id) + } + + func testLifecycleUsesOne350MillisecondInitialDelayAndAtLeast60SecondCadence() async { + let inventory = GatedInventoryService() + let sleeper = ManualSleeper() + let store = AutomationStore( + inventoryService: inventory, + manager: ImmediateAutomationManager(), + sleep: { try await sleeper.sleep($0) } + ) + + store.start() + store.start() + await sleeper.waitUntilCall(1) + let firstDuration = await sleeper.duration(call: 1) + let initialCallCount = await sleeper.callCount() + XCTAssertEqual(firstDuration, .milliseconds(350)) + XCTAssertEqual(initialCallCount, 1) + await sleeper.release(call: 1) + await inventory.waitUntilCall(1) + await sleeper.waitUntilCall(2) + let observedCadence = await sleeper.duration(call: 2) + let cadence = try! XCTUnwrap(observedCadence) + XCTAssertGreaterThanOrEqual(cadence, .seconds(60)) + + store.stop() + store.start() + await sleeper.waitUntilCall(3) + let restartedDuration = await sleeper.duration(call: 3) + XCTAssertEqual(restartedDuration, .milliseconds(350)) + store.stop() + await sleeper.releaseAll() + await inventory.releaseAll() + } +} + +private actor GatedInventoryService: AutomationInventoryRefreshing { + private struct Pending { + let call: Int + let continuation: CheckedContinuation + } + + private var calls = 0 + private var kinds: [String] = [] + private var pending: [Int: Pending] = [:] + private var waiters: [(Int, CheckedContinuation)] = [] + + func refresh(force: Bool) async -> AutomationInventorySnapshot { + kinds.append(force ? "force" : "automatic") + return await next() + } + + func refreshAfterCurrent() async -> AutomationInventorySnapshot { + kinds.append("afterCurrent") + return await next() + } + + private func next() async -> AutomationInventorySnapshot { + calls += 1 + let call = calls + let ready = waiters.filter { $0.0 <= calls } + waiters.removeAll { $0.0 <= calls } + for (_, waiter) in ready { waiter.resume() } + return await withCheckedContinuation { continuation in + pending[call] = Pending(call: call, continuation: continuation) + } + } + + func waitUntilCall(_ target: Int) async { + guard calls < target else { return } + await withCheckedContinuation { waiters.append((target, $0)) } + } + + func release(call: Int, generation: UInt64) { + pending.removeValue(forKey: call)?.continuation.resume(returning: AutomationInventorySnapshot( + generation: generation, + records: [], + health: [:], + refreshedAt: Date(timeIntervalSince1970: TimeInterval(generation)) + )) + } + + func callCount() -> Int { calls } + func callKinds() -> [String] { kinds } + + func releaseAll() { + for (call, value) in pending.sorted(by: { $0.key < $1.key }) { + value.continuation.resume(returning: AutomationInventorySnapshot( + generation: UInt64(call), records: [], health: [:], refreshedAt: .distantPast + )) + } + pending.removeAll() + } +} + +private actor ImmediateAutomationManager: AutomationManaging { + func perform( + _ operation: AutomationOperation, + record: AutomationRecord, + expectedChecksum: String?, + linkedProcesses: [ClassifiedDevProcess] + ) async -> AutomationOperationResult { + AutomationOperationResult( + operation: operation, + status: .succeeded, + appliedSteps: [], + verificationEvidence: [], + rollback: .notNeeded, + manualRecovery: nil + ) + } +} + +private actor GatedAutomationManager: AutomationManaging { + private var continuation: CheckedContinuation? + private var callWaiter: CheckedContinuation? + private var called = false + + func perform( + _ operation: AutomationOperation, + record: AutomationRecord, + expectedChecksum: String?, + linkedProcesses: [ClassifiedDevProcess] + ) async -> AutomationOperationResult { + called = true + callWaiter?.resume() + callWaiter = nil + await withCheckedContinuation { continuation = $0 } + return AutomationOperationResult( + operation: operation, status: .succeeded, appliedSteps: [], + verificationEvidence: [], rollback: .notNeeded, manualRecovery: nil + ) + } + + func waitUntilCalled() async { + guard !called else { return } + await withCheckedContinuation { callWaiter = $0 } + } + + func release() { + continuation?.resume() + continuation = nil + } +} + +private actor ManualSleeper { + private var calls: [Int: (Duration, CheckedContinuation)] = [:] + private var nextCall = 0 + private var waiters: [(Int, CheckedContinuation)] = [] + + func sleep(_ duration: Duration) async throws { + nextCall += 1 + let call = nextCall + let ready = waiters.filter { $0.0 <= nextCall } + waiters.removeAll { $0.0 <= nextCall } + for (_, waiter) in ready { waiter.resume() } + await withCheckedContinuation { calls[call] = (duration, $0) } + try Task.checkCancellation() + } + + func waitUntilCall(_ target: Int) async { + guard nextCall < target else { return } + await withCheckedContinuation { waiters.append((target, $0)) } + } + + func duration(call: Int) -> Duration? { calls[call]?.0 } + func callCount() -> Int { nextCall } + + func release(call: Int) { + calls.removeValue(forKey: call)?.1.resume() + } + + func releaseAll() { + let continuations = calls.values.map(\.1) + calls.removeAll() + for continuation in continuations { continuation.resume() } + } +} + +@MainActor +private func eventually( + _ predicate: @MainActor () -> Bool, + file: StaticString = #filePath, + line: UInt = #line +) async { + for _ in 0..<100 where !predicate() { + await Task.yield() + } + if !predicate() { + XCTFail("Timed out waiting for asynchronous store state.", file: file, line: line) + } +} + +private func automationRecord( + capabilities: Set = [] +) -> AutomationRecord { + AutomationRecord( + id: AutomationRecord.ID(rawValue: "store-test"), + kind: .launchAgent, + sourceKind: .launchAgent, + label: "Store Test", + displayName: "Store Test", + providerBundleIdentifier: nil, + ownerUID: 501, + ownership: .user, + executable: "/bin/true", + arguments: [], + environment: [:], + workingDirectory: nil, + schedule: AutomationSchedule(triggers: [.demand], summary: "On demand"), + sourceURL: URL(fileURLWithPath: "/tmp/store-test.plist"), + sourceChecksum: nil, + enabledState: .enabled, + loadState: .unknown, + approvalState: .notApplicable, + state: .idle, + evidence: [], + capabilities: capabilities, + validationFindings: [] + ) +} + +private actor ImmediateInventoryService: AutomationInventoryRefreshing { + let snapshot: AutomationInventorySnapshot + + init(snapshot: AutomationInventorySnapshot) { + self.snapshot = snapshot + } + + func refresh(force: Bool) async -> AutomationInventorySnapshot { snapshot } + func refreshAfterCurrent() async -> AutomationInventorySnapshot { snapshot } +} + +private struct FixedCapabilityDecisionProvider: AutomationCapabilityDecisionProviding { + let decisions: [AutomationRecord.ID: AutomationCapabilityDecision] + + func decisions( + for records: [AutomationRecord] + ) async -> [AutomationRecord.ID: AutomationCapabilityDecision] { + decisions + } +} diff --git a/Tests/DevScopeTests/AutomationVisualIdentityTests.swift b/Tests/DevScopeTests/AutomationVisualIdentityTests.swift new file mode 100644 index 0000000..92b90bc --- /dev/null +++ b/Tests/DevScopeTests/AutomationVisualIdentityTests.swift @@ -0,0 +1,80 @@ +import DevScopeCore +import XCTest + +@testable import DevScope + +final class AutomationVisualIdentityTests: XCTestCase { + func testEveryAutomationKindHasAnExactStableFallbackIdentity() { + let expected: [AutomationKind: AutomationVisualIdentity] = [ + .launchAgent: .init( + symbolName: "bolt.horizontal", + colorToken: .blue, + accessibilityTitle: "LaunchAgent" + ), + .launchDaemon: .init( + symbolName: "bolt.horizontal.circle", + colorToken: .indigo, + accessibilityTitle: "LaunchDaemon" + ), + .loginItem: .init( + symbolName: "person.crop.circle.badge.clock", + colorToken: .cyan, + accessibilityTitle: "Login item" + ), + .backgroundItem: .init( + symbolName: "person.crop.circle.badge.clock", + colorToken: .cyan, + accessibilityTitle: "Background item" + ), + .cron: .init( + symbolName: "calendar.badge.clock", + colorToken: .purple, + accessibilityTitle: "Scheduled automation" + ), + ] + + XCTAssertEqual(Set(expected.keys), Set(AutomationKind.allCases)) + for kind in AutomationKind.allCases { + XCTAssertEqual(AutomationVisualIdentity.fallback(for: kind), expected[kind]) + } + } + + func testAutomationStatesKeepSymbolTextAndSemanticColorTogether() { + let expected: [(AutomationState, String, AutomationColorToken, String)] = [ + (.running, "play.circle.fill", .green, "Running"), + (.idle, "pause.circle", .secondary, "Idle"), + (.disabled, "nosign", .secondary, "Disabled"), + (.needsApproval, "person.badge.clock", .orange, "Needs Approval"), + (.invalid, "exclamationmark.triangle", .red, "Invalid"), + (.unresolved, "questionmark.circle", .orange, "Unresolved"), + ] + + for (state, symbolName, colorToken, accessibilityTitle) in expected { + let identity = AutomationVisualIdentity.state(for: state) + XCTAssertEqual(identity.symbolName, symbolName) + XCTAssertEqual(identity.colorToken, colorToken) + XCTAssertEqual(identity.accessibilityTitle, accessibilityTitle) + } + } + + func testEveryAutomationStateLabelCarriesVisibleTextSymbolAndColor() { + let expected: [(AutomationState, String, String, AutomationColorToken)] = [ + (.running, "Running", "play.circle.fill", .green), + (.idle, "Idle", "pause.circle", .secondary), + (.disabled, "Disabled", "nosign", .secondary), + (.needsApproval, "Needs Approval", "person.badge.clock", .orange), + (.invalid, "Invalid", "exclamationmark.triangle", .red), + (.unresolved, "Unresolved", "questionmark.circle", .orange), + ] + + XCTAssertEqual(expected.count, AutomationState.allCases.count) + for (state, title, symbolName, colorToken) in expected { + let presentation = AutomationStateLabelPresentation(state: state) + XCTAssertEqual(presentation.title, title) + XCTAssertEqual(presentation.symbolName, symbolName) + XCTAssertEqual(presentation.colorToken, colorToken) + XCTAssertFalse(presentation.title.isEmpty) + XCTAssertFalse(presentation.symbolName.isEmpty) + } + } +} diff --git a/Tests/DevScopeTests/AutomationWorkspaceLayoutPolicyTests.swift b/Tests/DevScopeTests/AutomationWorkspaceLayoutPolicyTests.swift new file mode 100644 index 0000000..a5d5503 --- /dev/null +++ b/Tests/DevScopeTests/AutomationWorkspaceLayoutPolicyTests.swift @@ -0,0 +1,87 @@ +import XCTest + +@testable import DevScope + +final class AutomationWorkspaceLayoutPolicyTests: XCTestCase { + func testConstraintsFitEverySupportedWindowWidth() { + for width in [1080.0, 1280.0, 1728.0] { + let value = AutomationWorkspaceLayoutPolicy.constraints(availableWidth: width) + + XCTAssertLessThanOrEqual(value.minimumTotal, width) + XCTAssertGreaterThan(value.tableMinimum, value.railMinimum) + XCTAssertEqual(value.detailPriority, value.tablePriority) + } + } + + func testWiderWindowsIncreasePreferenceWithoutMakingPanelsFixed() { + let compact = AutomationWorkspaceLayoutPolicy.constraints(availableWidth: 1080) + let wide = AutomationWorkspaceLayoutPolicy.constraints(availableWidth: 1728) + + XCTAssertGreaterThan(wide.tablePreferred, compact.tablePreferred) + XCTAssertNotEqual(compact.railPreferred, wide.railPreferred) + XCTAssertNotEqual(compact.detailPreferred, wide.detailPreferred) + } + + func testMinimumsProtectSplitterAllowanceAcrossBoundaryWidths() { + let widths: [CGFloat] = [0, 17, 18, 19, 797, 798, 799] + + for width in widths { + let value = AutomationWorkspaceLayoutPolicy.constraints(availableWidth: width) + let usableWidth = max(0, width - 18) + let expectedMinimumTotal = min(usableWidth, 780) + + XCTAssertEqual(value.minimumTotal, expectedMinimumTotal, accuracy: 0.000_001) + XCTAssertLessThanOrEqual(value.minimumTotal, usableWidth + 0.000_001) + for minimum in [value.railMinimum, value.tableMinimum, value.detailMinimum] { + XCTAssertTrue(minimum.isFinite, "minimum must be finite at width \(width)") + XCTAssertGreaterThanOrEqual(minimum, 0, "minimum must be nonnegative at width \(width)") + } + } + } + + func testPreferencesRemainValidDuringTransientBoundaryWidths() { + let widths: [CGFloat] = [0, 1, 17, 18, 19, 400, 797, 798, 808, 828, 1080] + + for width in widths { + let value = AutomationWorkspaceLayoutPolicy.constraints(availableWidth: width) + let usableWidth = max(0, width - 18) + let preferences = [value.railPreferred, value.tablePreferred, value.detailPreferred] + let minimums = [value.railMinimum, value.tableMinimum, value.detailMinimum] + + XCTAssertLessThanOrEqual( + preferences.reduce(0, +), + usableWidth + 0.000_001, + "preferred widths must fit at width \(width)" + ) + for (preferred, minimum) in zip(preferences, minimums) { + XCTAssertTrue(preferred.isFinite, "preference must be finite at width \(width)") + XCTAssertGreaterThanOrEqual(preferred, 0, "preference must be nonnegative at width \(width)") + XCTAssertGreaterThanOrEqual( + preferred + 0.000_001, + minimum, + "preference must not undercut its minimum at width \(width)" + ) + } + } + } + + func testMinimumScalingUsesUsableWidthUntilItCaps() { + for width: CGFloat in [19, 797] { + let value = AutomationWorkspaceLayoutPolicy.constraints(availableWidth: width) + let expectedScale = (width - 18) / 780 + + XCTAssertEqual(value.railMinimum, 150 * expectedScale, accuracy: 0.000_001) + XCTAssertEqual(value.tableMinimum, 360 * expectedScale, accuracy: 0.000_001) + XCTAssertEqual(value.detailMinimum, 270 * expectedScale, accuracy: 0.000_001) + } + + for width: CGFloat in [798, 799, 1728] { + let value = AutomationWorkspaceLayoutPolicy.constraints(availableWidth: width) + + XCTAssertEqual(value.railMinimum, 150, accuracy: 0.000_001) + XCTAssertEqual(value.tableMinimum, 360, accuracy: 0.000_001) + XCTAssertEqual(value.detailMinimum, 270, accuracy: 0.000_001) + XCTAssertEqual(value.minimumTotal, 780, accuracy: 0.000_001) + } + } +} diff --git a/Tests/DevScopeTests/LaunchctlAutomationRuntimeStateProviderTests.swift b/Tests/DevScopeTests/LaunchctlAutomationRuntimeStateProviderTests.swift new file mode 100644 index 0000000..e329afe --- /dev/null +++ b/Tests/DevScopeTests/LaunchctlAutomationRuntimeStateProviderTests.swift @@ -0,0 +1,173 @@ +import DevScopeCore +import Foundation +import XCTest + +@testable import DevScope + +final class LaunchctlAutomationRuntimeStateProviderTests: XCTestCase { + func testParsesNamedAndBooleanPrintDisabledValuesAndRejectsConflicts() { + let parsed = LaunchctlAutomationRuntimeStateProvider.parseEnabledStates( + Data( + """ + disabled services = { + "com.example.disabled" => disabled + "com.example.enabled" => enabled + "com.example.true" => true + "com.example.false" => false + } + """.utf8)) + + XCTAssertEqual(parsed?["com.example.disabled"], .disabled) + XCTAssertEqual(parsed?["com.example.enabled"], .enabled) + XCTAssertEqual(parsed?["com.example.true"], .disabled) + XCTAssertEqual(parsed?["com.example.false"], .enabled) + XCTAssertNil( + LaunchctlAutomationRuntimeStateProvider.parseEnabledStates( + Data( + """ + "com.example.conflict" => enabled + "com.example.conflict" => disabled + """.utf8))) + } + + func testQueriesOneDomainMapAndDistinguishesMissingTargetsFromOperationalFailures() async { + let runner = RuntimeStateCommandRunner() + let provider = LaunchctlAutomationRuntimeStateProvider(runner: runner) + + let states = await provider.states( + for: [ + "com.example.loaded", + "com.example.disabled", + "com.example.default", + "com.example.operational-failure", + ], + guiUID: 501 + ) + + XCTAssertEqual( + states["com.example.loaded"], + LaunchdRuntimeState( + enabledState: .enabled, + loadState: .loaded + )) + XCTAssertEqual( + states["com.example.disabled"], + LaunchdRuntimeState( + enabledState: .disabled, + loadState: .unloaded + )) + XCTAssertEqual( + states["com.example.default"], + LaunchdRuntimeState( + enabledState: .enabled, + loadState: .unloaded + )) + XCTAssertEqual( + states["com.example.operational-failure"], + LaunchdRuntimeState( + enabledState: .enabled, + loadState: .unknown + )) + let invocations = await runner.invocations() + XCTAssertEqual(invocations.filter { $0.arguments == ["print-disabled", "gui/501"] }.count, 1) + XCTAssertEqual( + Set(invocations.filter { $0.arguments.first == "print" }.map { $0.arguments.last! }), + [ + "gui/501/com.example.loaded", + "gui/501/com.example.disabled", + "gui/501/com.example.default", + "gui/501/com.example.operational-failure", + ] + ) + XCTAssertTrue(invocations.allSatisfy { $0.environment["LC_ALL"] == "C" }) + } + + func testDeduplicatesLabelsAndBoundsConcurrentPrintQueries() async { + let runner = ConcurrencyTrackingRuntimeStateCommandRunner() + let provider = LaunchctlAutomationRuntimeStateProvider(runner: runner) + let uniqueLabels = (0..<12).map { "com.example.job-\($0)" } + + let states = await provider.states( + for: uniqueLabels + [uniqueLabels[3], uniqueLabels[7], uniqueLabels[3]], + guiUID: 501 + ) + let metrics = await runner.metrics() + + XCTAssertEqual(states.count, uniqueLabels.count) + XCTAssertEqual(metrics.printInvocations, uniqueLabels.count) + XCTAssertLessThanOrEqual(metrics.maximumConcurrentPrints, 4) + for (index, label) in uniqueLabels.enumerated() { + XCTAssertEqual(states[label]?.loadState, index.isMultiple(of: 2) ? .loaded : .unloaded) + } + } +} + +private actor RuntimeStateCommandRunner: AutomationCommandRunning { + private var commands: [AutomationCommand] = [] + + func run(_ command: AutomationCommand) async throws -> AutomationCommandResult { + commands.append(command) + if command.arguments == ["print-disabled", "gui/501"] { + return AutomationCommandResult( + status: 0, + standardOutput: Data("\"com.example.disabled\" => disabled\n".utf8), + standardError: Data() + ) + } + if command.arguments.last == "gui/501/com.example.loaded" { + return AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()) + } + if command.arguments.last == "gui/501/com.example.operational-failure" { + return AutomationCommandResult( + status: 113, + standardOutput: Data(), + standardError: Data("Operation not permitted\n".utf8) + ) + } + let label = command.arguments.last!.split(separator: "/").last! + return AutomationCommandResult( + status: 113, + standardOutput: Data(), + standardError: Data( + "Bad request.\nCould not find service \"\(label)\" in domain for user gui: 501\n".utf8 + ) + ) + } + + func invocations() -> [AutomationCommand] { commands } +} + +private actor ConcurrencyTrackingRuntimeStateCommandRunner: AutomationCommandRunning { + private var printInvocations = 0 + private var activePrints = 0 + private var maximumConcurrentPrints = 0 + + func run(_ command: AutomationCommand) async throws -> AutomationCommandResult { + guard command.arguments.first == "print" else { + return AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()) + } + printInvocations += 1 + activePrints += 1 + maximumConcurrentPrints = max(maximumConcurrentPrints, activePrints) + defer { activePrints -= 1 } + try? await Task.sleep(for: .milliseconds(10)) + + let target = command.arguments.last! + let label = String(target.split(separator: "/").last!) + let index = Int(label.split(separator: "-").last!)! + if index.isMultiple(of: 2) { + return AutomationCommandResult(status: 0, standardOutput: Data(), standardError: Data()) + } + return AutomationCommandResult( + status: 113, + standardOutput: Data(), + standardError: Data( + "Bad request.\nCould not find service \"\(label)\" in domain for user gui: 501\n".utf8 + ) + ) + } + + func metrics() -> (printInvocations: Int, maximumConcurrentPrints: Int) { + (printInvocations, maximumConcurrentPrints) + } +} diff --git a/Tests/DevScopeTests/LocalAutomationFileSystemTests.swift b/Tests/DevScopeTests/LocalAutomationFileSystemTests.swift new file mode 100644 index 0000000..9bb5174 --- /dev/null +++ b/Tests/DevScopeTests/LocalAutomationFileSystemTests.swift @@ -0,0 +1,564 @@ +import Foundation +import DevScopeCore +import XCTest +@testable import DevScope + +final class LocalAutomationFileSystemTests: XCTestCase { + func testReadRejectsFilesLargerThanTheConfiguredBoundary() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let source = fixture.approved.appending(path: "oversized.plist") + try Data(repeating: 0x41, count: 65).write(to: source) + let fileSystem = LocalAutomationFileSystem(maxReadableBytes: 64) + + XCTAssertThrowsError(try fileSystem.read(source)) { error in + XCTAssertEqual((error as? POSIXError)?.code, .EFBIG) + } + } + + func testFileAuthorizationAcceptsEquivalentParentWithDifferentDirectoryHint() throws { + let parentWithHint = URL(fileURLWithPath: "/tmp/devscope-authorization", isDirectory: true) + let parentWithoutHint = URL(fileURLWithPath: "/tmp/devscope-authorization") + let directory = try XCTUnwrap(AutomationDirectoryAuthorization( + directoryURL: parentWithHint, + resourceIdentifier: "device:inode" + )) + + XCTAssertNotNil(AutomationFileAuthorization( + fileURL: parentWithoutHint.appendingPathComponent("source.plist"), + directory: directory, + expectation: .absent + )) + } + + func testTemporaryWriteRejectsAuthorizedParentReplacedBySymlink() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem() + let authorization = try fixture.authorization(using: fileSystem) + + try fixture.swapApprovedParentForSymlinkToOutside() + _ = try fileSystem.metadata(for: fixture.approved) + XCTAssertThrowsError(try fileSystem.writeStagedFile( + nextTo: fixture.approved.appending(path: "target"), + data: Data("safe".utf8), + permissions: 0o600, + authorization: authorization + )) + XCTAssertEqual(try FileManager.default.contentsOfDirectory(atPath: fixture.outside.path), []) + } + + func testTemporaryWriteRejectsAuthorizedParentReplacedByDifferentDirectory() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem() + let authorization = try fixture.authorization(using: fileSystem) + + try fixture.swapApprovedParentForFreshDirectory() + _ = try fileSystem.metadata(for: fixture.approved) + XCTAssertThrowsError(try fileSystem.writeStagedFile( + nextTo: fixture.approved.appending(path: "target"), + data: Data("safe".utf8), + permissions: 0o600, + authorization: authorization + )) + XCTAssertEqual(try FileManager.default.contentsOfDirectory(atPath: fixture.approved.path), []) + } + + func testRemoveRejectsParentSwapAndDoesNotUnlinkOutsideFile() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let source = fixture.approved.appending(path: "same-name") + let outside = fixture.outside.appending(path: "same-name") + try Data("approved".utf8).write(to: source) + try Data("outside".utf8).write(to: outside) + let fileSystem = LocalAutomationFileSystem() + let authorization = try fixture.authorization(using: fileSystem) + let sourceAuthorization = try fixture.fileAuthorization( + source, directory: authorization, fileSystem: fileSystem + ) + + try fixture.swapApprovedParentForSymlinkToOutside() + _ = try fileSystem.metadata(for: fixture.approved) + XCTAssertThrowsError(try fileSystem.removeItem(sourceAuthorization)) + XCTAssertEqual(try Data(contentsOf: outside), Data("outside".utf8)) + } + + func testRemoveRejectsLeafReplacementEvenAfterMetadataReadsReplacement() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem() + let directory = try fixture.authorization(using: fileSystem) + let source = fixture.approved.appending(path: "source") + try Data("original".utf8).write(to: source) + let sourceAuthorization = try fixture.fileAuthorization( + source, directory: directory, fileSystem: fileSystem + ) + + try FileManager.default.removeItem(at: source) + try Data("replacement".utf8).write(to: source) + _ = try fileSystem.metadata(for: source) + XCTAssertThrowsError(try fileSystem.removeItem(sourceAuthorization)) + XCTAssertEqual(try Data(contentsOf: source), Data("replacement".utf8)) + } + + func testTrashRejectsLeafReplacementAndRestoresReplacementInPlace() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem() + let directory = try fixture.authorization(using: fileSystem) + let source = fixture.approved.appending(path: "trash-source") + try Data("original".utf8).write(to: source) + let sourceAuthorization = try fixture.fileAuthorization( + source, directory: directory, fileSystem: fileSystem + ) + + try FileManager.default.removeItem(at: source) + try Data("replacement".utf8).write(to: source) + XCTAssertThrowsError(try fileSystem.moveToTrash(sourceAuthorization)) + XCTAssertEqual(try Data(contentsOf: source), Data("replacement".utf8)) + } + + func testReplaceRejectsStagedFileReplacement() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem() + let directory = try fixture.authorization(using: fileSystem) + let destinationURL = fixture.approved.appending(path: "destination") + try Data("destination".utf8).write(to: destinationURL) + let destination = try fixture.fileAuthorization( + destinationURL, directory: directory, fileSystem: fileSystem + ) + let staged = try fileSystem.writeStagedFile( + nextTo: destinationURL, + data: Data("intended".utf8), + permissions: 0o600, + authorization: directory + ) + + try FileManager.default.removeItem(at: staged.url) + try Data("attacker".utf8).write(to: staged.url) + _ = try fileSystem.metadata(for: staged.url) + XCTAssertThrowsError(try fileSystem.replaceItem(at: destination, with: staged)) + XCTAssertEqual(try Data(contentsOf: destinationURL), Data("destination".utf8)) + } + + func testReplaceRejectsSameInodeStagedByteMutationAtCommitBoundary() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem() + let directory = try fixture.authorization(using: fileSystem) + let destinationURL = fixture.approved.appending(path: "destination") + try Data("destination".utf8).write(to: destinationURL) + let destination = try fixture.fileAuthorization( + destinationURL, directory: directory, fileSystem: fileSystem + ) + let staged = try fileSystem.writeStagedFile( + nextTo: destinationURL, + data: Data("reviewed-bytes".utf8), + permissions: 0o600, + authorization: directory + ) + let stagedIdentity = try fileSystem.metadata(for: staged.url).resourceIdentifier + + let handle = try FileHandle(forWritingTo: staged.url) + try handle.truncate(atOffset: 0) + try handle.write(contentsOf: Data("attacker-bytes".utf8)) + try handle.close() + XCTAssertEqual(try fileSystem.metadata(for: staged.url).resourceIdentifier, stagedIdentity) + + XCTAssertThrowsError(try fileSystem.replaceItem(at: destination, with: staged)) + XCTAssertEqual(try Data(contentsOf: destinationURL), Data("destination".utf8)) + } + + func testReplaceRejectsSameInodeStagedModeMutationAtCommitBoundary() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem() + let directory = try fixture.authorization(using: fileSystem) + let destinationURL = fixture.approved.appending(path: "destination") + try Data("destination".utf8).write(to: destinationURL) + let destination = try fixture.fileAuthorization( + destinationURL, directory: directory, fileSystem: fileSystem + ) + let staged = try fileSystem.writeStagedFile( + nextTo: destinationURL, + data: Data("reviewed-bytes".utf8), + permissions: 0o600, + authorization: directory + ) + let stagedIdentity = try fileSystem.metadata(for: staged.url).resourceIdentifier + + try FileManager.default.setAttributes( + [.posixPermissions: 0o644], + ofItemAtPath: staged.url.path + ) + XCTAssertEqual(try fileSystem.metadata(for: staged.url).resourceIdentifier, stagedIdentity) + + XCTAssertThrowsError(try fileSystem.replaceItem(at: destination, with: staged)) + XCTAssertEqual(try Data(contentsOf: destinationURL), Data("destination".utf8)) + } + + func testReplaceRestoresDestinationWhenStagedInodeMutatesAfterPrecheck() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem(hooks: .init( + beforeReplaceCommit: { stagedURL in + let handle = try! FileHandle(forWritingTo: stagedURL) + try! handle.truncate(atOffset: 0) + try! handle.write(contentsOf: Data("attacker-bytes".utf8)) + try! handle.close() + } + )) + let directory = try fixture.authorization(using: fileSystem) + let destinationURL = fixture.approved.appending(path: "destination") + try Data("destination".utf8).write(to: destinationURL) + let destination = try fixture.fileAuthorization( + destinationURL, directory: directory, fileSystem: fileSystem + ) + let staged = try fileSystem.writeStagedFile( + nextTo: destinationURL, + data: Data("reviewed-bytes".utf8), + permissions: 0o600, + authorization: directory + ) + + let outcome = try fileSystem.replaceItem(at: destination, with: staged) + + XCTAssertEqual(outcome, .unchanged) + XCTAssertEqual(try Data(contentsOf: destinationURL), Data("destination".utf8)) + XCTAssertFalse(FileManager.default.fileExists(atPath: staged.url.path)) + } + + func testReplaceRestoresAbsentDestinationAndRemovesExactStagedInode() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem(hooks: .init( + faults: [.replacePostRenameVerification] + )) + let directory = try fixture.authorization(using: fileSystem) + let destinationURL = fixture.approved.appending(path: "destination") + let destination = try XCTUnwrap(AutomationFileAuthorization( + fileURL: destinationURL, + directory: directory, + expectation: .absent + )) + let staged = try fileSystem.writeStagedFile( + nextTo: destinationURL, + data: Data("reviewed".utf8), + permissions: 0o600, + authorization: directory + ) + + XCTAssertEqual(try fileSystem.replaceItem(at: destination, with: staged), .unchanged) + XCTAssertFalse(FileManager.default.fileExists(atPath: destinationURL.path)) + XCTAssertFalse(FileManager.default.fileExists(atPath: staged.url.path)) + } + + func testReplaceRestoreCleanupFailureReportsExactStagedRecovery() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem(hooks: .init( + faults: [.replacePostRenameVerification, .replaceRestoredStagedUnlink] + )) + let directory = try fixture.authorization(using: fileSystem) + let destinationURL = fixture.approved.appending(path: "destination") + let destination = try XCTUnwrap(AutomationFileAuthorization( + fileURL: destinationURL, + directory: directory, + expectation: .absent + )) + let staged = try fileSystem.writeStagedFile( + nextTo: destinationURL, + data: Data("reviewed".utf8), + permissions: 0o600, + authorization: directory + ) + + XCTAssertThrowsError(try fileSystem.replaceItem(at: destination, with: staged)) { error in + let partial = try! XCTUnwrap(error as? AutomationFilePartialMutation) + XCTAssertEqual(partial.kind, .remove) + XCTAssertEqual(partial.commitState, .unknown) + XCTAssertEqual(partial.recoveryHandle, staged.authorization) + XCTAssertEqual(partial.observedFiles, [staged.authorization]) + } + XCTAssertFalse(FileManager.default.fileExists(atPath: destinationURL.path)) + XCTAssertEqual(try Data(contentsOf: staged.url), Data("reviewed".utf8)) + } + + func testDisplacedUnlinkFailureReportsCommittedInstallAndExactRecoveryHandle() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem(hooks: .init( + faults: [.replaceDisplacedUnlink] + )) + let directory = try fixture.authorization(using: fileSystem) + let destinationURL = fixture.approved.appending(path: "destination") + try Data("original".utf8).write(to: destinationURL) + let destination = try fixture.fileAuthorization( + destinationURL, directory: directory, fileSystem: fileSystem + ) + let staged = try fileSystem.writeStagedFile( + nextTo: destinationURL, + data: Data("reviewed".utf8), + permissions: 0o600, + authorization: directory + ) + + XCTAssertThrowsError(try fileSystem.replaceItem(at: destination, with: staged)) { error in + let partial = try! XCTUnwrap(error as? AutomationFilePartialMutation) + XCTAssertEqual(partial.commitState, .committed) + XCTAssertEqual(partial.observedFiles.first?.fileURL, destinationURL) + let recovery = try! XCTUnwrap(partial.recoveryHandle) + XCTAssertEqual(try! fileSystem.read(recovery.fileURL), Data("original".utf8)) + } + XCTAssertEqual(try Data(contentsOf: destinationURL), Data("reviewed".utf8)) + } + + func testFailedRestoreCollisionReportsEveryObservedEntryAndInstalledRecovery() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem(hooks: .init( + faults: [.replacePostRenameVerification, .replaceRestoreCollision] + )) + let directory = try fixture.authorization(using: fileSystem) + let destinationURL = fixture.approved.appending(path: "destination") + let destination = try XCTUnwrap(AutomationFileAuthorization( + fileURL: destinationURL, + directory: directory, + expectation: .absent + )) + let staged = try fileSystem.writeStagedFile( + nextTo: destinationURL, + data: Data("reviewed".utf8), + permissions: 0o600, + authorization: directory + ) + + XCTAssertThrowsError(try fileSystem.replaceItem(at: destination, with: staged)) { error in + let partial = try! XCTUnwrap(error as? AutomationFilePartialMutation) + XCTAssertEqual(partial.commitState, .committed) + XCTAssertEqual(Set(partial.observedFiles.map(\.fileURL)).count, 2) + XCTAssertEqual(partial.recoveryHandle?.fileURL, destinationURL) + } + XCTAssertEqual(try Data(contentsOf: destinationURL), Data("reviewed".utf8)) + } + + func testFailedSwapBackReportsDisplacedOriginalAsExactRecovery() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem(hooks: .init( + beforeReplaceCommit: { stagedURL in + let handle = try! FileHandle(forWritingTo: stagedURL) + try! handle.truncate(atOffset: 0) + try! handle.write(contentsOf: Data("attacker".utf8)) + try! handle.close() + }, + faults: [.replaceSwapBackFailure] + )) + let directory = try fixture.authorization(using: fileSystem) + let destinationURL = fixture.approved.appending(path: "destination") + try Data("original".utf8).write(to: destinationURL) + let destination = try fixture.fileAuthorization( + destinationURL, directory: directory, fileSystem: fileSystem + ) + let staged = try fileSystem.writeStagedFile( + nextTo: destinationURL, + data: Data("reviewed".utf8), + permissions: 0o600, + authorization: directory + ) + + XCTAssertThrowsError(try fileSystem.replaceItem(at: destination, with: staged)) { error in + let partial = try! XCTUnwrap(error as? AutomationFilePartialMutation) + XCTAssertEqual(partial.commitState, .unknown) + let recovery = try! XCTUnwrap(partial.recoveryHandle) + XCTAssertEqual(try! fileSystem.read(recovery.fileURL), Data("original".utf8)) + } + XCTAssertEqual(try Data(contentsOf: destinationURL), Data("attacker".utf8)) + } + + func testTrashFinalMoveFailureReturnsUnchangedOnlyAfterVerifiedRestore() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem(hooks: .init( + faults: [.trashFinalMove], + trashDirectory: fixture.root.appending(path: "trash", directoryHint: .isDirectory) + )) + let directory = try fixture.authorization(using: fileSystem) + let sourceURL = fixture.approved.appending(path: "source") + try Data("original".utf8).write(to: sourceURL) + let source = try fixture.fileAuthorization( + sourceURL, directory: directory, fileSystem: fileSystem + ) + + XCTAssertEqual(try fileSystem.moveToTrash(source), .unchanged) + XCTAssertEqual(try Data(contentsOf: sourceURL), Data("original".utf8)) + } + + func testTrashStagedIdentityMismatchWithFailedRestoreReportsRecovery() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem(hooks: .init( + faults: [.trashStagedIdentityMismatch, .trashRestoreCollision], + trashDirectory: fixture.root.appending(path: "trash", directoryHint: .isDirectory) + )) + let directory = try fixture.authorization(using: fileSystem) + let sourceURL = fixture.approved.appending(path: "source") + try Data("original".utf8).write(to: sourceURL) + let source = try fixture.fileAuthorization( + sourceURL, directory: directory, fileSystem: fileSystem + ) + + XCTAssertThrowsError(try fileSystem.moveToTrash(source)) { error in + let partial = try! XCTUnwrap(error as? AutomationFilePartialMutation) + XCTAssertEqual(partial.kind, .trash) + XCTAssertEqual(partial.commitState, .unknown) + XCTAssertEqual(partial.observedFiles.count, 2) + XCTAssertEqual(try! fileSystem.read(partial.recoveryHandle!.fileURL), Data("original".utf8)) + } + } + + func testRemoveUnlinkFailureReturnsUnchangedOnlyAfterVerifiedRestore() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem(hooks: .init(faults: [.removeUnlink])) + let directory = try fixture.authorization(using: fileSystem) + let sourceURL = fixture.approved.appending(path: "source") + try Data("original".utf8).write(to: sourceURL) + let source = try fixture.fileAuthorization( + sourceURL, directory: directory, fileSystem: fileSystem + ) + + XCTAssertEqual(try fileSystem.removeItem(source), .unchanged) + XCTAssertEqual(try Data(contentsOf: sourceURL), Data("original".utf8)) + } + + func testRemoveStagedIdentityMismatchWithFailedRestoreReportsRecovery() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem(hooks: .init( + faults: [.removeStagedIdentityMismatch, .removeRestoreCollision] + )) + let directory = try fixture.authorization(using: fileSystem) + let sourceURL = fixture.approved.appending(path: "source") + try Data("original".utf8).write(to: sourceURL) + let source = try fixture.fileAuthorization( + sourceURL, directory: directory, fileSystem: fileSystem + ) + + XCTAssertThrowsError(try fileSystem.removeItem(source)) { error in + let partial = try! XCTUnwrap(error as? AutomationFilePartialMutation) + XCTAssertEqual(partial.kind, .remove) + XCTAssertEqual(partial.commitState, .unknown) + XCTAssertEqual(partial.observedFiles.count, 2) + XCTAssertEqual(try! fileSystem.read(partial.recoveryHandle!.fileURL), Data("original".utf8)) + } + } + + func testAbsentDestinationThatAppearsIsNeverOverwritten() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem() + let directory = try fixture.authorization(using: fileSystem) + let destinationURL = fixture.approved.appending(path: "destination") + let destination = try XCTUnwrap(AutomationFileAuthorization( + fileURL: destinationURL, + directory: directory, + expectation: .absent + )) + let staged = try fileSystem.writeStagedFile( + nextTo: destinationURL, + data: Data("intended".utf8), + permissions: 0o600, + authorization: directory + ) + + try Data("attacker".utf8).write(to: destinationURL) + XCTAssertThrowsError(try fileSystem.replaceItem(at: destination, with: staged)) + XCTAssertEqual(try Data(contentsOf: destinationURL), Data("attacker".utf8)) + } + + func testExistingDestinationReplacementIsNeverOverwritten() throws { + let fixture = try FileSystemFixture() + defer { fixture.cleanup() } + let fileSystem = LocalAutomationFileSystem() + let directory = try fixture.authorization(using: fileSystem) + let destinationURL = fixture.approved.appending(path: "destination") + try Data("original".utf8).write(to: destinationURL) + let destination = try fixture.fileAuthorization( + destinationURL, directory: directory, fileSystem: fileSystem + ) + let staged = try fileSystem.writeStagedFile( + nextTo: destinationURL, + data: Data("intended".utf8), + permissions: 0o600, + authorization: directory + ) + + try FileManager.default.removeItem(at: destinationURL) + try Data("attacker".utf8).write(to: destinationURL) + XCTAssertThrowsError(try fileSystem.replaceItem(at: destination, with: staged)) + XCTAssertEqual(try Data(contentsOf: destinationURL), Data("attacker".utf8)) + } +} + +private final class FileSystemFixture { + let root: URL + let approved: URL + let parked: URL + let outside: URL + + init() throws { + root = try FileManager.default.url( + for: .cachesDirectory, + in: .userDomainMask, + appropriateFor: nil, + create: true + ).appending(path: "DevScopeTests", directoryHint: .isDirectory).appending( + path: "devscope-fs-\(UUID().uuidString)", directoryHint: .isDirectory + ) + approved = root.appending(path: "approved", directoryHint: .isDirectory) + parked = root.appending(path: "parked", directoryHint: .isDirectory) + outside = root.appending(path: "outside", directoryHint: .isDirectory) + try FileManager.default.createDirectory(at: approved, withIntermediateDirectories: true) + try FileManager.default.createDirectory(at: outside, withIntermediateDirectories: true) + } + + func authorization( + using fileSystem: LocalAutomationFileSystem + ) throws -> AutomationDirectoryAuthorization { + let metadata = try fileSystem.metadata(for: approved) + return try XCTUnwrap(AutomationDirectoryAuthorization( + directoryURL: approved, + resourceIdentifier: metadata.resourceIdentifier + )) + } + + func fileAuthorization( + _ file: URL, + directory: AutomationDirectoryAuthorization, + fileSystem: LocalAutomationFileSystem + ) throws -> AutomationFileAuthorization { + let metadata = try fileSystem.metadata(for: file) + return try XCTUnwrap(AutomationFileAuthorization( + fileURL: file, + directory: directory, + expectation: .existing(resourceIdentifier: metadata.resourceIdentifier ?? "") + )) + } + + func swapApprovedParentForSymlinkToOutside() throws { + try FileManager.default.moveItem(at: approved, to: parked) + try FileManager.default.createSymbolicLink(at: approved, withDestinationURL: outside) + } + + func swapApprovedParentForFreshDirectory() throws { + try FileManager.default.moveItem(at: approved, to: parked) + try FileManager.default.createDirectory(at: approved, withIntermediateDirectories: false) + } + + func cleanup() { + try? FileManager.default.removeItem(at: root) + } +} diff --git a/Tests/DevScopeTests/ProcessSelectionPolicyTests.swift b/Tests/DevScopeTests/ProcessSelectionPolicyTests.swift new file mode 100644 index 0000000..344db45 --- /dev/null +++ b/Tests/DevScopeTests/ProcessSelectionPolicyTests.swift @@ -0,0 +1,112 @@ +import DevScopeCore +import XCTest +@testable import DevScope + +final class ProcessSelectionPolicyTests: XCTestCase { + func testPIDReuseClearsSelectionInsteadOfSelectingTheReplacementProcess() { + let original = item(pid: 42, birth: 100, cpu: 1) + let replacement = item(pid: 42, birth: 200, cpu: 2) + + let result = ProcessSelectionPolicy.reconcile( + selectedProcessID: 42, + retainedProcess: original, + currentProcesses: [replacement] + ) + + XCTAssertNil(result.selectedProcessID) + XCTAssertNil(result.retainedProcess) + } + + func testSameBirthRefreshesTheRetainedSelectionWithCurrentMetrics() { + let original = item(pid: 42, birth: 100, cpu: 1) + let refreshed = item(pid: 42, birth: 100, cpu: 9) + + let result = ProcessSelectionPolicy.reconcile( + selectedProcessID: 42, + retainedProcess: original, + currentProcesses: [refreshed] + ) + + XCTAssertEqual(result.selectedProcessID, 42) + XCTAssertEqual(result.retainedProcess, refreshed) + } + + func testSelectingDifferentPIDAdoptsTheNewProcessInsteadOfComparingOldIdentity() { + let previouslySelected = item(pid: 41, birth: 100, cpu: 1) + let newlySelected = item(pid: 42, birth: 200, cpu: 2) + + let result = ProcessSelectionPolicy.reconcile( + selectedProcessID: 42, + retainedProcess: previouslySelected, + currentProcesses: [previouslySelected, newlySelected] + ) + + XCTAssertEqual(result.selectedProcessID, 42) + XCTAssertEqual(result.retainedProcess, newlySelected) + } + + func testMissingCurrentProcessPreservesTheEndedSelectionForInspection() { + let original = item(pid: 42, birth: 100, cpu: 1) + + let result = ProcessSelectionPolicy.reconcile( + selectedProcessID: 42, + retainedProcess: original, + currentProcesses: [] + ) + + XCTAssertEqual(result.selectedProcessID, 42) + XCTAssertEqual(result.retainedProcess, original) + } + + func testSelectionClearsWhenOnlyOneSnapshotHasVerifiableBirthIdentity() { + let known = item(pid: 42, birth: 100, cpu: 1) + let unknown = item(pid: 42, birth: nil, cpu: 2) + + for (retained, current) in [(known, unknown), (unknown, known)] { + let result = ProcessSelectionPolicy.reconcile( + selectedProcessID: 42, + retainedProcess: retained, + currentProcesses: [current] + ) + XCTAssertNil(result.selectedProcessID) + XCTAssertNil(result.retainedProcess) + } + } + + func testSelectionClearsWhenNeitherSnapshotHasVerifiableBirthIdentity() { + let retained = item(pid: 42, birth: nil, cpu: 1) + let current = item(pid: 42, birth: nil, cpu: 2) + + let result = ProcessSelectionPolicy.reconcile( + selectedProcessID: 42, + retainedProcess: retained, + currentProcesses: [current] + ) + + XCTAssertNil(result.selectedProcessID) + XCTAssertNil(result.retainedProcess) + } + + private func item(pid: Int32, birth: UInt64?, cpu: Double) -> ClassifiedDevProcess { + ClassifiedDevProcess( + process: DevProcess( + pid: pid, + parentPID: 1, + executable: "/bin/sleep", + command: "/bin/sleep 10", + resourceUsage: DevProcessResourceUsage( + cpuPercent: cpu, + residentMemoryBytes: 1, + elapsedTime: "00:10" + ), + birthToken: birth.map { ProcessBirthToken(seconds: $0, microseconds: 1) } + ), + classification: DevProcessClassification( + kind: .other, + displayName: "sleep", + projectHint: nil, + tags: [] + ) + ) + } +} diff --git a/Tests/DevScopeTests/ProcessStoreAutomationTests.swift b/Tests/DevScopeTests/ProcessStoreAutomationTests.swift new file mode 100644 index 0000000..e49afa6 --- /dev/null +++ b/Tests/DevScopeTests/ProcessStoreAutomationTests.swift @@ -0,0 +1,199 @@ +import DevScopeCore +import Foundation +import XCTest +@testable import DevScope + +@MainActor +final class ProcessStoreAutomationTests: XCTestCase { + func testManualRefreshRequestedDuringAutomaticScanRunsAsTrailingFullRefresh() async { + let scanner = SequencedBlockingProcessScanner(result: []) + let store = ProcessStore(scanner: scanner, gpuMetricProvider: NilGPUMetricProvider()) + store.usesAppleNaming = false + + store.refresh() + let firstScanStarted = await scanner.waitUntilCallCount(1) + XCTAssertTrue(firstScanStarted) + scanner.releaseCall(0) + await eventuallyProcessStore { !store.isRefreshing } + + store.refresh(isAutomatic: true) + let automaticScanStarted = await scanner.waitUntilCallCount(2) + XCTAssertTrue(automaticScanStarted) + store.refresh() + scanner.releaseCall(1) + + let trailingManualScanStarted = await scanner.waitUntilCallCount(3) + XCTAssertTrue(trailingManualScanStarted) + scanner.releaseCall(2) + await eventuallyProcessStore { !store.isRefreshing } + + XCTAssertEqual(scanner.includeCurrentDirectoryRequests, [true, false, true]) + } + + func testContextChangedDuringInFlightScanWinsAtSinglePublication() async { + let process = DevProcess( + pid: 8_001, + parentPID: 1, + executable: "/bin/sleep", + command: "/bin/sleep 20000", + argumentVector: ["/bin/sleep", "20000"], + resourceUsage: DevProcessResourceUsage( + cpuPercent: 0, + residentMemoryBytes: 1, + elapsedTime: "05:00:00" + ), + birthToken: ProcessBirthToken(seconds: 500, microseconds: 1), + launchLabel: "com.example.context-race" + ) + let scanner = BlockingProcessScanner(result: [process]) + let store = ProcessStore(scanner: scanner, gpuMetricProvider: NilGPUMetricProvider()) + store.usesAppleNaming = false + + store.refresh() + await scanner.waitUntilStarted() + let inventory = AutomationInventorySnapshot( + generation: 12, + records: [automationRecordForContextRace()], + health: [.launchAgent: AutomationSourceHealth( + kind: .launchAgent, + state: .healthy, + message: nil, + refreshedAt: Date(timeIntervalSince1970: 12) + )], + refreshedAt: Date(timeIntervalSince1970: 12) + ) + store.updateAutomationContext(inventory: inventory, longRunningThreshold: 1) + scanner.release() + await eventuallyProcessStore { !store.isRefreshing } + + XCTAssertEqual(store.liveSnapshot.automationInventory.generation, 12) + XCTAssertEqual( + store.liveSnapshot.automationLinksByProcessID[process.pid]?.recordID, + inventory.records[0].id + ) + XCTAssertEqual(store.liveSnapshot.longRunningProcessIDs, [process.pid]) + XCTAssertEqual(store.liveSnapshot.processes.map(\.pid), [process.pid]) + } +} + +private final class SequencedBlockingProcessScanner: ProcessProviding, @unchecked Sendable { + private let condition = NSCondition() + private let result: [DevProcess] + private var requests: [Bool] = [] + private var releasedCalls: Set = [] + + init(result: [DevProcess]) { + self.result = result + } + + var includeCurrentDirectoryRequests: [Bool] { + condition.withLock { requests } + } + + func snapshot(includeCurrentDirectories: Bool) throws -> [DevProcess] { + condition.lock() + let callIndex = requests.count + requests.append(includeCurrentDirectories) + condition.broadcast() + while !releasedCalls.contains(callIndex) { + condition.wait() + } + condition.unlock() + return result + } + + func waitUntilCallCount( + _ expectedCount: Int, + timeout: Duration = .seconds(1) + ) async -> Bool { + let deadline = ContinuousClock.now + timeout + while ContinuousClock.now < deadline { + if condition.withLock({ requests.count >= expectedCount }) { + return true + } + try? await Task.sleep(for: .milliseconds(1)) + } + return condition.withLock { requests.count >= expectedCount } + } + + func releaseCall(_ callIndex: Int) { + condition.withLock { + releasedCalls.insert(callIndex) + condition.broadcast() + } + } +} + +private final class BlockingProcessScanner: ProcessProviding, @unchecked Sendable { + private let started = DispatchSemaphore(value: 0) + private let gate = DispatchSemaphore(value: 0) + private let result: [DevProcess] + + init(result: [DevProcess]) { + self.result = result + } + + func snapshot(includeCurrentDirectories: Bool) throws -> [DevProcess] { + started.signal() + gate.wait() + return result + } + + func waitUntilStarted() async { + await withCheckedContinuation { continuation in + DispatchQueue.global().async { + self.started.wait() + continuation.resume() + } + } + } + + func release() { + gate.signal() + } +} + +private struct NilGPUMetricProvider: GPUMetricProviding { + func snapshot() throws -> DevGPUMetric? { nil } +} + +@MainActor +private func eventuallyProcessStore( + _ predicate: @MainActor () -> Bool, + file: StaticString = #filePath, + line: UInt = #line +) async { + for _ in 0..<1_000 where !predicate() { + try? await Task.sleep(for: .milliseconds(1)) + } + if !predicate() { + XCTFail("Timed out waiting for asynchronous process-store state.", file: file, line: line) + } +} + +private func automationRecordForContextRace() -> AutomationRecord { + AutomationRecord( + id: AutomationRecord.ID(rawValue: "context-race-record"), + kind: .launchAgent, + sourceKind: .launchAgent, + label: "com.example.context-race", + displayName: "Context Race", + providerBundleIdentifier: nil, + ownerUID: 501, + ownership: .user, + executable: "/bin/sleep", + arguments: ["20000"], + environment: [:], + workingDirectory: nil, + schedule: AutomationSchedule(triggers: [.runAtLoad], summary: "At load"), + sourceURL: URL(fileURLWithPath: "/tmp/com.example.context-race.plist"), + sourceChecksum: "context-race", + enabledState: .enabled, + loadState: .loaded, + approvalState: .notApplicable, + state: .running, + evidence: [], + capabilities: [.exportRecord], + validationFindings: [] + ) +} diff --git a/Tests/DevScopeTests/SystemAutomationCommandRunnerTests.swift b/Tests/DevScopeTests/SystemAutomationCommandRunnerTests.swift new file mode 100644 index 0000000..ee078ec --- /dev/null +++ b/Tests/DevScopeTests/SystemAutomationCommandRunnerTests.swift @@ -0,0 +1,317 @@ +import Darwin +import Foundation +import XCTest +@testable import DevScope +import DevScopeCore + +final class SystemAutomationCommandRunnerTests: XCTestCase { + func testOrdinaryCommandCapturesBothStreamsEnvironmentAndExitStatus() async throws { + let result = try await SystemAutomationCommandRunner().run(AutomationCommand( + executable: "/bin/sh", + arguments: [ + "-c", + "printf '%s' \"$DEVSCOPE_RUNNER_TEST\"; printf 'stderr-value' >&2; exit 7", + ], + environment: ["DEVSCOPE_RUNNER_TEST": "stdout-value"] + )) + + XCTAssertEqual(result.status, 7) + XCTAssertEqual(String(data: result.standardOutput, encoding: .utf8), "stdout-value") + XCTAssertEqual(String(data: result.standardError, encoding: .utf8), "stderr-value") + } + + func testCaptureLimitDrainsAllOutputThenRejectsPartialResult() async throws { + let processGroup = LockedValue(nil) + let runner = SystemAutomationCommandRunner( + hooks: .init(afterSpawnBeforeInstall: { processGroup.set($0) }), + maximumCapturedBytes: 64 + ) + let fallback = cleanupFallback(processGroup) + defer { + fallback.cancel() + killGroupIfPresent(processGroup.value) + } + let started = ContinuousClock.now + + do { + _ = try await runner.run(AutomationCommand( + executable: "/bin/sh", + arguments: [ + "-c", + "/usr/bin/yes 1234567890 | /usr/bin/head -c 200000", + ] + )) + XCTFail("Expected outputLimitExceeded") + } catch SystemAutomationCommandError.outputLimitExceeded { + // Expected only after the output pipe reaches EOF. + } catch { + XCTFail("Expected outputLimitExceeded, received \(error)") + } + + XCTAssertLessThan(ContinuousClock.now - started, .seconds(4)) + await assertProcessGone(processGroup.value) + } + + func testExecutionTimeoutTerminatesTheSpawnedProcessGroupAndFailsClosed() async throws { + let processGroup = LockedValue(nil) + let runner = SystemAutomationCommandRunner( + hooks: .init(afterSpawnBeforeInstall: { processGroup.set($0) }), + executionTimeout: .milliseconds(50) + ) + let fallback = cleanupFallback(processGroup) + defer { + fallback.cancel() + killGroupIfPresent(processGroup.value) + } + let started = ContinuousClock.now + + do { + _ = try await runner.run(AutomationCommand( + executable: "/bin/sleep", + arguments: ["30"] + )) + XCTFail("Expected executionTimedOut") + } catch SystemAutomationCommandError.executionTimedOut { + // Expected after the runner terminates and reaps the process group. + } catch { + XCTFail("Expected executionTimedOut, received \(error)") + } + + XCTAssertLessThan(ContinuousClock.now - started, .seconds(4)) + await assertProcessGone(processGroup.value) + } + + func testAlreadyCancelledTaskDoesNotSpawnAChild() async throws { + let gate = DispatchSemaphore(value: 0) + let spawnCount = LockedValue(0) + let runner = SystemAutomationCommandRunner(hooks: .init( + afterSpawnBeforeInstall: { _ in spawnCount.mutate { $0 += 1 } } + )) + let task = Task { + _ = waitSynchronously(gate, timeout: .now() + 5) + return try await runner.run(AutomationCommand(executable: "/bin/true", arguments: [])) + } + + task.cancel() + gate.signal() + await assertCancellation(from: task) + XCTAssertEqual(spawnCount.value, 0) + } + + func testCancellationRequestedBeforePIDInstallationStillTerminatesSpawnedGroup() async throws { + let spawned = DispatchSemaphore(value: 0) + let allowInstallation = DispatchSemaphore(value: 0) + let processGroup = LockedValue(nil) + let runner = SystemAutomationCommandRunner(hooks: .init( + afterSpawnBeforeInstall: { pid in + processGroup.set(pid) + spawned.signal() + _ = allowInstallation.wait(timeout: .now() + 5) + } + )) + let task = Task { + try await runner.run(AutomationCommand(executable: "/bin/sleep", arguments: ["30"])) + } + + XCTAssertEqual(spawned.wait(timeout: .now() + 5), .success) + let fallback = cleanupFallback(processGroup) + defer { + fallback.cancel() + killGroupIfPresent(processGroup.value) + } + task.cancel() + allowInstallation.signal() + + let started = ContinuousClock.now + await assertCancellation(from: task) + XCTAssertLessThan(ContinuousClock.now - started, .seconds(4)) + await assertProcessGone(processGroup.value) + } + + func testCancellationEscalatesFromTERMToKILLForTermIgnoringProcessGroup() async throws { + let installed = DispatchSemaphore(value: 0) + let processGroup = LockedValue(nil) + let runner = SystemAutomationCommandRunner(hooks: .init( + afterInstall: { pid in + processGroup.set(pid) + installed.signal() + } + )) + let task = Task { + try await runner.run(AutomationCommand( + executable: "/bin/sh", + arguments: ["-c", "trap '' TERM; while :; do sleep 1; done"] + )) + } + + XCTAssertEqual(installed.wait(timeout: .now() + 5), .success) + let fallback = cleanupFallback(processGroup) + defer { + fallback.cancel() + killGroupIfPresent(processGroup.value) + } + let started = ContinuousClock.now + task.cancel() + await assertCancellation(from: task) + let elapsed = ContinuousClock.now - started + XCTAssertGreaterThanOrEqual(elapsed, .seconds(1.8)) + XCTAssertLessThan(elapsed, .seconds(4)) + await assertProcessGone(processGroup.value) + } + + func testCancellationKillsPipeHoldingDescendantAfterLeaderExits() async throws { + let leaderExited = DispatchSemaphore(value: 0) + let processGroup = LockedValue(nil) + let pidFile = FileManager.default.temporaryDirectory + .appendingPathComponent("devscope-runner-child-\(UUID().uuidString)") + defer { try? FileManager.default.removeItem(at: pidFile) } + let runner = SystemAutomationCommandRunner(hooks: .init( + afterSpawnBeforeInstall: { processGroup.set($0) }, + afterLeaderExitObserved: { _ in leaderExited.signal() } + )) + let task = Task { + try await runner.run(AutomationCommand( + executable: "/bin/sh", + arguments: [ + "-c", + "(trap '' TERM; exec /bin/sleep 30) & child=$!; echo $child > \(pidFile.path); exit 0", + ] + )) + } + + XCTAssertEqual(leaderExited.wait(timeout: .now() + 5), .success) + let descendantPID = try XCTUnwrap(readPID(from: pidFile)) + let fallback = cleanupFallback(processGroup) + defer { + fallback.cancel() + killGroupIfPresent(processGroup.value) + _ = Darwin.kill(descendantPID, SIGKILL) + } + let started = ContinuousClock.now + task.cancel() + await assertCancellation(from: task) + XCTAssertLessThan(ContinuousClock.now - started, .seconds(4)) + await assertProcessGone(descendantPID) + } + + func testExecutionTimeoutKillsPipeHoldingDescendantAfterLeaderExits() async throws { + let processGroup = LockedValue(nil) + let descendantPID = LockedValue(nil) + let pidFile = FileManager.default.temporaryDirectory + .appendingPathComponent("devscope-runner-timeout-child-\(UUID().uuidString)") + defer { try? FileManager.default.removeItem(at: pidFile) } + let runner = SystemAutomationCommandRunner( + hooks: .init(afterSpawnBeforeInstall: { pid in + processGroup.set(pid) + descendantPID.set(waitForPID(from: pidFile, deadline: .now() + .seconds(2))) + }), + executionTimeout: .milliseconds(50) + ) + let fallback = cleanupFallback(processGroup) + defer { + fallback.cancel() + killGroupIfPresent(processGroup.value) + if let descendantPID = descendantPID.value { + _ = Darwin.kill(descendantPID, SIGKILL) + } + } + let started = ContinuousClock.now + + do { + _ = try await runner.run(AutomationCommand( + executable: "/bin/sh", + arguments: [ + "-c", + "(trap '' TERM; exec /bin/sleep 30) & child=$!; echo $child > \(pidFile.path); exit 0", + ] + )) + XCTFail("Expected executionTimedOut") + } catch SystemAutomationCommandError.executionTimedOut { + // Expected after the deadline terminates the pipe-holding descendant. + } catch { + XCTFail("Expected executionTimedOut, received \(error)") + } + + XCTAssertLessThan(ContinuousClock.now - started, .seconds(4)) + await assertProcessGone(try XCTUnwrap(descendantPID.value)) + } + + private func assertCancellation(from task: Task) async { + do { + _ = try await task.value + XCTFail("Expected CancellationError") + } catch is CancellationError { + // Expected. + } catch { + XCTFail("Expected CancellationError, received \(error)") + } + } + + private func cleanupFallback(_ processGroup: LockedValue) -> Task { + Task.detached { + try? await Task.sleep(for: .seconds(5)) + guard !Task.isCancelled else { return } + killGroupIfPresent(processGroup.value) + } + } + + private func assertProcessGone(_ pid: pid_t?) async { + guard let pid else { + XCTFail("Missing spawned PID") + return + } + for _ in 0..<100 { + errno = 0 + if Darwin.kill(pid, 0) == -1, errno == ESRCH { return } + try? await Task.sleep(for: .milliseconds(20)) + } + XCTFail("Process \(pid) was not reaped") + } + +} + +private final class LockedValue: @unchecked Sendable { + private let lock = NSLock() + private var storage: Value + + init(_ value: Value) { + storage = value + } + + var value: Value { lock.withLock { storage } } + + func set(_ value: Value) { + lock.withLock { storage = value } + } + + func mutate(_ body: (inout Value) -> Void) { + lock.withLock { body(&storage) } + } +} + +private func killGroupIfPresent(_ processGroup: pid_t?) { + guard let processGroup, processGroup > 0 else { return } + _ = Darwin.kill(-processGroup, SIGKILL) +} + +private func waitSynchronously( + _ semaphore: DispatchSemaphore, + timeout: DispatchTime +) -> DispatchTimeoutResult { + semaphore.wait(timeout: timeout) +} + +private func waitForPID(from url: URL, deadline: DispatchTime) -> pid_t? { + repeat { + if let pid = readPID(from: url) { return pid } + Darwin.usleep(1_000) + } while DispatchTime.now() < deadline + return readPID(from: url) +} + +private func readPID(from url: URL) -> pid_t? { + guard let text = try? String(contentsOf: url, encoding: .utf8), + let pid = pid_t(text.trimmingCharacters(in: .whitespacesAndNewlines)) + else { return nil } + return pid +} diff --git a/Tests/DevScopeTests/TableSortStateTests.swift b/Tests/DevScopeTests/TableSortStateTests.swift new file mode 100644 index 0000000..81ccf43 --- /dev/null +++ b/Tests/DevScopeTests/TableSortStateTests.swift @@ -0,0 +1,301 @@ +import DevScopeCore +import XCTest +@testable import DevScope + +final class TableSortStateTests: XCTestCase { + func testAutomationRunsStartsDescendingAndNameStartsAscending() { + var state = AutomationTableSortState() + state.activate(.runs) + XCTAssertEqual(state.direction, .descending) + state.activate(.automation) + XCTAssertEqual(state.direction, .ascending) + } + + func testAutomationStateSortUsesSemanticRank() { + let rows = [ + makeAutomationRow(id: "unresolved", state: .unresolved), + makeAutomationRow(id: "disabled", state: .disabled), + makeAutomationRow(id: "idle", state: .idle), + makeAutomationRow(id: "invalid", state: .invalid), + makeAutomationRow(id: "running", state: .running), + makeAutomationRow(id: "needs-approval", state: .needsApproval) + ] + + XCTAssertEqual( + AutomationTableSortState(column: .state, direction: .ascending) + .sorted(rows).map(\.record.state), + [.running, .idle, .needsApproval, .disabled, .invalid, .unresolved] + ) + } + + func testAutomationRunsSortsDescendingByLinkedProcessCount() { + let rows = [ + makeAutomationRow(id: "none", linkedProcessCount: 0), + makeAutomationRow(id: "many", linkedProcessCount: 5), + makeAutomationRow(id: "some", linkedProcessCount: 2) + ] + + XCTAssertEqual( + AutomationTableSortState(column: .runs, direction: .descending) + .sorted(rows).map(\.linkedProcessCount), + [5, 2, 0] + ) + } + + func testAutomationTextColumnsSortByPresentedText() { + let rows = [ + makeAutomationRow(id: "two", displayName: "Zulu", trigger: "At login"), + makeAutomationRow(id: "one", displayName: "Alpha", trigger: "Weekly") + ] + + XCTAssertEqual( + AutomationTableSortState(column: .automation, direction: .ascending) + .sorted(rows).map(\.id.rawValue), + ["one", "two"] + ) + XCTAssertEqual( + AutomationTableSortState(column: .trigger, direction: .ascending) + .sorted(rows).map(\.id.rawValue), + ["two", "one"] + ) + } + + func testAutomationKindAndOwnerSortUseSemanticRanks() { + let kindRows = [ + makeAutomationRow(id: "a", kind: .cron), + makeAutomationRow(id: "b", kind: .backgroundItem), + makeAutomationRow(id: "c", kind: .loginItem), + makeAutomationRow(id: "d", kind: .launchDaemon), + makeAutomationRow(id: "e", kind: .launchAgent) + ] + let ownerRows = [ + makeAutomationRow(id: "a", ownership: .appleSystem), + makeAutomationRow(id: "b", ownership: .managed), + makeAutomationRow(id: "c", ownership: .thirdPartySystem), + makeAutomationRow(id: "d", ownership: .user) + ] + + XCTAssertEqual( + AutomationTableSortState(column: .kind, direction: .ascending) + .sorted(kindRows).map(\.record.kind), + [.launchAgent, .launchDaemon, .loginItem, .backgroundItem, .cron] + ) + XCTAssertEqual( + AutomationTableSortState(column: .owner, direction: .ascending) + .sorted(ownerRows).map(\.record.ownership), + [.user, .thirdPartySystem, .managed, .appleSystem] + ) + } + + func testAutomationSortPreservesIdentityAndUsesRecordIDForTies() { + let rows = [ + makeAutomationRow(id: "c", linkedProcessCount: 1), + makeAutomationRow(id: "a", linkedProcessCount: 1), + makeAutomationRow(id: "b", linkedProcessCount: 1) + ] + let sorted = AutomationTableSortState(column: .runs, direction: .descending).sorted(rows) + let selectedID = AutomationRecord.ID(rawValue: "b") + + XCTAssertEqual(AutomationTableSortState().sorted(rows).map(\.id.rawValue), ["c", "a", "b"]) + XCTAssertEqual(sorted.map(\.id.rawValue), ["a", "b", "c"]) + XCTAssertEqual(Set(sorted.map(\.id)), Set(rows.map(\.id))) + XCTAssertEqual( + AutomationPresentation.resolvedSelection(current: selectedID, visibleIDs: sorted.map(\.id)), + selectedID + ) + } + + func testProcessSortStartsInSnapshotOrderWithoutAnActiveHeader() { + let state = ProcessTableSortState() + XCTAssertNil(state.column) + XCTAssertEqual(state.direction, .ascending) + } + + func testDefaultComparatorsPreserveSnapshotOrder() { + let rows = [ + processRow(pid: 30, snapshotOrder: 2), + processRow(pid: 10, snapshotOrder: 0), + processRow(pid: 20, snapshotOrder: 1) + ] + + XCTAssertEqual(sortedRows(rows, using: ProcessTableSortState()).map(\.pid), [10, 20, 30]) + } + + func testFirstActivationSortsEveryProcessColumnInItsSemanticDefaultDirection() { + let rows = processSortingRows() + let expectations: [(ProcessTableSortColumn, [Int])] = [ + (.process, [0, 1, 2]), + (.pid, [0, 2, 1]), + (.cpu, [1, 0, 2]), + (.memory, [1, 2, 0]), + (.time, [2, 0, 1]), + (.command, [2, 1, 0]) + ] + + for (column, expectedSnapshotOrder) in expectations { + var state = ProcessTableSortState() + state.activate(column) + + XCTAssertEqual( + sortedRows(rows, using: state).map(\.snapshotOrder), + expectedSnapshotOrder, + "Unexpected first-activation order for \(column)" + ) + } + } + + func testRepeatActivationReversesActualRowOrder() { + let rows = processSortingRows() + var state = ProcessTableSortState() + state.activate(.memory) + let firstOrder = sortedRows(rows, using: state).map(\.snapshotOrder) + + state.activate(.memory) + + XCTAssertEqual( + sortedRows(rows, using: state).map(\.snapshotOrder), + Array(firstOrder.reversed()) + ) + } + + func testEqualPrimaryValuesUseDeterministicStableTieOrder() { + let rows = [ + processRow(pid: 30, memory: 100, snapshotOrder: 2), + processRow(pid: 10, memory: 100, snapshotOrder: 0), + processRow(pid: 20, memory: 100, snapshotOrder: 1) + ] + var state = ProcessTableSortState() + state.activate(.memory) + + XCTAssertEqual(sortedRows(rows, using: state).map(\.pid), [10, 20, 30]) + } + + func testProcessMetricSortStartsDescendingAndReverses() { + var state = ProcessTableSortState() + state.activate(.cpu) + XCTAssertEqual(state.column, .cpu) + XCTAssertEqual(state.direction, .descending) + state.activate(.cpu) + XCTAssertEqual(state.direction, .ascending) + } + + func testProcessTextSortStartsAscendingWhenChangingColumns() { + var state = ProcessTableSortState(column: .memory, direction: .ascending) + state.activate(.process) + XCTAssertEqual(state.column, .process) + XCTAssertEqual(state.direction, .ascending) + } +} + +private func makeAutomationRow( + id: String, + displayName: String? = nil, + kind: AutomationKind = .launchAgent, + state: AutomationState = .idle, + ownership: AutomationOwnership = .user, + trigger: String = "On demand", + linkedProcessCount: Int = 0 +) -> AutomationTableRow { + let record = AutomationRecord( + id: .init(rawValue: id), + kind: kind, + sourceKind: kind == .cron ? .crontab : .launchAgent, + label: id, + displayName: displayName ?? id, + providerBundleIdentifier: nil, + ownerUID: 501, + ownership: ownership, + executable: "/bin/true", + arguments: [], + environment: [:], + workingDirectory: nil, + schedule: .init(triggers: [.demand], summary: trigger), + sourceURL: URL(fileURLWithPath: "/tmp/\(id).plist"), + sourceChecksum: nil, + enabledState: .enabled, + loadState: .unknown, + approvalState: .notApplicable, + state: state, + evidence: [], + capabilities: [], + validationFindings: [] + ) + return AutomationTableRow(record: record, linkedProcessCount: linkedProcessCount) +} + +private func processSortingRows() -> [ProcessTableRow] { + [ + processRow( + pid: 10, + title: "Alpha", + cpu: 20, + memory: 100, + elapsedTime: "00:00:20", + command: "charlie", + snapshotOrder: 0 + ), + processRow( + pid: 30, + title: "Beta", + cpu: 30, + memory: 300, + elapsedTime: "00:00:10", + command: "bravo", + snapshotOrder: 1 + ), + processRow( + pid: 20, + title: "Charlie", + cpu: 10, + memory: 200, + elapsedTime: "00:00:30", + command: "alpha", + snapshotOrder: 2 + ) + ] +} + +private func processRow( + pid: Int32, + title: String = "Process", + cpu: Double = 0, + memory: Int64 = 0, + elapsedTime: String = "00:00:00", + command: String = "command", + snapshotOrder: Int +) -> ProcessTableRow { + ProcessTableRow( + item: ClassifiedDevProcess( + process: DevProcess( + pid: pid, + parentPID: 1, + executable: title, + command: command, + resourceUsage: DevProcessResourceUsage( + cpuPercent: cpu, + residentMemoryBytes: memory, + elapsedTime: elapsedTime + ) + ), + classification: DevProcessClassification( + kind: .javascript, + displayName: title, + projectHint: nil + ) + ), + title: title, + isFavorite: false, + isWatched: false, + automationBadges: [], + snapshotOrder: snapshotOrder + ) +} + +private func sortedRows( + _ rows: [ProcessTableRow], + using state: ProcessTableSortState +) -> [ProcessTableRow] { + rows.sorted(using: state.comparators + [ + KeyPathComparator(\ProcessTableRow.stableOrderKey, order: .forward) + ]) +} diff --git a/Tests/DevScopeTests/WorkflowIntelligenceEnhancerTests.swift b/Tests/DevScopeTests/WorkflowIntelligenceEnhancerTests.swift new file mode 100644 index 0000000..87d25cd --- /dev/null +++ b/Tests/DevScopeTests/WorkflowIntelligenceEnhancerTests.swift @@ -0,0 +1,42 @@ +import DevScopeCore +import XCTest +@testable import DevScope + +final class WorkflowIntelligenceEnhancerTests: XCTestCase { + func testCacheKeyChangesWhenWorkflowFactsChangeUnderTheSameStableID() { + let baseline = workflow(cpu: 10, memory: 128 * 1_024 * 1_024, processIDs: [41]) + let changedUsage = workflow(cpu: 85, memory: 5 * 1_024 * 1_024 * 1_024, processIDs: [41]) + let changedComposition = workflow(cpu: 10, memory: 128 * 1_024 * 1_024, processIDs: [41, 42]) + + XCTAssertNotEqual( + WorkflowNoteCacheKey(workflow: baseline, items: []), + WorkflowNoteCacheKey(workflow: changedUsage, items: []) + ) + XCTAssertNotEqual( + WorkflowNoteCacheKey(workflow: baseline, items: []), + WorkflowNoteCacheKey(workflow: changedComposition, items: []) + ) + XCTAssertEqual( + WorkflowNoteCacheKey(workflow: baseline, items: []), + WorkflowNoteCacheKey(workflow: baseline, items: []) + ) + } + + private func workflow(cpu: Double, memory: Int64, processIDs: [Int32]) -> DevWorkflow { + DevWorkflow( + id: "workflow:stable", + title: "Stable Workflow", + subtitle: "facts", + kind: .buildWorkspace, + processIDs: processIDs, + primaryProject: "DevScope", + tags: [], + totalCPU: cpu, + totalMemoryBytes: memory, + risk: cpu >= 80 ? .heavy : .normal, + confidence: 0.9, + summary: "Current workflow facts.", + suggestedAction: "Inspect it." + ) + } +} diff --git a/config/DevScope.entitlements b/config/DevScope.entitlements new file mode 100644 index 0000000..48da0c8 --- /dev/null +++ b/config/DevScope.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-write + + + diff --git a/config/DevScopeDeveloperID.entitlements b/config/DevScopeDeveloperID.entitlements new file mode 100644 index 0000000..8aa5d5d --- /dev/null +++ b/config/DevScopeDeveloperID.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.automation.apple-events + + + diff --git a/docs/APP_STORE_READINESS.md b/docs/APP_STORE_READINESS.md new file mode 100644 index 0000000..96f24f5 --- /dev/null +++ b/docs/APP_STORE_READINESS.md @@ -0,0 +1,144 @@ +# DevScope Mac App Store Readiness + +DevScope now has a reproducible Mac App Store preparation path, but the current product scope is **not ready for Mac App Store submission** because App Sandbox blocks the core process scanner in local validation. + +Local macOS 27 validation compared the full-access and sandbox policies. The +full-access candidate populated process and automation inventory, while the +sandbox candidate showed no running processes and an explicit blocked state. +This confirms the distribution conflict on the target OS and SDK rather than +resolving it. + +Current evidence: + +- The sandbox-signed release bundle builds, signs, validates, and launches. +- The first sandboxed scanner path using `/bin/ps` is blocked with an operation-not-permitted error. +- A native `libproc` fallback was added and validated by unit tests outside the sandbox. +- Inside the sandboxed `.app`, the native fallback still returns no visible process records. +- The sandboxed app therefore reports `0 tracked processes`. + +The final upload also depends on Apple account assets that must exist on the release Mac: + +- a Mac App Store application signing identity (installed on the audited Mac) +- a Mac App Store installer signing identity (installed on the audited Mac) +- a provisioning profile for the final bundle identifier +- an App Store Connect app record using the same bundle identifier + +The audited Mac has no provisioning profile matching `com.s1korrrr.DevScope`; the App Store Connect record has not been verified. + +The current recommended bundle identifier is: + +```bash +com.s1korrrr.DevScope +``` + +## What Is Prepared + +- App Sandbox entitlement: `config/DevScope.entitlements` +- Privacy manifest: `Resources/PrivacyInfo.xcprivacy` +- App icon generation: `script/generate_app_icon.swift` +- Release bundle builder: `script/build_release_bundle.sh` +- Release validator: `script/validate_release_bundle.sh` +- Sandboxed local launch smoke: `script/sandbox_smoke.sh` +- Mac App Store package builder: `script/package_app_store.sh` + +The release bundle contains: + +- `Contents/Info.plist` +- `Contents/MacOS/DevScope` +- `Contents/Resources/AppIcon.icns` +- `Contents/Resources/PrivacyInfo.xcprivacy` +- App Sandbox entitlements in the code signature + +## Local Sandbox Validation + +Run this before attempting App Store signing: + +```bash +./script/sandbox_smoke.sh +``` + +Expected result: + +```text +Validated .../dist/release/DevScope.app +Sandbox: enabled +Sandbox smoke passed: .../dist/release/DevScope.app +``` + +This proves the app can be built, signed with sandbox entitlements, validated, and launched locally. It does **not** prove App Store readiness. The current sandbox smoke shows that both shell-based process scanning and native fallback process scanning are blocked or empty, which is a release blocker for the full DevScope feature set. + +## App Store Package Build + +After creating/downloading the App Store signing assets, this command builds the package. Do not upload it until the sandbox compatibility blocker is resolved. + +```bash +DEVSCOPE_BUNDLE_ID="com.s1korrrr.DevScope" \ +DEVSCOPE_MARKETING_VERSION="0.1.0" \ +DEVSCOPE_BUILD_VERSION="1" \ +DEVSCOPE_APP_STORE_SIGN_IDENTITY="Apple Distribution: YOUR NAME (TEAMID)" \ +DEVSCOPE_INSTALLER_IDENTITY="3rd Party Mac Developer Installer: YOUR NAME (TEAMID)" \ +DEVSCOPE_PROVISIONING_PROFILE="/path/to/DevScope.provisionprofile" \ +./script/package_app_store.sh +``` + +If your Apple account uses a differently named modern signing identity, pass that exact identity string. You can inspect installed signing identities with: + +```bash +security find-identity -p codesigning -v +``` + +The package script creates: + +```text +dist/app-store/DevScope.pkg +``` + +Upload the package with Apple Transporter or App Store Connect tooling, then complete App Review metadata. + +## App Review Notes + +Use plain language in App Review notes: + +- DevScope is a local developer utility for inspecting user-owned development processes. +- It scans local process metadata with system tools and does not require a server. +- It shows CPU, memory, elapsed runtime, command context, process tree context, and local workflow grouping. +- Termination actions require explicit user interaction and use normal user-owned process signaling. +- The app does not collect analytics, does not track users, and does not upload process data. +- Apple naming support is optional and deterministic scanner facts remain authoritative. + +## Required Manual Smoke Before Upload + +Run these against the sandbox-signed `dist/release/DevScope.app`: + +- Live process table populates within a few seconds. +- Search filters process names, commands, PIDs, folders, and runtimes. +- Runtime Tree shows only detected runtime groups. +- Focus groups AI/ML, training, workspace, web, and local project groups correctly. +- CPU, memory, Apple GPU, and machine load graph update smoothly. +- Copy/export/folder actions work. +- `TERM` works against a disposable test process owned by the current user. +- `TERM Tree` works against a disposable parent/child process tree. +- Force kill remains behind the destructive menu. +- Settings and Support open correctly. + +## If Sandbox Blocks Core Features + +The current sandbox build blocks process scanning, even with the native fallback, so there are three honest release shapes: + +1. Ship the full DevScope product outside the Mac App Store using Developer ID signing and notarization. +2. Build a reduced Mac App Store variant that does not promise process inspection/termination, or obtain an Apple-approved entitlement/path that restores the core behavior. +3. Ship both editions with distinct capability copy and separate runtime validation. + +Do not submit a degraded build that misrepresents DevScope's process-control feature set. + +## Full-Feature Public Release Fallback + +For the full DevScope feature set, use Developer ID distribution until the App Store sandbox issue is resolved: + +```bash +DEVSCOPE_DEVELOPER_ID_SIGN_IDENTITY="Developer ID Application: YOUR NAME (TEAMID)" \ +DEVSCOPE_NOTARY_KEYCHAIN_PROFILE="devscope-notary" \ +./script/package_developer_id.sh +``` + +The Developer ID path signs with hardened runtime, uses non-sandboxed entitlements, creates a zip, submits it to Apple's notary service when a notary keychain profile is configured, staples the ticket, and verifies Gatekeeper acceptance. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..cb8e4ab --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,38 @@ +# Architecture + +DevScope is a native Swift package with two primary modules: + +- `DevScopeCore` contains deterministic models, process and automation policy, + scanners, correlation, presentation state, command boundaries, and storage + abstractions. It is designed for focused unit and integration testing. +- `DevScope` is the macOS executable. It composes AppKit/SwiftUI views, system + adapters, application lifecycle, notifications, local file access, and + optional Foundation Models presentation enrichment. + +## Data and action flow + +1. System adapters gather bounded process and supported automation facts. +2. Core services normalize those facts into explicit snapshots and inventories. +3. Presentation policy filters and groups deterministic records for the UI. +4. Optional on-device model output may improve labels or notes but cannot replace + source facts or authorize actions. +5. User actions pass through scope and capability policy, explicit confirmation + where required, system execution, and post-action result verification. + +## Trust boundaries + +- Operating-system command output, file content, process identifiers, and model + text are untrusted boundary inputs and must be parsed or validated. +- Destructive process and automation operations remain user initiated. +- The App Sandbox build is a compatibility-validation surface with restricted + visibility; it is not equivalent to the full direct-download product. +- Local support data is bounded and remains on the Mac unless the user exports or + shares it. + +## Distribution + +SwiftPM builds are development artifacts. A production direct-download archive +must be universal where claimed, signed with Developer ID, hardened, notarized, +stapled, validated, and smoke-tested from the exact distributed bundle. App Store +distribution is a separate product because sandbox constraints currently block +the full process-inspection promise. diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md new file mode 100644 index 0000000..b59e100 --- /dev/null +++ b/docs/INSTALLATION.md @@ -0,0 +1,100 @@ +# Installation and Maintenance + +DevScope ships as source and as an optional community prerelease. The community +archive is ad-hoc signed and not notarized by Apple. There is no notarized public +binary and no in-app updater yet. + +## Requirements + +- macOS 14 or newer +- A current stable Xcode toolchain with Swift 6 support +- Git and a POSIX-compatible shell + +## Run from source + +From the repository root: + +```bash +./script/build_and_run.sh --verify +``` + +This builds and launches `dist/DevScope.app` as a local development artifact. + +## Download the community prerelease + +Download both the macOS universal zip and its `.sha256` file from +[GitHub Releases](https://github.com/rsitech-ai/devscope/releases), keep them in +the same directory, and verify the archive: + +```bash +shasum -a 256 -c DevScope-*.zip.sha256 +``` + +The archive contains `DevScope.app` and +`UNNOTARIZED_COMMUNITY_PREVIEW.txt`. Read that warning before opening the app. +The preview is ad-hoc signed and not notarized, so Gatekeeper may reject it. Do +not disable Gatekeeper or other macOS security protections. Build from source if +the preview is blocked, or wait for a Developer ID signed and notarized release. + +## Install the full local app + +```bash +./script/install_local_full_build.sh +``` + +The installer builds the non-sandbox process-control variant, validates its +bundle policy, places it at `/Applications/DevScope.app`, and launches it. This +is a local build, not Developer ID/notarization proof. + +## Update + +DevScope has no automatic updater. Review and fetch the desired source revision, +rerun the readiness/tests appropriate to that revision, then rerun: + +```bash +./script/install_local_full_build.sh +``` + +A future signed release must publish its own exact update and checksum guidance. + +## Uninstall + +Quit DevScope and move `/Applications/DevScope.app` to Trash in Finder. The app +does not install a privileged helper or background updater. + +DevScope keeps preferences and bounded recovery data under the current user's +Library, including `~/Library/Application Support/DevScope`. Remove that folder +only if you also want to delete local recovery/backups; inspect it first because +that cleanup is permanent once Trash is emptied. + +## Troubleshooting + +### No processes or working directories appear + +Do not use `script/sandbox_smoke.sh` as the daily install. App Sandbox blocks the +full machine-wide scanner. Install the full local build, then open **Settings > +Access** for DevScope's exact Ready, Needed, or Blocked explanation. DevScope +cannot grant itself Full Disk Access or bypass App Sandbox. + +### The app does not launch from a source build + +Confirm `xcodebuild -version` and `swift --version` satisfy the requirements, +then run `./script/build_and_run.sh --verify` and use its first build or bundle +validation error. Do not bypass Gatekeeper for an untrusted downloaded binary. + +### Restore last copy is unavailable + +Copy a process command or visible-row export from DevScope first. Recovery stores +a bounded redacted payload; it deliberately does not preserve unredacted secrets. + +### Repositories are grouped under an owner-folder name + +For layouts such as `~/dev/acme-labs/project`, launch DevScope with +`DEVSCOPE_WORKSPACE_OWNER_COMPONENTS=acme-labs` (comma-separate multiple names). +DevScope will then select the repository below that configured owner folder. +Without explicit structure, it conservatively treats the first component after +the development root as the project so nested working directories remain stable. + +For bugs, support, privacy, and security-reporting routes, see +[SUPPORT.md](../SUPPORT.md), [PRIVACY.md](../PRIVACY.md), and +[SECURITY.md](../SECURITY.md). diff --git a/docs/RELEASE_CHECKLIST.md b/docs/RELEASE_CHECKLIST.md new file mode 100644 index 0000000..b7fe682 --- /dev/null +++ b/docs/RELEASE_CHECKLIST.md @@ -0,0 +1,71 @@ +# DevScope Release Checklist + +DevScope is a local process monitor and process-control utility. Treat release work as a security and distribution exercise, not only a build step. + +## Required Before GitHub Release + +- [ ] Run `swift test`. +- [ ] Run `swift build`. +- [ ] Run `swift build -c release`. +- [ ] Run `./script/build_and_run.sh --verify`. +- [ ] Run `./script/sandbox_smoke.sh`. +- [ ] Build and validate an ad-hoc full-access bundle with `config/DevScopeDeveloperID.entitlements`. +- [ ] Run a redacted full-history Gitleaks scan and inspect reachable historical images and author metadata. +- [ ] Smoke test live process refresh, search, category filters, sorting, Settings, Sponsor, graph display, copy/open-folder actions, and kill confirmation dialogs. +- [ ] Sign the app with a Developer ID Application certificate. +- [ ] Enable hardened runtime for outside-App-Store distribution. +- [ ] Notarize the signed app and staple the notarization ticket. +- [ ] For full-feature public distribution, run `./script/package_developer_id.sh` with Developer ID signing and notary credentials. +- [ ] Publish exact install, update, privacy, and uninstall instructions. +- [ ] Follow the exact sequence in `docs/RELEASING.md` and record the final commit and artifact digests outside that self-referential commit. + +## Required Before Mac App Store Submission + +- [x] Decide whether Mac App Store is compatible enough to test. DevScope now has a sandboxed package path, but process inspection and signaling must be manually verified under App Sandbox before upload. +- [x] Add App Sandbox entitlements. +- [x] Add packaged privacy manifest. +- [x] Add generated app icon to the release bundle. +- [x] Add release bundle validation script. +- [x] Add App Store package script. +- [x] Test sandbox-signed launch. +- [x] Compile and run the complete test inventory with the current release toolchain; preserve documented stable-toolchain compatibility. +- [x] Compare full-access and App-Sandbox behavior on macOS 27. Full access: 921 processes. Sandbox: 0 processes with explicit blocked state. +- [ ] Resolve App Sandbox scanner blocker. Current sandbox evidence: app launches but `/bin/ps` is blocked and the native `libproc` fallback returns no visible process records, so the app shows zero tracked processes. +- [ ] Create the App Store Connect app record for the final bundle identifier. +- [x] Install Mac App Store application and installer signing identities. +- [ ] Download or create a provisioning profile for the final bundle identifier. +- [ ] Build an App Store package with `./script/package_app_store.sh`. +- [ ] Verify which process-scanning and termination features still work in the sandboxed release app. +- [ ] If sandboxing breaks core functionality, ship via Developer ID/notarization instead of weakening user expectations in the App Store build. +- [ ] Provide App Review notes explaining process visibility, kill behavior, local-only data handling, and why requested permissions are needed. + +## Privacy and Safety Copy + +- DevScope scans local process metadata using system tools. +- DevScope does not require network access for process scanning. +- Apple naming, when available, runs through Apple platform APIs and falls back to deterministic local labels when unavailable or unsafe. +- Termination actions require explicit user interaction and use normal user-owned process signaling. + +## Useful Release Commands + +```bash +./script/sandbox_smoke.sh +./script/validate_release_bundle.sh dist/release/DevScope.app +security find-identity -p codesigning -v +``` + +```bash +DEVSCOPE_DEVELOPER_ID_SIGN_IDENTITY="Developer ID Application: YOUR NAME (TEAMID)" \ +DEVSCOPE_NOTARY_KEYCHAIN_PROFILE="devscope-notary" \ +./script/package_developer_id.sh +``` + +```bash +DEVSCOPE_BUNDLE_ID="com.s1korrrr.DevScope" \ +DEVSCOPE_MARKETING_VERSION="0.1.0" \ +DEVSCOPE_BUILD_VERSION="1" \ +DEVSCOPE_APP_STORE_SIGN_IDENTITY="Apple Distribution: YOUR NAME (TEAMID)" \ +DEVSCOPE_INSTALLER_IDENTITY="3rd Party Mac Developer Installer: YOUR NAME (TEAMID)" \ +DEVSCOPE_PROVISIONING_PROFILE="/path/to/DevScope.provisionprofile" \ +./script/package_app_store.sh +``` diff --git a/docs/RELEASING.md b/docs/RELEASING.md new file mode 100644 index 0000000..d3c28e0 --- /dev/null +++ b/docs/RELEASING.md @@ -0,0 +1,100 @@ +# Maintainer Releasing + +This procedure prepares one exact DevScope source commit and its macOS artifacts. +Repository publication and release creation require explicit owner authority. +Developer ID use, notarization, and App Store submission remain separate +credentialed actions. + +## 1. Establish the candidate + +Start from the intended `main`, confirm the working tree is clean, and record the +exact commit, toolchain, supported platform claims, and unresolved owner gates. +Do not tag while `docs/release/0.1.0/BLOCKERS.md` contains an unaccepted +publication blocker. + +## 2. Validate source and policy + +```bash +./script/check_open_source_readiness.sh +swift package show-dependencies --format json +swift test -Xswiftc -warnings-as-errors +swift build -c release -Xswiftc -warnings-as-errors +bash -n script/*.sh +plutil -lint Resources/PrivacyInfo.xcprivacy config/*.entitlements +git diff --check +``` + +Run the checksum-pinned Gitleaks workflow or an equivalent local Gitleaks 8.30.1 +full-history scan with redacted output. Separately inspect reachable deleted +images and Git author metadata; a secret scanner does not close visual privacy or +identity disclosure. + +## 3. Rehearse both product policies + +Build the sandbox compatibility bundle: + +```bash +DEVSCOPE_DIST_DIR="$(mktemp -d)/sandbox" \ + ./script/build_release_bundle.sh +``` + +Validate its App Sandbox and user-selected-file entitlements with +`DEVSCOPE_REQUIRE_SANDBOX=1 ./script/validate_release_bundle.sh `. + +Build the full-access local candidate with +`config/DevScopeDeveloperID.entitlements`, then validate with +`DEVSCOPE_REQUIRE_SANDBOX=0` and `DEVSCOPE_REQUIRE_APPLE_EVENTS=1`. An ad-hoc +signature proves bundle policy only; it is not a distributable production +signature. Confirm each candidate contains `LICENSE`, `NOTICE`, and +`THIRD_PARTY_NOTICES.md` under `Contents/Resources`. + +Run `./script/build_and_run.sh --verify`, exercise the README quickstart and the +primary Processes/Automations paths, and review DevScope-owned logs. + +## 4. Rehearse the exact source archive + +Create a `git archive` from the exact candidate commit in a temporary directory. +Inspect its file list, build and test from the extracted archive, rerun the +quickstart where practical, and confirm no ignored local files are required. +Record archive and artifact SHA-256 digests in the GitHub release or another +immutable release record; do not hardcode a self-referential final commit digest +inside that same commit. + +## 5. Production distribution + +Only after the owner supplies and authorizes the signing/notary inputs, run: + +```bash +DEVSCOPE_DEVELOPER_ID_SIGN_IDENTITY="Developer ID Application: YOUR NAME (TEAMID)" \ +DEVSCOPE_NOTARY_KEYCHAIN_PROFILE="devscope-notary" \ +./script/package_developer_id.sh +``` + +Require hardened runtime, timestamped signing, notarization acceptance, stapling, +Gatekeeper approval, checksum publication, and a clean-machine install/runtime +smoke of the exact archive. Keep the reduced sandbox/App Store route separate. + +### Community prerelease fallback + +When no Developer ID identity or notary profile is available, maintainers may +produce a clearly labeled ad-hoc signed prerelease: + +```bash +DEVSCOPE_SOURCE_COMMIT="$(git rev-parse HEAD)" \ +DEVSCOPE_ACKNOWLEDGE_UNNOTARIZED_PREVIEW=1 \ + ./script/package_community_preview.sh +``` + +The script validates the universal full-access bundle, packages an explicit +`UNNOTARIZED_COMMUNITY_PREVIEW.txt`, rejects AppleDouble metadata, and writes a +SHA-256 manifest. Publish it only as a GitHub **prerelease** whose title and first +paragraph say that it is ad-hoc signed and not notarized. Never describe this +fallback as a production, Developer ID, Gatekeeper-approved, or notarized build. + +## 6. Publish and verify + +After explicit external-action approval, push the exact candidate, require green +hosted checks, protect the default branch and release tags, publish the tag and +release, and inspect the repository signed out. Recheck security settings and +protections after any visibility change. Follow +[Installation and maintenance](INSTALLATION.md) for user-facing lifecycle copy. diff --git a/docs/open-source/OPEN_SOURCE_MANIFEST.json b/docs/open-source/OPEN_SOURCE_MANIFEST.json new file mode 100644 index 0000000..32bfea2 --- /dev/null +++ b/docs/open-source/OPEN_SOURCE_MANIFEST.json @@ -0,0 +1,33 @@ +{ + "schemaVersion": 1, + "project": "DevScope", + "license": "Apache-2.0", + "copyrightOwner": "Rafal Sikora", + "publicMaintainer": "RSI Tech", + "website": "https://rsitech.ai", + "contact": "info@rsitech.ai", + "sourcePublication": { + "status": "canonical-fresh-history-organization-repository", + "publicationBlocked": false, + "canonicalRepository": "https://github.com/rsitech-ai/devscope", + "legacyHistoryIncluded": false, + "checker": "script/check_open_source_readiness.sh", + "requiresOwnerVisibilityChange": false + }, + "productionDistribution": { + "fullFeatureRoute": "developer-id-direct-download", + "status": "blocked-external-notarization", + "communityPreview": "available-ad-hoc-signed-unnotarized-prerelease", + "sandboxFeatureParity": false + }, + "dependencies": [], + "sbom": "docs/open-source/SBOM.spdx.json", + "security": { + "privateReportingDocumented": true, + "historySecretScanRequired": true, + "historySecretScanWorkflow": ".github/workflows/secret-scan.yml", + "githubActionsPinnedToFullCommitSHA": true, + "historicalPrivacyReviewRequired": true, + "historicalPrivacyReviewStatus": "legacy-history-excluded-by-fresh-publication" + } +} diff --git a/docs/open-source/OPEN_SOURCE_STATUS.md b/docs/open-source/OPEN_SOURCE_STATUS.md new file mode 100644 index 0000000..17d5697 --- /dev/null +++ b/docs/open-source/OPEN_SOURCE_STATUS.md @@ -0,0 +1,46 @@ +# Open-Source Status + +## Current verdict + +The canonical public repository is +[rsitech-ai/devscope](https://github.com/rsitech-ai/devscope). It is published +from a clean source snapshot with fresh Git history. The legacy private +`s1korrrr/devscope` history is deliberately not transferred because it contains +deleted local audit images and author metadata that are irrelevant to the public +project. + +Each public snapshot must pass the deterministic checker, strict tests, release +build, exact-tree secret scan, review, and clean source-archive inspection. The +fresh-history route resolves the legacy-history privacy blocker without a force +push or disclosure of the private objects. + +## Distribution boundary + +Source publication is independent of binary production distribution. A +downloadable community prerelease may be ad-hoc signed and unnotarized only when +its archive and release notes say so prominently. The production direct-download +route still requires a real Developer ID identity, hardened runtime validation, +Apple notarization, ticket stapling, Gatekeeper assessment, and an exact-artifact +smoke test. + +The sandbox build validates packaging constraints but currently does not provide +the full process-inspection product and must not be marketed as feature-equivalent. + +## Final local gates + +Run from a clean publication candidate: + +```bash +./script/check_open_source_readiness.sh +swift test -Xswiftc -warnings-as-errors +swift build -c release -Xswiftc -warnings-as-errors +bash -n script/*.sh +plutil -lint Resources/PrivacyInfo.xcprivacy config/*.entitlements +``` + +Also run an exact-tree secret scan, inspect a `git archive` of the exact public +commit, and verify the fresh public history contains no legacy objects. + +The current dependency declaration and source-package inventory are recorded in +`OPEN_SOURCE_MANIFEST.json` and `SBOM.spdx.json`. They must be regenerated when +an external package or redistributed component is added. diff --git a/docs/open-source/PUBLICATION_GATE_MATRIX.md b/docs/open-source/PUBLICATION_GATE_MATRIX.md new file mode 100644 index 0000000..66cb697 --- /dev/null +++ b/docs/open-source/PUBLICATION_GATE_MATRIX.md @@ -0,0 +1,18 @@ +# Publication Gate Matrix + +| Gate | Completion evidence | Authority | +|---|---|---| +| License and notices | Apache-2.0 text, NOTICE, third-party notices, checker pass | Repository maintainer | +| Community health | Contributing, conduct, support, governance, security, privacy, issue and PR templates | Repository maintainer | +| Source integrity | Strict tests and release build, clean diff, shell/plist checks | Engineering | +| Supply-chain disclosure | Dependency graph reviewed; actions pinned to full SHAs; manifest current | Engineering | +| Secret/privacy history | Fresh public history excludes legacy objects; exact-tree secret scan and source-archive inspection pass | Security/release reviewer | +| Repository settings | Visibility, branch protection, required reviews/checks, private vulnerability reporting, security features | Repository owner | +| Public source release | Exact reviewed commit tagged or published with accurate notes | Repository owner | +| Direct-download binary | Developer ID, hardened runtime, notarization, stapling, Gatekeeper, exact-bundle smoke | Apple/release owner | +| Community prerelease | Ad-hoc signature disclosed, trust warning embedded, checksum published, exact-bundle smoke | Release owner | +| Mac App Store binary | Explicit reduced product scope, provisioning, validation, upload, review | Product and Apple account owner | + +No row implies completion of a later row. In particular, public source does not +make an ad-hoc signed preview production-ready, and a packaging smoke does not +prove App Store product viability. diff --git a/docs/open-source/SBOM.spdx.json b/docs/open-source/SBOM.spdx.json new file mode 100644 index 0000000..49cfee8 --- /dev/null +++ b/docs/open-source/SBOM.spdx.json @@ -0,0 +1,33 @@ +{ + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "DevScope-0.1.0-source", + "documentNamespace": "https://github.com/rsitech-ai/devscope/sbom/devscope-0.1.0-source-20260720", + "creationInfo": { + "created": "2026-07-17T19:50:00Z", + "creators": [ + "Tool: DevScope open-source readiness process" + ] + }, + "packages": [ + { + "name": "DevScope", + "SPDXID": "SPDXRef-Package-DevScope", + "versionInfo": "0.1.0", + "downloadLocation": "https://github.com/rsitech-ai/devscope", + "filesAnalyzed": false, + "licenseConcluded": "Apache-2.0", + "licenseDeclared": "Apache-2.0", + "copyrightText": "Copyright 2026 Rafal Sikora", + "primaryPackagePurpose": "APPLICATION" + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relationshipType": "DESCRIBES", + "relatedSpdxElement": "SPDXRef-Package-DevScope" + } + ] +} diff --git a/docs/release/0.1.0/APP_REVIEW_NOTES.md b/docs/release/0.1.0/APP_REVIEW_NOTES.md new file mode 100644 index 0000000..ae30475 --- /dev/null +++ b/docs/release/0.1.0/APP_REVIEW_NOTES.md @@ -0,0 +1,11 @@ +# Draft App Review Notes + +**Not submission-ready; owner and product approval required.** + +DevScope is a local macOS process-inspection tool. The full product reads local process metadata, working directories, commands, and resource measurements, and can send TERM/KILL only after explicit confirmation with PID-birth validation and protected-service checks. It does not require an account or network backend. + +The current Mac App Store sandbox build cannot inspect the local process set and therefore does not deliver the full product. Do not submit it as equivalent to the full build. If a reduced App Store edition is approved, these notes must be rewritten to describe its exact reduced functionality, how reviewers reach every feature, and why the listing/screenshots accurately represent that build. + +Reviewer prerequisites for a future full-capability route: launch developer tools/services locally to populate rows. No demo account exists. No purchases or subscriptions exist. Permission guidance must explain that sandbox blocking is not fixable through System Settings. + +Owner must supply review contact, support URL, privacy URL, content-rights/export answers, DSA status, age rating, accessibility declarations, and any regional restrictions. diff --git a/docs/release/0.1.0/APP_STORE_CHECKLIST.md b/docs/release/0.1.0/APP_STORE_CHECKLIST.md new file mode 100644 index 0000000..82ec2a7 --- /dev/null +++ b/docs/release/0.1.0/APP_STORE_CHECKLIST.md @@ -0,0 +1,23 @@ +# App Store Checklist + +Official requirements rechecked 2026-07-14: [App Review Guidelines](https://developer.apple.com/app-store/review/guidelines/), [App Sandbox](https://developer.apple.com/documentation/security/app-sandbox), [upload builds](https://developer.apple.com/help/app-store-connect/manage-builds/upload-builds/), [app information](https://developer.apple.com/help/app-store-connect/reference/app-information), [screenshot specifications](https://developer.apple.com/help/app-store-connect/reference/screenshot-specifications), [app privacy](https://developer.apple.com/help/app-store-connect/manage-app-information/manage-app-privacy/), [age ratings](https://developer.apple.com/help/app-store-connect/manage-app-information/set-an-app-age-rating), [accessibility nutrition labels](https://developer.apple.com/help/app-store-connect/manage-app-accessibility/overview-of-app-accessibility-nutrition-labels/), and [EU DSA trader requirements](https://developer.apple.com/help/app-store-connect/manage-compliance-information/manage-european-union-digital-services-act-trader-requirements/). + +- [x] Version/build drafted: 0.1.0 (1) +- [x] Universal local Release bundles and privacy manifest +- [x] Icon representations have exact expected pixel dimensions +- [x] App Store packaging scripts fail closed +- [x] Xcode 27/SDK 27 strict tests, Release, AddressSanitizer, and ThreadSanitizer +- [x] Apple Distribution and installer identities installed +- [ ] Product-compatible App Sandbox behavior +- [ ] App Store Connect record and matching `com.s1korrrr.DevScope` provisioning profile +- [ ] Signed `.pkg` and Apple validation +- [ ] Name/subtitle/promo/description/keywords/categories approval +- [ ] Support, privacy, and optional marketing URLs +- [ ] Current sanitized screenshots and optional preview +- [ ] Review contact/instructions and final review notes +- [ ] Privacy labels and in-app privacy policy +- [ ] Accessibility declarations +- [ ] Age rating, export compliance, content rights, DSA status +- [ ] Pricing, territories, manual/automatic/phased release decision +- [ ] Upload, processing inspection, TestFlight smoke +- [ ] Explicit approval before Submit for Review diff --git a/docs/release/0.1.0/BLOCKERS.md b/docs/release/0.1.0/BLOCKERS.md new file mode 100644 index 0000000..8605167 --- /dev/null +++ b/docs/release/0.1.0/BLOCKERS.md @@ -0,0 +1,31 @@ +# Release Blockers + +## Source publication follow-ups + +The canonical public source is published through a fresh-history +`rsitech-ai/devscope` snapshot. Legacy private objects, internal audits, +monetization work, and author metadata are not transferred. + +1. **Repository protections — Repository owner.** Keep pull-request protection, + private vulnerability reporting, dependency alerts, and available security + features enabled. Add required hosted checks when Actions billing permits + reliable execution. +2. **Public archive — Release owner.** Inspect the exact public commit archive and + record the tag, source commit, checksum, and all limitations in release + metadata. + +## Production binary gates + +1. **Distribution route — Product owner.** The full-feature product requires direct Developer ID distribution. A reduced sandbox/App Store edition would be a separate scoped product. +2. **Notarization — Apple account owner.** The installed `Developer ID Application: Rafal Sikora (2NY8A789TN)` identity is usable. Configure a notary Keychain profile backed by App Store Connect credentials, then produce a hardened, timestamped, notarized, stapled archive that passes Gatekeeper. +3. **Clean-machine runtime — QA/release owner.** Install and exercise the exact notarized archive on a clean supported Mac, including permission guidance, core inspection, safe fixture termination, automation read paths, update/uninstall behavior, and log review. +4. **Compatibility — QA.** Minimum macOS 14 runtime and physical Intel hardware runtime remain unexecuted. Universal build validation is not a substitute for hardware/runtime coverage. +5. **Performance/accessibility — QA.** Instruments/memgraph and the complete VoiceOver, keyboard, increased-contrast, and Reduce Motion matrices remain unverified. +6. **Public metadata — Product owner.** Public ownership, maintainer, website, and contact are approved. Final release notes and any hosted update mechanism still require exact-artifact review. + +An ad-hoc signed community prerelease is permitted only when the archive and +release page prominently state that it is unnotarized and not a production +Developer ID distribution artifact. + +Local source, test, runtime, and package checks cannot close owner credentials, +hosted CI, Apple notarization, clean-machine, or physical-hardware gates. diff --git a/docs/release/0.1.0/PRIVACY_DATA_MAP.md b/docs/release/0.1.0/PRIVACY_DATA_MAP.md new file mode 100644 index 0000000..7a629a4 --- /dev/null +++ b/docs/release/0.1.0/PRIVACY_DATA_MAP.md @@ -0,0 +1,19 @@ +# Privacy Data Map + +DevScope has no repository evidence of network transmission, accounts, analytics, advertising, tracking, third-party SDKs, or backend storage. The final App Store declaration still requires owner confirmation and must not be inferred solely from code. + +| Data | Source | Purpose | Storage/retention | Deletion | Recipient/tracking/linkage | +|---|---|---|---|---|---| +| Process PID, parent PID, executable, command, CWD, resource metrics | Local macOS process APIs, `ps`, `lsof`, `ioreg` | Local process inspection and grouping | Primarily in memory; history window about 2 minutes | Cleared on app exit/history rollover | No recipient observed; no tracking; not account-linked | +| Favorite/watch identity | User action plus process identity | Restore local categorization | UserDefaults as SHA-256-derived `v2:` keys | User can remove category; legacy raw keys are migrated to hashes on launch | Local only; not account-linked | +| Recovery copy | Explicit copy/export action | Restore last redacted copy | Local Application Support file, bounded to 2 MB, mode 0600 | Replaced by subsequent copy; app has no dedicated clear UI | Local only; not tracking | +| Clipboard content | Explicit copy/export action | User-requested transfer | macOS general pasteboard retention | Controlled by OS/user/next clipboard owner | Other local apps may read pasteboard | +| Apple Foundation Models prompts/results | Optional local naming feature | Improve display labels/workflow notes | In-memory cache | App lifecycle/cache eviction | Apple on-device framework; owner must confirm intended disclosure wording | +| UserDefaults settings | User choices | UI preferences | Local until app data removal | Change setting or remove app data | Local only | +| Automation definitions and provenance | Local launchd property lists, current-user crontab, Login Items/Background Items system diagnostics | Detect, classify, correlate, and locally manage approved user-owned automations | Inventory is primarily in memory; source diagnostics are reduced to health text/fingerprints | Cleared on refresh/app exit; original definitions remain under macOS/user control | Local only; no recipient or account linkage | +| Automation recovery backups and manifests | Explicit confirmed mutation of a user-owned automation | Conflict-safe rollback and manual restoration | Local Application Support, owner-only 0600 files under a 0700 directory, bounded by count/age | Pruned by bounded retention, successful recovery cleanup, or exact user/audit cleanup | Local only; may contain the original local automation command/environment | +| Automation notification state | Locally observed strong process links and verified state transitions | Optional long-running, unexpected-exit, and repeated-failure notifications | Transition state in memory; opt-in settings in UserDefaults | Cleared on app exit or setting removal | Delivered through local macOS notifications only | + +Privacy manifest declares UserDefaults required-reason API category with reason `CA92.1`. No ATT use was found. The repository now includes the public-source policy in `PRIVACY.md`; its GitHub URL becomes usable after source publication. A separately hosted URL and in-app policy link remain App Store/product-owner decisions. The owner must still confirm privacy labels, retention promises, local Foundation Models wording, support contact, and any operational telemetry not represented in this repository. + +The 2026-07-14 macOS 27 automation audit found no network transmission, telemetry, new third-party dependency, privileged helper, or secret logging in the automation engine. Raw current-user crontab content was kept out of committed evidence; only its checksum and result state are recorded. Redacted export is the default, and explicit unredacted output is restricted to owner-only permissions. diff --git a/docs/release/0.1.0/RELEASE_MANIFEST.json b/docs/release/0.1.0/RELEASE_MANIFEST.json new file mode 100644 index 0000000..8618ef1 --- /dev/null +++ b/docs/release/0.1.0/RELEASE_MANIFEST.json @@ -0,0 +1,88 @@ +{ + "schemaVersion": 3, + "product": "DevScope", + "copyrightOwner": "Rafal Sikora", + "publicMaintainer": "RSI Tech", + "website": "https://rsitech.ai", + "contact": "info@rsitech.ai", + "platform": "macOS", + "version": "0.1.0", + "build": "1", + "date": "2026-07-20", + "verdict": "SOURCE_PUBLICATION_APPROVED_COMMUNITY_PREVIEW_PRODUCTION_BINARY_BLOCKED", + "source": { + "integratedBranch": "main", + "auditedProductBaseline": "e8f3f973dcb94f83c89ba73bbde600bc7c920e1e", + "exactPublicationCommit": null, + "exactPublicationCommitStatus": "record-in-public-release-metadata" + }, + "tests": { + "command": "swift test -Xswiftc -warnings-as-errors", + "executed": 669, + "failures": 0 + }, + "sourcePublication": { + "technicalBuildAndTestGatesPassed": true, + "publicationBlocked": false, + "canonicalRepository": "https://github.com/rsitech-ai/devscope", + "freshHistoryPublication": true, + "legacyHistoryIncluded": false, + "secretFindings": 0, + "visualHistoryPrivacyClear": true, + "dependencyCount": 0, + "license": "Apache-2.0", + "repositoryVisibilityChanged": true, + "ownerSettingsVerified": true, + "exactArchiveValidationPassed": true, + "hostedChecksStatus": "record-from-public-organization-pr" + }, + "artifacts": [ + { + "role": "local-full-feature-validation", + "distribution": "local-ad-hoc-full-access", + "architectures": ["x86_64", "arm64"], + "bundleIdentifier": "com.s1korrrr.DevScope", + "signature": "ad-hoc", + "executableSHA256": null, + "digestStatus": "record-from-exact-final-artifact", + "validated": true, + "licenseNoticesEmbedded": true, + "productionDistributable": false + }, + { + "role": "sandbox-compatibility-validation", + "distribution": "local-ad-hoc-sandbox", + "architectures": ["x86_64", "arm64"], + "bundleIdentifier": "com.s1korrrr.DevScope", + "signature": "ad-hoc", + "executableSHA256": null, + "digestStatus": "record-from-exact-final-artifact", + "validated": true, + "licenseNoticesEmbedded": true, + "fullFeatureParity": false, + "productionDistributable": false + } + ], + "communityPreview": { + "status": "AD_HOC_SIGNED_UNNOTARIZED_PRERELEASE", + "architectures": ["x86_64", "arm64"], + "trustWarningEmbedded": true, + "sha256Published": true, + "productionDistributable": false + }, + "publicBinaryDistribution": { + "status": "BLOCKED_EXTERNAL_NOTARIZATION", + "developerIDIdentity": "Developer ID Application: Rafal Sikora (2NY8A789TN)", + "developerIDIdentityAvailable": true, + "notaryKeychainProfileAvailable": false, + "developerIDSigned": false, + "notarized": false, + "stapled": false, + "gatekeeperApproved": false + }, + "upload": { + "appStoreValidated": false, + "testFlightUploaded": false, + "submittedForReview": false + } +} diff --git a/docs/release/0.1.0/RELEASE_NOTES.md b/docs/release/0.1.0/RELEASE_NOTES.md new file mode 100644 index 0000000..3e98d44 --- /dev/null +++ b/docs/release/0.1.0/RELEASE_NOTES.md @@ -0,0 +1,23 @@ +# DevScope 0.1.0 Community Preview Release Notes + +Initial release candidate hardening includes: + +- Universal Apple silicon and Intel Release bundles. +- Clear sandbox-versus-full-build access diagnostics. +- Single-window process scanner ownership. +- Scrollable settings, reduced-motion support, accessibility announcements, and accessible graph summaries. +- Truthful folder/recovery feedback. +- Hashed favorite/watch identity persistence with one-time legacy-key migration. +- Stronger credential redaction and safe spreadsheet export. +- Actionable file-mutation recovery locations with Finder and copy-path actions. +- Durable copy recovery shared by command-bar and table context-menu actions. +- Classification-independent protected-process enforcement. +- Fail-closed App Store sandbox packaging and exact entitlement validation. +- Configured macOS 26 CI gates for Apple silicon and Intel runners, covering both sandbox and full-access bundle policies. + +The downloadable community preview is ad-hoc signed and not notarized by Apple. +It is not a production Developer ID or approved App Store build. The canonical +public source uses fresh Git history under `rsitech-ai/devscope`; legacy private +audit objects and author metadata are not included. Developer ID signing, +notarization, stapling, Gatekeeper approval, clean-machine proof, and App Store +submission remain open. diff --git a/docs/release/0.1.0/RELEASE_STATUS.md b/docs/release/0.1.0/RELEASE_STATUS.md new file mode 100644 index 0000000..a37bf1f --- /dev/null +++ b/docs/release/0.1.0/RELEASE_STATUS.md @@ -0,0 +1,56 @@ +# DevScope 0.1.0 Release Status + +## Current verdict + +**SOURCE PUBLICATION APPROVED / COMMUNITY PRERELEASE ROUTE READY / PRODUCTION BINARY BLOCKED — 2026-07-20.** + +The repository contains an Apache-2.0 license, public contribution and security +policies, privacy and architecture disclosures, pinned CI automation, a source +manifest and SPDX SBOM, and a deterministic publication checker. The canonical +public repository is `rsitech-ai/devscope`, seeded from a clean source snapshot +with fresh Git history. The legacy personal repository and its deleted audit +objects are not transferred. + +The full-feature local bundle is universal (`x86_64 arm64`), ad-hoc signed, +structurally validated, and runtime proven. A checksummed community prerelease +may be published only with an embedded unnotarized warning. Production binary +distribution remains **BLOCKED:EXTERNAL** because the installed Developer ID +identity has no configured notary Keychain profile, so no notarized, stapled, +and Gatekeeper-approved artifact was produced. Sandbox +packaging passes, but the sandbox product is not feature-equivalent. + +## Verified gates + +| Gate | Status | Evidence | +|---|---|---| +| Public-source contract | PASS | Apache-2.0, NOTICE, community/security/privacy/governance files, architecture, manifest, SPDX SBOM | +| Deterministic source gate | PASS | `script/check_open_source_readiness.sh` | +| Public history and tree | PASS BY CONSTRUCTION | Canonical publication uses fresh history from the exact sanitized source archive; legacy private objects and author metadata are not transferred; exact-tree Gitleaks remains required | +| Dependency declaration | PASS | Swift package dependency graph is empty; third-party notices and SBOM agree | +| Strict test suite | PASS | 669 tests, 0 failures, warnings treated as errors | +| Strict release build | PASS | Production build completed with warnings treated as errors | +| Exact development runtime | PASS | Exact staged bundle launched, matched to its running executable, and passed strict bundle-signature verification; Processes and Automations received a real UI smoke | +| Universal full-access package | PASS (local) | `x86_64 arm64`, Apple Events entitlement, App Sandbox absent, signature/policy validation passed, license notices embedded | +| Universal sandbox package | PASS (constrained) | `x86_64 arm64`, sandbox and user-selected read-write entitlements, signature/policy validation passed, license notices embedded | +| Community prerelease | READY (unnotarized) | Explicit acknowledgement, ad-hoc signature disclosure, embedded trust warning, clean universal zip, and published SHA-256 required | +| Developer ID identity | PASS (installed) | `Developer ID Application: Rafal Sikora (2NY8A789TN)` is available with its private key | +| Notarization | BLOCKED:EXTERNAL | No `devscope-notary` Keychain profile or equivalent App Store Connect credentials are configured | +| Repository publication settings | OWNER ACTION | Verify public visibility, default-branch protection, private vulnerability reporting, and security settings after organization import | +| Remote required checks | VERIFY ON PUBLIC PR | Record organization-run results; do not inherit the legacy personal repository's billing failure as code evidence | +| Exact hardened-source archive | PASS (local) | The source tree passed readiness, 669 strict tests, warnings-as-errors release build, and exact-tree Gitleaks from a clean `git archive`; repeat from the exact public commit | + +## Log boundary + +The exact development bundle produced no DevScope-owned error or fault during +the fresh scan interval. The macOS 27 beta host emitted framework-owned App +Intents/CoreSpotlight connection and donation errors because Apple host services +were unavailable. These are documented platform diagnostics, not a claim that +the complete unified system log is silent. + +## Release policy + +- Record the exact source commit in the release tag or GitHub release; do not hardcode a self-referential commit in tracked evidence. +- Do not publish an ad-hoc signed local bundle as a production download. +- Publish an ad-hoc signed archive only as an explicitly unnotarized community prerelease with its checksum and trust warning. +- Do not market the sandbox build as the full-feature product. +- Treat source publication, Developer ID distribution, and Mac App Store distribution as independent gates. diff --git a/docs/release/0.1.0/SECURITY_STATUS.md b/docs/release/0.1.0/SECURITY_STATUS.md new file mode 100644 index 0000000..bd618f9 --- /dev/null +++ b/docs/release/0.1.0/SECURITY_STATUS.md @@ -0,0 +1,42 @@ +# Security Status + +The standard repository scan targeted immutable revision `bedc3ac5a04e974ef2a3336e40866a88453bb1ff`. Thirty ranked high-priority files received full-file receipts. Four reportable medium/low candidates were reproduced and fixed in the release worktree: + +1. Critical service protection no longer depends on presentation classification. +2. Redacted commands now cover common `redis-cli -a`, `mysql -p`, and `curl -u` credential forms. +3. TSV exports flatten row/cell separators and neutralize spreadsheet formula prefixes. +4. App Store packaging pins sandbox entitlements and sandbox-required validation; entitlement validation parses the exact plist value. + +One low least-privilege candidate remains deferred: non-TCC `lsof` failures and genuine permission denial currently collapse to the same “zero CWDs” signal, which can lead to Full Disk Access guidance. It requires provenance-aware scanner changes and targeted failure injection. One adjacency-based entitlement validator candidate was suppressed by an actual signed-bundle counterexample, though the parser was hardened anyway. + +No critical/high findings, remote attack surface, third-party dependency, committed credential, network listener, privileged helper, updater, WebView, or binary framework was found in that dated scan. Its readable report was stored outside the repository and is not a reproducible artifact of the current checkout; `RELEASE_MANIFEST.json` therefore does not claim or link current-snapshot scan proof. + +That dated main-to-working-tree security diff scan covered all 22 source-like files changed in its `bedc3ac5a04e974ef2a3336e40866a88453bb1ff` scope, including the release workflow. It validated and fixed a `curl --user` / `--proxy-user` bulk-redaction gap and pinned `actions/checkout` to the then-reviewed immutable commit `34e114876b0b11c390a56381ad16ebd13914f8d5`. Both candidates were revalidated within that dated snapshot, whose sealed report recorded zero surviving reportable findings. + +Residual risk: the earlier standard scan's lower-ranked baseline files remain outside that scan's full-file review, and the deferred `lsof` failure-provenance hardening remains open. That dated diff scan did not cover the current continuation. + +## 2026-07-20 release continuation + +The owner explicitly waived a fresh Codex Security app scan for this continuation. +Local regression work fixed complete-value redaction for Digest, AWS Signature, +proxy authorization, and cookie headers, and added exact recovery-path handling. +This is focused local evidence, not exhaustive current-snapshot scan coverage. + +Four deleted screenshots in the legacy private history were visually confirmed +to expose local identity, workstation-path, process, and workspace information. +The canonical public repository therefore uses a fresh source snapshot and does +not transfer legacy commits, deleted objects, or private author metadata. + +## 2026-07-14 automation-engine addendum + +The layered automation engine adds a higher-risk local management surface and was reviewed against explicit ownership, canonical-path, stale-generation, content-validation, backup, postcondition, rollback, and process-birth gates. Runtime audit fixes closed independent source hangs, cancellation-ignoring source deadlines, ambiguous LaunchAgent runtime errors, stale Start Now verification, incoherent running projection, implicitly enabled duplicates, and command descendants that retained output pipes past leader exit. Commit `310f54c` then closed the final review findings with a shared typed `launchctl print` absence classifier, fail-closed identical-definition provenance, per-source quarantine for cancellation-ignoring refreshes, and four-worker deduplicated runtime discovery. Ordinary, AddressSanitizer, and ThreadSanitizer suites each passed 547/547; the focused controller rerun passed 105/105; the warnings-as-errors Release build and diff check passed. + +The completed independent immutable whole-branch re-review found no Critical, Important, or Minor issue after those fixes. The changed slice has no surviving correctness or security finding. Product commands use fixed absolute executables plus argument arrays, recovery artifacts are 0700/0600 and content-authenticated, unknown launchd state is inspection-only, cross-app modern services remain read-only, imports reject symlinks/non-regular/oversized data, and unredacted export is owner-only. No shell interpolation, network transmission, new dependency, updater, privileged helper, credential, or public listener was introduced. + +Residual automation risk is reported fail-closed: `/usr/bin/crontab ` +blocks in the current macOS 27 process context, and modern Login Items/Background +Items enumeration times out. The 30-second command deadline terminates and reaps +the complete child group; the independent 10-second source deadline preserves +healthy inventory. These integrations must not be described as fully operational +on this host until a permission/toolchain change enables verified write/readback +or enumeration. diff --git a/docs/release/0.1.0/TEST_EVIDENCE.md b/docs/release/0.1.0/TEST_EVIDENCE.md new file mode 100644 index 0000000..88a9862 --- /dev/null +++ b/docs/release/0.1.0/TEST_EVIDENCE.md @@ -0,0 +1,48 @@ +# DevScope 0.1.0 Test Evidence + +- Evidence date: 2026-07-20 +- Canonical publication target: `rsitech-ai/devscope` `main` +- Audited product baseline: `e8f3f973dcb94f83c89ba73bbde600bc7c920e1e` +- Candidate delta: credential redaction, exact recovery-path presentation, durable command-copy recovery, coherent development-bundle signing, embedded release license notices, dual-policy CI, and public release documentation +- Host: macOS 27.0 (beta host) +- Toolchain: Xcode 26.6, Swift 6.3.3 + +## Source verification + +- `./script/check_open_source_readiness.sh`: passed. +- `swift test -Xswiftc -warnings-as-errors`: 669 tests, 0 failures. +- `swift build -c release -Xswiftc -warnings-as-errors`: passed. +- `bash -n script/*.sh`: passed. +- `plutil -lint Resources/PrivacyInfo.xcprivacy config/*.entitlements`: passed. +- GitHub YAML parsed with Ruby Psych: all workflow, issue-form, funding, and Dependabot files passed. +- `gitleaks dir --redact --no-banner --no-color .`: approximately 81.15 MB, no pattern findings. +- Public snapshot `gitleaks dir --redact --no-banner --no-color .`: required at the exact organization PR commit. +- Clean `git archive` verification: readiness, all 669 strict tests, the warnings-as-errors release build, and exact-tree Gitleaks must pass from the exact public commit extraction. +- Public history inspection: the organization repository is seeded from a fresh source snapshot and must contain no legacy private commits or deleted objects. +- Hosted Secret Scan and Release Gates results must be recorded from the exact organization PR rather than inherited from the legacy personal repository. +- Official Apache-2.0 text comparison: byte-for-byte match. + +The Gitleaks policy contains narrowly scoped rule, path, and line-pattern +exceptions for synthetic credential-redaction tests and published artifact +SHA-256 digests. It does not exclude an entire test or documentation tree. + +## Runtime and package verification + +- `script/build_and_run.sh --verify`: exact staged development binary launched and matched its executable; the completed bundle passed strict signature verification. +- Real UI smoke: Automations loaded with explicit diagnostics; Processes loaded live inventory; exact-PID search, process detail, self-protection, and durable command copy were exercised. +- Full-access local package: version 0.1.0 (1), `x86_64 arm64`, App Sandbox absent, Apple Events entitlement present, strict signature and policy validation passed, with license notices embedded. +- Sandbox package: version 0.1.0 (1), `x86_64 arm64`, required sandbox and user-selected read-write entitlements present, strict signature and policy validation passed, with license notices embedded. + +Artifact SHA-256 digests must be recorded from the exact merged/final artifact +in GitHub release metadata or another immutable exact-build record; none is +claimed by this candidate document. + +The reviewed recovery-cache repair modifies application source. Fresh artifact +hashes must therefore be recorded from the exact merged release build rather +than inherited from the earlier audited baseline. + +## Distribution boundary + +This is verified source and local runtime/package evidence. It is not Developer +ID signing, notarization, Gatekeeper approval, App Store validation, TestFlight +upload, clean-machine installation proof, or App Review approval. diff --git a/docs/screenshots/devscope-main-sanitized-2026-07-15.png b/docs/screenshots/devscope-main-sanitized-2026-07-15.png new file mode 100644 index 0000000000000000000000000000000000000000..a0523328d2858f04230494e218a3b4b0c1dfef00 GIT binary patch literal 2472263 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 z zD2@9}yIFixQ|MZu-9tdc;mHZ&xowUPY7#s zEiSlH*bXGu{kEPgPHBnkf4SfmMyoU?N#sfu5H}|Cf~>>6*%ove=$>-89T(W_rQ0)92KE&-P z=J%S##8hM<$}PPuO0(iw3KMByWF=qRZF!LnRJq!eSVf1kFmoFHAv^ z--lPInn`&nvb7y}oX44-)&#He0!IQE3>!fbD)>vU;pQ#|rXb^p^Q4MiEvL%ay{{z1 zo$kOqwdU|G1$pjY=wx#R0&4h_Py9pcCrRKSHcSb8ZG_KTdPxfG%JgcjhuV1mHHM zw|9P!?XnGlvELZTMM(dG*TGcG_VYpklkVaPJy@@k$ypfM?fPvdp=T9jy6o=gwU4jz zDoK8oAF}LRinO(-{qz+m$!7m$Z4UeOpytMPj9ca z+8>Ewx?Gqov=BL=-P#YkT3Z9IJINH9uiqO0K$Ksz-`wAj)ac_)4ThNRXd6r)wdpuD z#A;>*9m%YNx7^j%-#RRIQTQstA62?a=j zyp4qThM;S8M+Gi^t{q0)PiB_S)A=l-s3c28LSx)j_WN260;wNq#|xVZbS$czq-v0> zs6>O|9#NR1CwXX59a*JEP}Ef!x_a^^<<gLduE}s^ZX8$| zVXk#2nW_Dc{c#yo`g1`J7fO-UI2d#_{fxZW7E%jzLp@xaad2VCA-<_o4p3|r{gjJ% zL`#N1(niAI)9XhStEpQL5N386rDm8enMTABgB>7`#dZ{|eqmY}Ou4`B2-*EdaS=%E zX# zR2ooQrBur*LtChFtfHg-5bOB67g6OXCLTM=E!wx*d3X3>sz1tTCYSVBu+}r-P$8dW zz$kJb+=$f);Bt!*{+ znX>Pm+BtI#QORg5b9F17?~KyvJYs|CW$%b1>M<`>UG;k&_eGo!^7k;8-9c!dz4KB! z9u*$Aj!RK%XM|Gkg2VOC@VAa2;%;w**lBzy12;XZ=m~y?;lQ+y_XLI=f<;E^m&7b@ zuACMMq#_Tv;9M?ob7nE?#-EURdbyAAT3}?fLiWEW@4raxx9f|?E}2{mc?&HVc-|)) zNbo=3xFU0;qF2~t4Akh~QDsiMzE65x9?Ov(w!dXb*gtrA*}XM4#z$_#MdbqjLns9m zkw0!@kN)#nw4Ew|TRFua-4(7nDIeDoAIIvAxaYN{YG7dmb!h(S+r1laPR7JfaI_4PIR7kB#J?Thf|6xXd}YIky& z1-E98j9nu%bRF!jPS*w9#ce?1>)nnk?1LH3*l{YD(i1*7J3!R&14Lx)+8}DTF0~*VGe;n%6u+9Hq+d~@D{evNR zxmTdqU$=qB=nvH;QU=`niIm87ejf7s|MHKw~2o6f#|Ec zxSTF3$I;o%z_xYVu5Pqg>Du)s2%5TbQ|XcVhrT|uV0m>;(SP|7?woMpQ*Nl4ATCXDOy?NXFU&m#`lihKSHSSykfanP&qKdi4`L-Y`J(W1Y;?8rDSjaZfHcW0T zI0x<}byNJcFw3QB&e%iGM#E^N*DRQZXXfD-jyinZZ)4EjoiRAu^`WQx*+w`E+>UYt z%*`~qL&DaM;tViFG5mouVM^gN)~nq`HlY9r z{c|2*Mv?Ee0m}a1V;s|ZjMF3@$r?BZOHjbh5oxsoo0y@~JJ2S;WK~x@49vcWWO+vD zt-`>Iiy!di-GP5_Kv=^PeW2>?5_i81Q?5B*jHj$W} z=X?|J`W5P1f;}FE}hcWSSVd-a$^2A z_o&`4?W!`2c^@!Juhy~}Kr#UXu3@^VDO�sh1u;w{>z0!wK6&ihhX?WNXgM$GD6Q zcX3(@gCQ229FT|Q{2GD*Cjzj`sA(^+J+*9W84Fc=rV1@C@#}9?7S+F^pEe{4o1sk@ zsNU2gxu}`+y*85?0_)jn(OoXw;0Yrqy_Zzk=Re=YMZarJ(I$tos0&Sff&phxB*sIuU^(!a!e0-n9Q1p4^RdHKg zf=>_VG=sq^gu(q+o^gpJfFDp}P)tto)$enmX@9)>Qr3@hn)qKi)XRkua z@iq1}&#DFgEWNYHx3D4G*$Db~_sJhhR*dX+O>g6?!A`Y#7@92|xvWgvwH{9Esi~~J z8+u$Jn@Ug#)Iy1$?l#QLW4jlzEhYJs*?~vnpyIQ;lVSk>gL2*BjaMyar&Irh zwIA<7Zm01}htvIuy#S{nDb!Zz6z34^r9MrTICo8UIbFL7hOxwvvY1u9JZlb|u@kL_ z?cRq7=c%D{tw@cnI-=3yHrqUf2a)}`DNxi$QO^KrQcl%GNsoRhAMGOg4?jg0Gj%AK{wTE^ND=zOIpdSl8kD+59(EO%s)bt%nke_pxT_ zAVnV$Z?Zt#QMO@Do|HYWt-(V?<)g+0iTnD{h6Li3P_6pX4}}`D*}6X{>#@-D+U!gN z-=4DR_doC@byN&wE=pIy?)WL6AXovAm+|s`OA}F}<wmp+i;nGf zt5Nu&)gyd8()jN2P~}@dS!7qOzOBQvB`NcbF6Msw#oWalw@M-c6|G!kQ)O}SC&M29 zND^gOH}o@wAfETz(MCeg1&FFhU@bt>?GaQr9PsW*;nY(r@M46rJrsmwlmeA=D^R&x zlN`&W-1q#<>(W06;#k9>VO2HD0R0l*2%FzhK|bI9Blr@)1sSKQw>M`5w3SKmQS>+T zyU?&0gIAhN-3{G%K(+51A{eLd(ABBY3fu_;)>k9oOI^_Xu`OxI^J{RaGt=akOE4{D zcT2P{=_jpJ^vv(khE^hz)$5yCTl?#5(N0raMkd5dQ(a7q?c8RIV-Hn(e0PW}0h2_W z5IW3%!XMxgaKrwr>iSOGol-Ra_xNV?h8n*~@SRCoy)xqQItV_&_ImM5vybW4jW#yO z1U>3$loA>H@sUKyU+A)_9t*t5gZ+e%oUIm%a{)RpnsnKmJ(r-hpZ_uosrdr5tT?iN zAW{M2DlveFvomThE|Gv3wy<}tJQj5TGM+aX6`fRNAfKa$x?zuI0xBcsE)PbMgh(CZ z+=CV*!Tee_eC~9MuINIm82CZ%a)3ZAX-Zk_F$VUHDH|(y!dVa=7$Dv`ES08ijuoE$ zh1(YTIDx7`-Mca(fKlleFa*wrv8$HQrP)MlSjF9TCmGI%r0NcvcK^1l;;1z9SaM=z zu(*6-bJ^iuwsv1hHEdedgv4d;6U`Ov0y)MSDPqR+VwJHbf{Ig4hUrG5_UL%X?O|S) zb|_gz!!0E7f|*t%9I8h$c32v-$|oghFv$E2)P6c~%E|dwSC{^8=$y&<9P`bzOP0yA z%GqfBqD_mu;d(rLukuTZezg3LQ~3)xXXUt(RU95-9gy~M8JQWQsUn(WIb^Yi9xz>g z%}xA)91hE@_q6|7J)i>X0M(iq4is>6(_}9j_(XdGT^?NUoKxlyM zi3!Ir_ui>164_**J{;8-_U@F=c5-D26I&NdICT^)>$M-DFKTxDE3wG_2r>69swpRX zxv<2gXP2w>Abu_kUoWM^!_byUGS+cg0GcQJUMfxLdFZ`Php^0dqRw&pxU+9?Ar!?c zt$$%}HZz9q>Uu}@OEGb0h~kufD0(F>n3IoAnygvue>gyeqzmtpl=Q;+a z9mbybWE3imO4;!b_(Dl&hNF9qD$}5Bv5e(y?`jMnAth(8*Vr6DX-FU?Qu~cFAVrFpvo10lviju)s|*K&_A^7sKR93 zrO)cG#C7PN-tAlnZ5x%mY508*nIMduom}Mj)QClEY}a!A+@bg=%%8w$?V3p*S(Kkn zsh}5RXvl+_2p~%Q;x0{5VDBC@_-QrW)+nWfFyxbIH&lnEud2BxTK{Uc*(Z&g$Idf`gCjmzYVJ|GV$R-p4Np87r4-I1APgK)fM^tr`7a|d=RV@D|lDaf`& zPn{w*Q@G|iZZI;uFoI`Hr(AmP!JM$a(#V%NBlSetYZ%tlFU?XkJ;vz9qS)6wj`Pry z->#neQaXE*-nB%vGS{a9wrp$RR$TkRtyM`s}NI+;mkf$S}IHL$+C@UD5En6Ueig zDucnf|GAbiyuJ+|GJo=pjPDOGbAkL<*39A{b771`ZC;F!4hc=wTLyUB4blpqapY%k z5WU)Q4eBVq-NXnzE!#Nj{S3)HeZl!vw*>Tob_`DMqVNi6$U`J^wPIEC@_zMfN94r< zt(qlOXZqUw$})~Mw~$=Su9ptgH4!omTBL6}yFUA$T9`E2VBHYF8mVztf8PBhHpz;l z=gojS8Ief~erMi+u?fu^oZa3f%|}5ek|X8_u%n}#!f~44YUWZkx;4uskheO5emK4v zXb6vc=C-E1w9KTwOcVhd8auoJ3qpP=Jk2cT5dnSBf?-yzb>2-u8=cOv*&H42FKRo~ zVGpCg1W_f%nt-l<1Sat4rXt{D+vu+55zqk8f>(g*f;|P()=Fsgm-QH%+w_v^{Ipox zY@y*a~MH(tWJi|71R{V8P2FyqF!;% z6g75l4SWZR_kzih`SwdzfK9MOI8hxA=Wkm{9O2qcUvsI-ka7GAj?95gCY#7}NfLL7 z#gA~O!;&D|1G&W@=)74<;l3Z#HxDQ}fZc8FV-l9Lt3T6NxNVn|6;mha+M&b%A70}e z-3gBk%9bVX)_d;yL33R7)0-haY2PCJ5nQ~DECSy7A1%Z>mB1}6ETh@(0tnzncvpg< ziuNiXWjnds5a(PQww$8k+eCrc*BS2S8r}iX4@Gx&mDQ|9 z`q$sBTgsV=9goM9Dj(&(*U)LOO=Uo0VnalF++7yMNtl^4o%(3cZKZu*luJ z=`n3CZ}fdjueo!ekBJ`diP0*H0$3hK^|Tq9joaE2tBB0ma}Ty{-Bp~bU>HYD$`;y8 zWJB2HL?kWPD8ii_lVlDqZ)_DxRubAk4mouH247 zqAm$j#O5n;O#>%HoK)#5G0v!ahM&j;J!L!T&T2$AtVo1-J~&JUkO{G4x`t=1!@4s!YT_x}gxXoob%bCyT(QTElZd4XEbCTNL}Q813qrMF zTz0j+Pmnz>>53T;)ksDb-v{BIIdT2|^{;DQ^DW9lnrcWv<|IR9%-6bS(C@ z0c82A{MrRn9pc-@Hmf!)cCaiNmdRN7qnR0Pk(wIw>&F1O#^8(B70TGx6Ot&ih`u;Z zzCRsk`Se^|`dK~iA@uaDrS6ZrczPzGB?-O^kfnXTVmvP7y$PAf{FYElMQ3tgbSBzV z{4~?Bisgv=n23n{5A^70*?sYyT|v@ZsNv;e*+l)-7^I?a|1EY{z%bc;*{zXJ86I@! zWD$Xz5yND^?pWNOF=le_!O+2>et&P~U=UmHXus*vkj1zapyQ$_8^CS{3g`HWN5Jwu zLy#V_tXZx-q{fl|IT2Faal^(S;fVOS z6$Q+Xjw?A-`PI*R%%oZr@Qz@@2TyGdb$O@Kkc3!d!?Kd$-CZ^s6+c{Ub`jo%TA2r^ zy^eXciAlqUrh2I^+FkWnNq);>g#bt7SLwjBkc&tidO*Gi%FU@=Gv$fE77Exr#LlF> z=h}A20z~q%-S@0^5jdpbE5m45VzSl4Oy@!N^PorX4-(A}cQze2vS*WJ80f{0v>I$r zTnD#pF1rz^zkC)eFS<9(kQK0rr^B$hWAt>745i7Chzcz*X3z)r?6(0J5KW4=RRv3E zglf%gcKT98@1)aNYh+8m+hh>V+e$T$55QAhis($Sj}`}!6m3vAZCZ@X8!{d z9J+nGGy_V5Engi{5;S5-Ypsn^W)rdZ^VOlr#dwaI+zsqIL0o^s@WBmx5NFp?!44?J z?;x}AfgVg>^6eJXBlZgzJr814a^y^R$*Rk*mAonH5Ei;3@nOL2JBaNV9dfF$I{SDT zv$7I(G!7?y;_e0XVLTQ^apU%k%|ZinH(agnLmgCx?%euk)!W%$eORQ{9|U?>*O4p_ zNvaTfVEAxeU|rP<&(l89ZX|rXE$RjMc+~-GsZYdOXyGrfRNb{6N5;alQwR{AXA=j| z9@<60_!)Z^#W7&#@Hf%NLf1=}0g79Gx8rvV%?c}qn{@cv9-!Wz>xGNroHB2qXX=_o zh&>|m2r4V|Jfn{_dOkP`2Q%4Z2xIWktkTYOpK&_FNyeB1>p)CBMKb4$!S!1%=HA%h z=}J9a`}zW8n%nyE=9W{(1JMU61JyZlHRz(H$|F7YBRf4YBllEho7ZPGlilueAjo*c zKl6CXV@NoM_5-L>^IKR0VQZL`L5vbrcBb#S2Ly5t=1c`)$R{*JVNbw-%bE;tq5K)1 zin$NZc5L&@a4IU!S_P?v0WanU0t6?l3^5Wd2JkQO&v0cp7Ua)fdz~ffxwr_a&;n7G zDfE+bDYfRn*%9F*Taq%%Rtd1Q5$iA6=Wy;c2cN~ox}k+7*OOs^MJ}68-uVnE10p8K zFTh`}Ycu+*nzp}mcDwYyen+kEGfJB;+IMPK!{%ytV&-awbeq+d(pOGd7TvCDhPY~% zM`MLg{_s+q$>LRM&QoQUs=l1>BlxDF5ffp-_|wZ#Xh%u=zdgR1u&e0@#&Z(2MyGPW zK>sV3;f;;7;kQmy08=yX2PTYa*6iDc1g8qWwn0AP11G)l)ohwqd9f}vvS=(_TI4H? z(ct&JYmjW(b}Eo2a)s4&%L2Ywh|!*O%SAnyNhUT;9y6i%obZHQSB*IQdpTX4mpX-= z1mq)$&l)Wyfg)4XYqWI}8k>GnxeZ?b; z=Wlx<$x}+>X$Iy0H94x^#RsD#Im9QUVz147LH6yy<|5|T1#Ihjl-{?nK52I{X67(< z!iZ!{>x=Yj>XcsUxQ$l~BsslIXE{-Wy&3}_n>RpKDlBAK{Evj7`8=Q@GrCPwNq$I{ z%F&a8k$BL~j!lufE<2G!Yda^Rcrn)UbFk)};y;T^-KT^M>pvrH6LaBLuZ}C9BA-(T zwyBWL7t835l{5}@(zzboN>msZ-#rdc*yP_ zj5?g-66%OKnELF+j=Ukmd&Pbv;>TKa&+gPMikt1t(es*Y^t6`t^L#S z@<)GK#-NUhS(@$ndu^;Zz6?mLYW z9)#fEk%e;8_{&evQC^Z)15iJ*nL51x@l~{MMpzFyD+x8%*qyrci8LSW``^B)Is!bU$g@^=Wo0*V#j}DcKXwqbc-YNmVupkfIbjE126L; z-p_M{45{_BzPM2O=HeQWQI`^Z;dL#HAl22W1f3e(yykROyIxbIE&@T&Bpr)E3(?*~ zD!YSab%EN^;IyxhIVIt}2yO<3w8@CGUQG;uPi4Gof6heFJ>uN6r4&{2B%9fY)VuEk z%T1ZQU(!mY{$!iJVTUMkclXQz(q5n17OmUvvZ;T06(Yjgepp{+*AF{kw#av*n<5#T zs5tO1WPU$<@d4oMn@L;Ls|XtGddkh);;Dlp(}V83J_J`nr_Mr*S6Kqd1ZF<@v;yx8 z+8!0G>j62+LUH=VNkKwR28PhVLP}{M448{`3broiNs^vdH z&crUZR~)8vaa9kKcBJw=MPReY@oPuRqqx=-T3-c7lL@xuqk_=-5#(I}4N~mTk`ZW` zE>;&vyr~MMlkOXgq__ZF(Oy)>3k!$PSz&sFN#vWJ0nwK`6J|KIJ|B-{oup?t<$GY` z1j-ry2C>ynu?&5)$P#gM^DVtuzT9E(60U{$z2z9S z4SRlt5r3?WGmW;W*I)l@lRy3;{{@cil)IQ8rwhB>;nlds^cLe85W}!e2C`TNp1{s< z0>qFbqn}6ZynfgsyzP*e3bazb2bIY(ak<7rCVlTQcQU02{;8#~v4%c`+^|&&2ndY+ z2o2r(eG-mo1rCZ`(Y-0MQ|k_|S!kg8xW)DY0w!Hrz-4=6lGIYSe`|W7#Ird)ydpmA zO+jXB_6nofKZD;RFEi*&ulz?stUk<=7-C-J)e=0DaZD>o6*x`8VCTYuG)Sm8#x|Qq zvUu@DMREy&*_mwy=y1(Bm8ezd6>qi@JvFTm-Z@!R)Lx%Qb4C$8d#5hRAzNux_f@EJ%&ivG*3aOjyPR9$rPODi;JvU6@wa3P*hpPpp!Cn(w{ z@_ReTvy-HJaKr?R=_D%`Ng}kI(>$jo+1aU;jS{1X%M;zLmF>xr(ls;oXqwYbXOczc zt}8P;iLlDVCgYY2@%@ykw@9|MHbOG#tGs6Qy@U5yvc;hh|m+iDxw#(@8G}<*-emMSKnuOG02kC*1&o8?azLi)_$P zOIfuD*dJBwf7CF4LS(CI<}w%?d&uH9r57^_CO~li;Tl^rqRAv=8O^qD8@c6pkg z-txcLD_G_?RSu_N?#1)g_5Jg~8-r{p+JO85atKIopO>k&0iW#7B#o9E4sJ$snnGRy zBl^x2%` zy3eu#_p+bvu^3aRPWSQ(D6^?N#W=0MrQG3l;pwm!IfKktbqrD2Y6nF2d{^Pv-#X?| zgtQXNZGVO|GQRoquzPmJ0wciXM@ik-c+8~F`2Evb%sG?r`nNlI`+lhp@V!G^I0DQT z^ZL)As@Dw7uF3A=CwL?_IV`ZeBb-BSXVfBqPDAX}L=;r<<9hAP_mZ6KCirn}z`CY# z>OiX+L>8F?N@YAA3$aF%rxX&?bLv2mGW=TV|5tUs!7c39?Ab$j zA57buCZ$ii+ufh<^Ncz=z+F|x`11Xm`Pp6ZIa18NIklL8Lb6S4=$QaSp~+`b&ov4u z`Q6&=8-=Sw51`SVd5$O3dv80+oMbFoxU%a8U&m@1ab$_D@8IydXhr1Boe+ks*Pe6} zfj#xoszC8G<=P1b`5?(d?fSGK(DQ3q&Lw8F)R520jXh_iuG%H+#PA?KTKF*V{MFMM z{K^NSVEUrmp1w1T!|*fxS+YQpu)X1K%`p%w32>n!u4HA}uHNki)8Rh%BDO@G7q7vG zPL^3Z1T}@>y>DJ^9c@==07%%oDgqkEl%5=U&$d2+&&AFOuI4da{z|-@6U1&V zd^>P}5Y$rclayh%RY$3V-E?A>Qz4cRhdwP7&-nF3%mO|Vu z|K@_HW_f*-Pmu|x-`aoDZXijA))=#_?Yc210 zqfCV#k?_HC>*<`_P~L*!I@xcs>HSCp_s47)fZbJ-bVF+UO!&_?IA(O1!TBsgvv_}W zZEO>K$GV}CA=*LY-LJAgR07x#CtFAs*<1UkVo{D8p5#Wry6WdNjG@yx=;acSX1Zfm;O~H7J*QfKj^WgXx-C9fGc&Cdf z*IC)=hNm*A;S$f!UD%?^b&UO`Nhi5+(^hBA`_vR>bX@ela^yTzv8KZI=E-Bh?ALzM z&Ms!QdS&Z>TSc4r)gdu37{K%W9(orF1xjwnmipnHDU7d9ND}FxMGxTu4YY7-Yd@fq z<&4k#GYtyrj4A#tz|Ip?I$Qef$@JI^MaM6i)~?h&ZBA7@V#G?L02;*_4#dA2 zn#hV7?@HObN)XWdetC0?xq22zK|8FilPL5A;4CzcvTqJ7bm$A197@Np2l-OP0>zCF z&>`pc*hj8)f0Gw-_ZS&wn;^YWzo!Qt4r1nVsTP<8`ij{aua!CN3dXi=I}a4_8u^Pw zJZQYGDs(j$03_qyXB{UN<=>nyj(Q$->>s+KUs2;_-1Zs2v%$DfdDpuvyF8)-nU_%x zTJwf8R?jCtH2jDZa={4C->$II=*%vd8mVO`33yR09=qK31Mcu|%nlU-77<;;ch8$E zY|bf1$J-X#=N!xSIa^f4N?BhlyHD13BWS{cs~nU3VS zcQ!vy<5SvRik{|@y=%Z%xO(j3TVP}{^%Z^(r6RRoO08MSugRwk-r?JL=3A)$m#bs$ zI?{sSD$+#$lgPeb-nd1_zWzbZJ+g=s&ylb%T*-mKAVb`7O5XhO_0S!@K4KC;R=-0> z(oYceo8U~c&9|cOjyTnw`2Xl|0=c+?|DrA5ySPZ^F;UT0P1%MB_Z~Yt zzAdWWe!=`(VUR*1!HuvYIr6}cOD#FmxQeBiI;(wfgTf&js%h7k(cI(k`t0XG6&Q#p zyaX!?%$QdFnofMuHCQ-duyl_)G4_FYtoES`#`0X+s(+O3RY(2!RM_YVk)7Ea{%~_~ z7B)tcntPp^VqZ=ml#$V}B!G)<^g~_V92P#35xhQ&CH%F%p*b{Wf;sMua{REPBG9T3 zLH@>}LbNtWX+ze!*706@=RU{G$MJ8HD)wKJ&Hj1X+aj7GT_2VFus84B!&?paCll=1 z_sjk($o=?;N0$v53}Xj&xj5|5p1aHOMYhoq^gJt|eEFA~$Ecv25+idaqMhKGDuh-I z_hB7Gl`W+^mZ!vctnY;iVXW4C-$}-akzjID)h1hD^WGs|agVl5CwgW@Q`Jb?5u9Zi z*7(@@fFOUSe&|AO#|su8Os%RQ!?+@}0Xfd$1!oh0u(7dKU3oCa5hZOCUnpPXTG z^<0F$0@-!SGqwa83^rT_ux`j1P=!hPY;;n4r8D9~3oENdnRVh&R{S$DQlK1F>&~tT zQJM2-Lff*1k90&#>9rusY>|$N{dWY1 zbdW>RyP+MZC6jO0ZaB(4E9Q z_g`k~QFDo(GrSa*YxfwhdoSE{O#7mZIGnvt^O%y7t;uy(4ZSuuugpXu#(t8qhBzb} z-6zhDuz5gW_;3CP=E*7z$S`>VSDG;XHiE1ss5ic*+2SYt^Q+@-vPP4ajZ4lErow!=yO8W|5k(Pa0vtFL&x7szx{|iOmVAEYUj%}hLiaPDqia}(+-;mjeRXMK? zS7Rea;TN&?6W6_QRf0SfE0!sVTb!;8Kdai$1gu8daS^`32|)^k!946&Fh=0J=B(n| z-lD<3maN+hi@R*F3q+k5Y__%0~rNhL=vX%XZ!W1 zE(64dZB@E#%s379HA9b-h&&m_p9VrUiMy-mJTd9rjVK2E6ylWn@KTo?Br6T*MsfE! z?Wn0PX+O01E-r!oCZfKG*RVS>mszc1ObFxABnwv zv7Cuw5;|h;st=AtWC7iUg+hum(3Cb{HRS@vjdj38gC@_Q@fe8>(R}`HbRNu#UKF>8 z2BB+LN&)p>mmvGnhx)$-U8GE|U_*vjsfEK2i>b)_GdEzd4pBc!%t1hJ^*)Y~qE8VH z{JjA~11}pA(E2)0dorq`VesyN+bZT??B-|UgOl?AENBN?u9!;EcMg!kUh4W6T7*7e zKG8ABoeKB9+=t=zPuWD73lt>NAfi|c8KX%3EWF?&JsX{Soq7%$(Hs!$Ya&<--;i|8pTNnC#Vbk ze7+4a@;slnC^E#ZQCB1%?%|(`mNGsglP`LVN_;oAzfkAoBCZr7hJ#;S9Xvdn-&?YH zg`hhbFC1HddvZ;=tl`(g^lwmwHspvKv)cb68)R?Pu83hTCl)hOdset7)%&Z&83Zlcnmi z?loFa2^DPpvXoz~h?@9^!do+f3c;t}i#pSH>t+9}OR=LQWl(6ylWa8~Lo>m*`coT$ z;Pe;V2P~AMOfO^?ELkEca(nud>iV09o4o8%PqSg3712?*(iu z0w%YgA%F~ge%tA8Q3s|`N|hsV4MY{HBK>JBkKXH344Et5-Pp~9j3dfUd&ul6P0GNi zx>1(|&1GWT$*&RJ+dw@DZ>W4NV*SRgSf?OI^Dbq8##TWkJ}>~OBZ7X)qc5=~)3IRu zni-W!<8LMw39A5^;&XFJb(SPVd3aSW#=Dh4h}at|C3+kic3vssd3mqEHpF0JVKX+O z*JPYgu;r)TR^mDcRUE=-nfU1gi61_wO}ubBinu>&;Wj7N#{GIiPBOyl8meMQHLtd5 z?z9d|Y;ZaCdKC9zK*KG?2$9#B#{Ho@^zm|PWKasL&I_EyB3*eiuq@~l)^cg)(1{^EtUjO z7-D5l385_{{EOj3*WLE3y|T&H74(SfA_KIv61oO>M%RdJ{je6OG^#m*z=i9&=NYK} zJtY-F0VfMKm5Pf;zkE}EjB|~nrH}kZTPJlG{(iECBd*$~0Xm1aINx#f97Hdg*uCvn zD2fSu8gu>z>QH$Nt@1rra=@>ay<-$Oh;@Bp_}ejYC+O-$nJ8{+zp3!z-{O&U_m4Em z?@cyW{)UY>gjZ=RAD~ z<$IzBt6Jw8Fitfb+6U_d4E%bj40vHXT_1@7u?B&I7t4EBHtVzPLJtt|g4;jX*H;&R zcS>t(ApnAGz0kp$8OMrB>I;B~w8M>^vDM@euxxe&Z~>gEnK)g?aB2yQ$A!|qob*Rf z6uZrMEN*%oly2fY7o3B$#Rjc8p)MqZf`nC}#jw|ELZs2dkG0+bN|{R@dg@QYn`lvK$fbVS}~2^!_0 zgF{ebFjkt4f_9Q0qsz$v-aX&o(Kv6PJq)ZjowK^Kdv_NzDv==X=T?2G1_K@|u-h*I z5Ix0Nfbe8%F^2K5?V1bu*f-%iN3)f5K>b!r=AH_Y6GbVRV@&2{Ga}M?wTf(14q5?#FE(ph{#KTV9oUP9W|!?t<)9*G(L)K3s@d80*yU$Mrl z2G)ljf3WJH`k5Qyx?h3o&OGO`ueMn@-W-}_FUsJ_aroC?bUmiVNJnhaI=V)pvDWAE z^>p4}l{^cN?}|h(mL}6ig!aq-tqtLy!`xOs5Q);C(=7bA)I{{6dGy`HHW=_idizSw z+k#2gEEQ};wliG*B;V_PsJ^D9n1!DER%KJIPwdu1E;AQ1Gj~RcYpwt6iuc^hf_&zO zB9Z@&>7?ws+@5&b^6nh;-{=2-(q_zekY3V6LyuL}YP3n>6p;3Og=?QQ&%WBK>g(32 zvH3JH4AH7Tj->Fg2nFsBN;_ zsX4W@Z}CC;cY^z)oKtk1IXW4a*gl=tkX?$~*MAszOQ-Vqck7$2)C#`w%Ra)|s>y6& ztRH_(thG`4tfnrLrBv6s!4oSq`2&+#^Gd}r6Z2}+Hkp&HmUwLc5FfK%9Tmi<_OCT zyDUo*?~M;+v^%RG!@XCDM32WPyi#oIf)toRgwGyHzV2iSPO_~doO|05KO*s3wdBo0 zdjV~vrOh@<;?1rKeIcL2e z`NX=`x@NC^{o}U-tvDH2#zahPn4a(Of_(L=9M7)6tZ?=F7l??yjn-c=Q2v9g5#nqz z%nEWM3g7X#x%?-Z%ALFk&@FL6JoQJ5Mg`W`&mT!tu zdRVzy>R6!K(G`qn?5GHAz*}ybpTMn5gKkqe1O`o0M43LC!2W9gg<(6>|3MPE;7tH2Knd#~WuTh?+KU7Tr*(GNI^(HIT=ndntiw%!ClV@)@ z>z{l4N8_1Q08vxn{pu=io>rtSPNTo7_4*O_{JZI{9GSUdbs6no8wplH@elgyKJ_;A zoZCa{W><%~iqB8j2jy6L$$a7(>$P}?~K%}9kxPkffmj)x+rJly2V8{EdO$P04 zu)kcRzbAnv!V-sxpzWAXssrx%5j(ViRTmJ{K>f*v&G=*uoWPAVV*XV4+B{?;Lc3+i zB&pv@@Cv5t#b?uoqUnCwf8boi#`Oh_~N`>CEXYW9|UK$d49MAa^W7UoV_C3_!_o zJ*AEVwDxpP85fsnv?*N2xHi3(EbmFZ^CyydR-Jf=43vJ}VXEehiemfb4v!s|%|zR^ z%C;g3vr{LKgq_?IJ-zo37f@4O6yS=Yb9jOm)9Y(VkGP&9z1XVAmI=4r=oKCQ_y}6^g4gRyh8ETP0v<}>?LY78V`X@ zxZpB%hGPzrF0Ag(kRM-;O_usnoe4iEO=ddwjt27Mx5t2=1DZZjuVYM+2k+w2B%>Uy z^V2m{63_Y;BY%KmtJY#0B9Ery*G&hBcsT|zy9f#N57UUn@($k-w24|R(>{w#Oe7|R zfOuwRJk61fpGS5!uKl$e+USK^M#ruyT`H$pvhsLjW+slY{8ayO!`xz+o9RQv&>DSy z&FC7@>C+xMrjd9))+`#w2=Tei;RnZ*?e8bm>TCSs735|$Ol7r=6OEYrPYtu2YolVP zovPY+*Q5!cswUJ&pjTrT}rHD0#<-#A|v#1wxdY9M_=^R0EW zZE`~Cqe7>q#n-X~5u^H5JYRn+=HAfg>6Paq{LvSayy25T)6Sm?nI%b_O0|~LC$ImQ zSa6l;D{*T29Kkf3LX|)BW_0=`*T7h+Yh@VTk!0L}-@`XaYH+=5G3U_g!Hn-PfORdK z?C&wWr>rzGF1Bv2qouebHWzi-Z@j*UWzub=wbzmR8UaMe`UVwe4;+w`oj~V6)&zm z*JTjKvo$DO{QRD8^eDGEMO0~vH(>o{o)nu`-oc|me|dGirsNnH%5|#dk zA7pRFd1Cqfia!sZcg64}h13DZsD&C_$!wcIb1H+hp@GD~!%$-z*~>x^0(l-XFpChg|M&sBpGxhV>`v$QW@=YR*j@&tSg;XzzRO!j1aP+y7eLg*-qVRY7IeM$@>c0T(XpYCV|# zEZ0=gMP4V+R@VeRswY=C0V8{^Fe%8xxchtNjX|q3Rd5U5^3sOPo1F6-9q!%eH0M-o z#yG$J^R(RE-Ww=myfCRuyXig9TnQod?=_j0^Qn5%+EsBMRb*=U#t~B&>K1Se9@vX} zbCOQU^M`*tOH9nPDqpdx8TB|i3NK(NVvEnDv4s+LVn3Menzu$=IZLJWpG3yDm%h(; z>i9jed1p0ZD()G}!Im(Pc)3{0xDQs?N3&!txgS-Du(Sn+pwD{J>SMtH^%^^akZx9^ zqsWhRlIX6*Kt0lyLGyGxWS;!hH4v~t6!lVkNq2{?iz*In!;>-lLgN__&$zJ@UKeUe zk5dYtw;lI`IZT1I`mj}8@h!mCaq^|t=ck=?xNXw*=!Z}?a z*6XvWbmY86yv{upon?7hc@1#d?3WuyW~rfi!D)PQ4vtx9*oP=Xr3h_}q@J|ILp0ml zFCR-dh%1tM2hXB5z^lvdh<>rpm?t{D2XH7c0BQ^xkf8&DB4WwyCtv^mK(mI~)bzr< zRn^z;2PA&*#PXrA4}3GP^?9W^>%oSc01bdx-l+*Q+oj}xwPC5SgjyiR)Hw{;W}6}) z4}-VBRKe*&yy(>y+5#;8X~}1x2l?m@8V0^RtJE;K*Y|8%#nhcbB^oCvKk!WVw4`Jj zU=XRm@V50c+x(<$^7z$TB_m-2VRB0(qs>+LSGERFRWdalMI`X@0?HGt&(h`|3x6m5^z57-~`hS}&WIu?iADlcq2{sExQ$p-+11){tiFFO!L58l- z@)P&K^%t?GZcn#20#xHR)&3Vb=Z&0t4IT~g2sWhQn<#k3nHD{1@3uHCpr%>>T3#;q zompz=Lm(Qoi-kr~6&F0r;yfoN>N)US>L1=@5Q>h(MV@b0u? zV6p=-T9oGi+awy18mW07D`lTBxGj9h;*xvFc1LP|G~;pdWuf_ZO{BGuDa3vu|CR4g z-s7*zEvhAHIa%~muG8e%;?JJH9qBBi9$S|i)~P%?-SqTP|0T98Ic{3tK$(kukLm~# zO_TNy(wn@U{;b*SW#$twVB51q8}0U^5zUnu&hqvHAqRra?p5{`($QC$4XGu2LKRzJ zc#1?Tf=#>rCkK4qm3#Mvp{|&L!k*{6g0a0=1bw*Zlz=aH zH!2@8221w}bs9No;6nR^GQiD!_K{hIwWF~8KhEkuP9}3{YkdTTJTUp44&X5BS~ZW1m>)RC zBi~SJ=n$<0W^1GD`Z0c$oA)qucojKY(o0nm+NO!S91lCpilJ}Dcc8UAdF{ebIw{6f zQ9aHI0U!iCGGYw#Q5S}gYvnbp!hRtA4?E4?PvWAn}6@``3$*ujM^sNJ|dEg+Yz+-X;pZYnizs)RHFF{ zd`n0WoRW10T@a*{vA;|oyRBso3MraEY7zj{1^sj}ZJ4Dd3#4P^t$!kVhwPL(=ne-;iFwx;EkBv?h%}wYrFlC13Xd!<7OF5w zu@x<%@;*&RVN}26Q>C!|`miLd3^Fn$pT8;l){vQb=z6%nFuQMD3AZ>w#X36!#3udgG({ z>dic1f>fgPRD-v*;yxzRs?kJ!E?0!Tq`{Kt!D<~_QcNOjLwr_)rSk?~^DEz;%=kD> zDBDijB#<=?Z-4d6o}EGow=ivvJ~+?24V5wc+x4q;b>EX}?O?PE5FC)z@fZ-uWm?*H zEz((19#W)l$-PzG@;Iq1=P6(q{v=t}tE!glRDY*omxS8tcZvv-_y?0tXUfp{g_tI#AfE4O5r4V&R-~rGJ8O9kenPPTxbOx-GXFNZ`XPDWgAfLr^B=`@mp^P0UUFFf zl0MHaga(SoN{QCvvnH3MYP~?#<)5$&jiwh|m2B`ACm-IGZF`X&B?K2^$YW_Xl zVZZ9QVmn&c_~6MC5V>QSQ3Yej+6VrI4AkQD%yS2vh4$D+)g#e;_BT8p%?891`Zaie zPM)z>{Vnf>pQP$=cxk4~SZY0h?Y~9}kT@B6B|2sY%$Od~eLaiB9tD2{r}e2uI-@OD z?s>-7-v9C~_V)dlA7;N;?0tQ>9_6rFc@ST%U*FL4iP42)`>X+X!~X}f0;*fd=Tprq z%?B~8kHo8#ZGu{y18{+}16^XO{dR*x4e*=ueKuGynT-RhuPpZZb$_&&I@g(DiTSN3 zv)5)IGZU}3y8bfQID3%+SFeI0j@zT&!bM{rN;rZ1H9k+#YoP|B1x~!t#CFndhfHS^ zZB(BRa<6~x^)TYc>5QdX62(L6oP%4D=Wc=sKJ0(M@`p61afN3-V6qnbWieC;NG7Hh z&|L#Zh?sjBb)DI*V|03&x+N$;mizHAy6zSYV(Y^G8Dn~^b!IOvvW3jzMGqlO4B48| zk$=U}y-MGaW$E_-88j}?>Q(L;_{IG#ggf{hwJF2KTNDl1DRPE>ETi8SLkIBu#%gb( zmZBsd>$b@Rzwl=DpG(*9`VGEd4>^df5U^@zg`vjp_&6g8l~43g-s9;V^){^@v>K%o zq`|T{AGYmUw?@s0zbq^4+XSFJ-DI^i_7AZ;pSq8r#A9e!((k_XvIR4Mgv|BMz}Jr=wt4L) z)c>MY`S#~>!4>0QT;-(WO3`GXW9Eia%mSi!8#1U7$rudTw0bbjvU7V;Yh#MG>-0eL zkg2!NSVUdwq|XZ!9FUJAdI^cmPOI2PQHN})TaZSU|0jz z|9D*RIK#GMMc3JRrV>J(DB`g>?IR_AuFElyNh@Mt&*$G>5H&_!Y)ROqISM1r7FV#y zUn_q1p7E1}cRz61Be!kwj>EE`v5{BAC}f>?QaCuAW~l24s(R9XdMfp44uYy(n9ynW zNrHxDXfLN#?fj=u&Q=a9u2aghlK*$!VH*#QwL-!#Djq478AprIW{S8!d0J~tDE&Sc zW}IZ$t6#Vo2ZIJ^*zp9E!2ExXozVE9e|u?v4*KH;3J5-*45U#TAY>yes9!;Cd;-}n zWn(A@45weJzg|Tjf8o{hLko3?bo!_MZD7`>Hut~8(xkoEzhmNFy~Ut3v%eS%7NAfD z3FjAVph{q!3KIlsb7xH!pURw{g)ke9dnvPfc*pj+jwGAa=wO(+*B6|LYg1uk{8O|! z*@PUgpyjL6`!%EnMPRWkk?48RZLNMPEu(i~Xkn=C#dYC-eb7}+#xC4?ERSmINVD0- z!-`d>1%ale$fwP}_}kFBNTYd=ZrWQgDRDQI1jC6mUckqN!O5fjwl!E+itmO!Y2@n1 zBhzV(jsh)G(`o$~xwv*`x3qkEQ+#gc5VO<-CIa&V2dF~wzfA?wfMJt!t+*GUxqPXN ztEt(HyT}9Q(#Y=Tc@|z%Tmv83S3=oH;zbQdJwQTFQ3*^+v>aX}J5PeTHKox%0D7bu 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

M4OvAqhax~W z?iWXw{%Y`QyA|kHM*lIqTa~h_^l+_(FA#s&WEKR=(|*cwZgWpJ@LsSram zcdMwmt&m?(F|hrHDN;DEjYz#6YC@)};bR}@k6tBgIZ+R=C;@O(yypT@x>W;-*iUsL z*u7+WC01RZCIhtXi8m!w4LOkVc9XKR#H_V-z%v` zn9%`z*RegNSfG@qD`$Z@)u;Wg!F^XTLmg=|x@be9SANjp56E3KoT}LSpY}~-+3dg9 z+|(-v zG{L@lI+j`j&AaluYoz7Pwk05vm@LE|=?r`O`4%ktu^6j_5%O={dIFXsNqa(H|9r3f|83iK_>b;N`BoP1KIi)O|gf91G zPwx)WL>N_AlNqcYJ~>K>X|!1jXCUd)tixC8XB$?ajc2oYve*Qca-B&3v2kgjI#fFpYi#DpdzAvTX*}PxRo`D8hA5;839%=m=(&h~-vv-+glf_}l)9Q`xGe>D z(1bAF9~6-pN{4coG%{6eXV^ZaW5;Ddj2UJTZbZiZRzS>&P3T!(O>)BBC4=?hRK=-2 zqSw3~!)urR#`rO%N{+Bd!UIC~YsmGY{&QtxoSRUvynpT`S+@M$;y%$={rU;D za9B~*+N7GtG&z9Xit%LUG8>0^&QNj9?x2{yPqgXcBklp8627?_>BNh(GqFOjLaA6_ z2%1L>%jltCYU^?sU+lC-8~bpLa683wri#4>`YCv#0zC7^!?vPqv>d7e}nz zAR0yyc|N|zVX+26uNl+>bomYOS2wbyW4e$AL=|Oe`7!pFquWKO=eZzo#zs?q%;=x$ z2{VP1V|K2@g9~|gS!C#jK4$pliqW^7X<#_jH&kTLX6ty|0P?7xO&T-#Q! z`nDJRy3p~lbt+JGnFC1FZjiev2K%0AoD?y#Y80*8P-_k{{rc{knrgX~)24CL^|ceD zDIfse|GQHEM5H7cpq7_`Ni4^Rk;h8HGO?r~p67P0}KEP$sly-du-T zO@vO7wH>MT?mYL(qfO731$eL60N~6K{HtPGDkB2xV}?E6p3SIOO!~QTlb6F-C~!%) zJl5N~Tunfffcrygztf48)WD2aF$1?2u3{EaQ9a6x(Secgh2L*lQKggLLj5nGTnDu$ zcUygj5~sp&TwAAor)RDS9;<4>KLV;g*1cJ`Ree3%m`rT8<@+nq6pht6;3BSDnX-PA zfvcurgE)C)fxIj0d`Tijtzlk*)v&(uN3--=wzXZHeE`0nVYMM(Lo^6uJy zvqoX_@vCmlP3}r}4oeMxS@Di6XR*d3z7E_uHT&nZvAB;GX^&1UKMp1&gc6V6i-|XJ z21lMX99>jq5K|Cv^C50V4_v#+`5&Ny6xH>53|%K{(TM6_n3vBL&ORjWBy(mpG6hoU z0Cb1bQb7t>chI*XgQL$E)4-O1^jF2)O4}vUJW|6zx7Ooa|6-qx2L_F|Y94q;i-YSo zhr~8E=7wz5_as1<`_9Eat8|j4?cPl4-y!;_7`({t6339S69HHWi3CXNVpM$NAsd6p z#H87EpG}(?IpJyMc*;~Wnp1(=#r(mXk?k9vl`EaYmBwU=Tk9Z9?6tT5#2xO#7fci) zR`;}*yDf>tuTa(c#vc4wNR?X*_}!nCJG`uX4Re>I;BEG`>ssu_Nl&gLbvmyc*}6kr z(QN82qboWY!&geqvM6}Zr)-n+IpEd|HgK0e(?KTdbge^gt751CLW0F}t-zikXm`~r zuz~PHK^=k<)`z92n0_z5+V?Ap-Ota!Dwye1!)lCq$4;urn|*Db?zh|vvw`a{AG{-q zNfJcH25l<+U*Y`m=Fa1Np2h(iGNR9v

B}>ZFkegJP;XvTo{+gq}sWJh|_GnS31Ey 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

=XXA8q-v|xSC`ZSQByq_j_iruG5y@^P_|p*H`AS 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

K)3cr~E>O8I1A*LRo)gFvkj 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

j+I!dxWrJ*7s)A0 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≪*}KpUHi`FPQjhG3Oe``^ecJ5AHTEabpZfsg;i&7G84g z`Fderbs4SnsD&SN%E6(8(~~9yBJBp42Y}Z$@h}8@ye3`Y zd4Op=&#Lv(LG#P&ob$oGfN?*dWUl)sQT^-uZZopvALsGsd#^SqNDok1v+$!XJbLdF zf04^SyT;vf+#CPZKME&l$B4u4I9}<jgA^G+^Z&MTPK((AYb5}aACkE3}i=$$&u&wFJ z)k8EIBm<~_KTpps1jp*#PV=M|W(7)yo3Q8`10B>%g=0A=iYa@y9#o_~rCjvAvxpI3 zkoqueKIyL_XE#>p<>W*R!T~foezm_to$OWyQIl+4!KtXL(q7u~*yy)0(ttI9%DUD; z{;aq7w@*5NWm>f9_|P_ZT<-Y-DDbYt?la>Lk4e)hF?dc&n$)1GABXldx&7_6i9O`I^%aUjDZg&h$BuCghqDYfRF^a6Uf5C z_jfwT*vAPV6wCn2%s-DMI`U(1u+_slz#oXzMX{IPlv{9i7l-B^Y~i(A$ivNtU$kf^ zGyDGn8NbG|FM7`YJ!}T3cXPonw>CDI#-|M$N?iTshi1_VaHkh(Nk;4gE>)R=uM0>& zf%tnu^*0cCXQrn4wI_xfC5Yy_{l75N?$Qjl1&v{G5+6s%%U#G3&D_;?Zkf)#t%a%g`f+KIs4= z{Et-aaB4RmrdMsQDuj;};H9tFE%8D_vDqfT6Tge&Oo1kpI3U5r$Z$L)b(W&xY%fA< z`!K&GjgpsX+Z{y2GbwkUrI^h$YsW_BDwt1z=qk=W}!1N3l1J^Ya;Y3 zC~vns%jI&+>VNp7$AL%-Z04VRA@6&{fba^pmU4;6p5{S%y01&(s9IR~jYzT$vfk{V zgaGN0jjXoCm0zF13jJ>>4s-~j*8VXEY#-#sFEHL83nbqEZ>#g9z{syUxF$k^Pm(Qh zJ1p&3E4=E~0pmTENeTd`Zz#JW-H+v-T70qhG$u1@{pPYhr-gV>k2Rk0&Gj9GI$FTx zqmVtOe)S1o(0M18zCtFdpyZ8--{s6|5}%;OUCK07t4X|m8AU7A-4$WLjwTSjKl3v1 zOMD{^{Sfny)1nN25|sQ8qZ2_nAo7jCjhKzRf!=X08@E%}`fXGLI%3}mAlH7JeZ1tj zhH_Z7o?L%!V&LC~qj#uWW$pGYswyt;7&>_baQ2VGZTvIvJ$`JIY>*ES7ky!so>N&z zGJd+W4P@Y?7(1z}?#}c;?%K>5C(xlP#bye|M= zzeQOB%(L{XF!?~lA6V%YJNBwt@*{^lPDo!@kE;Cp z=R&a@{H0f<$!>;+`}B4F$qTo$tv)#gk-v#t>)AV&e*gQA&BMpHR`&lQU@0*maA3qPM7nKah$I|CG zub=-}uy2b+h9E161x@zw+4|M^7dDCv{_#;p zv;&jSl7aAgMzbeEN&4|cYtOn-4X?yS^3i#8#k}FmHp=eC5q!6AE)`Qv)gpTCibvFB zLB4bz-mTPF!_tDoz-Y^mKySgp6WKe<0oq^vwEvp}{Md|RLY7gl6_+_Zq>v|{u-8i_ zU+^Cez1{pOS9KT~?;MS4lXBe09L`dIOL@YLI6jber2g!Ba%Q(9vkKW?3>}PPjpy5l%f$WhyOiPxts})!^YqDq`)udGBZDU2iTEGx8q) zYIQ4!E|^Dv0-M5*x)d>kuw&{sl^!EFc}%MQhUrEvJy^4cGvAM=j@bw~?TbvSgx0{? zH|MDhG7hp#v)0o|pkrDY^)qR5--*oEKc<$|Dj}OG?B+_Ky?n(Kew9k(RZXsW_ zF#HDAaXpIkGpnOJ=0rR>ono%HPYc&gQL2$qZ&?NK5p)eA1=y6zf3Ds?*AVFsk1dPk z_6HZEmw)va;a)3?M=}Z1u|pQX2j?EAQ!+$~JEL4sC4BOGHxKP>GReVEMX-gtq0)?~9|PR4 zo=w_(%FS$tc(`fO)`_)#@^3@BVVU1o%6l3Zzxf9TeK~sWSyLh$G?~iPk2^eE*=Y#M z%M13CnqNIl#7jcWy&4&Er)RqABjL*lfG-;Jp>WuA{k4A_UV$$f?g~FbCO^1ubk`<= zF0G|NJ4_9dv%@4Lq}0uS(1}NM!>)w*Z_jhY{=b^N8GVqZxaf>H|ETdKF%#TRYl_?u zy3`jSb>YDT8NgB{b6f?N_(3H74@MI(2iNdo`6JeE9G3{GolH4!;D*}V5ETm2PnhDtu@3})7G(iIH&4q8e;0B?gkA-CjJsj?Gu z`50kd_4nf(N+0ZW+i}aCdI;Z66cM)!@IkZudVDew>fu${5z^t}7=x>M&K{QnQxTcI zL5zD${`|Y+AF~(xp@3S-i08P!H=`zl!nC6!gl?zzg>FLU|d39>o$=RrPRdww{y zZLTe5TPMbu6jN6IAmDAjjhEtat_!l{*&nW)AEt*4d$@~%#x5lOTEji|D{tOzia`n6 zA&CV-;?S@w1yxpuMx4?HJhyO5iu?A+nR2nF+A&& zQ0J7Oa^R8LQNDGp(^a85a$p9It?0GO5V0lt5N)@=CGOT11)4Maez)1FlJZ>m%vNG8 zXP&$nlo=t?k+80*TZ{F9_r4A*lW?#19kBMll!`pYB%P- zt}%-$nFB-ZE}k0qZA~-3=~N8=BrVFIY78iit(q$SADYfQ9IF3)D>p zcnY26q$EN-t1kVwXHnwe~e*mSo2?n&3=F9Nh*j)^45r`iQC~rfJ$hL``>Gs*VjK z)6n3kPyA4Z@J=Mc*{TcUN&K`k6I~6-yIu$HfAcU5vVJ{s zdGfkz7V6_2t;4x5rfMdFa|frrgR<}w`by`_Y0q?>i06dPwIi7+p#wjMKYm*g7btY^ zc1fyASbc{MA`I)q2ELp-WvAB8d_3$vY)x(U9`FKOXWnTt$1G}%GSitfkg;`Wn)j~N zsRzbWS|dLXt$z1b68x=W>}a-2Q(A|X_O|MsYdN%WysB^gB-Z^NsC_KU^J(8+q@)=n z9lcdn!M-50R}Xc~feY2x>{C3SKIk>VE{@D$p8To+GDzjj+bd z5Coq#Z7hzkS7{~9MlWL^*PD~}T!%fSJk9r6wEp{rWr*h6gTJytacu^#Zd`@G)~nWj z?*YSHlYvb<+eT*MLd2*_%&3~AK>ZG`@e}sQr-^5pS;P$d21LQd8an!|@!GfXPDZg+ zl1|!^Wj8E4n1s%i%yQ*nyl;E>sSa1@HTcji3u}v?u5*C3`(_2<1*sF7($%xR3(+B^ z@aPOv9&Cimp(W3eHuBC3g)C6vaL~(iw3GVFr?V&j*+9=4ez*W`d4o}ziiWdoE=dDs z4C~%3R6V2wkF%%^slGTs_2jQLCfAZ;Gnd1}%3^Dep5E}ah6>5B@p_;6KaY7i;6K*% zBG}D z@WleH^v@j2?7oEcRE4^-03q^+i zC;Z`Wl2Oe0!9*oC=}b;O%gJ)Yo>t11RVAW6h90HH-&qe|e22Z3xmoff{rNUCOluTh4AA?52=a^0MnvxDmR8E>O0+H}`t8r{= zb%JXVCSX-mTt)+ST`~~k)>76Y9H5tRSrKL>+k+dV+Ktn1By>SNEV$U(w=n-*Dr-R3 zuA5?T{7*|C$&G8w_n~w}ag|^n@x7NOq{Ok{njnfHSZAVxH$2AY!b)qz6Ru@49X>kL|@0u}d31^n-Nl@H^omIg~fp*|G`- zTrj1-1`9W+%m?SqFfI*Yfk9BRsNTwVmOYL9lcmj^0Z+%Lo}i>5l79`p1Ke%V#?rkX zn8HE*GmFJ6O0>dbrrG7m3u;ehbZX_938n5X89*so(;GZBX2tIV)k@@j7Bhv7d$H7r z6e)l0c53LkRO?&wY{XL+kwz%q;XY^GYKxuEBwXh;oxcyV*9g%r>}o1B#4s;2Nlnl-qtTm{eds15e$^U(I4px4kD9jOXujbBdguttfQ{#Pvo_r#&PO4MBX8BS zLc1;OT_~ipq!WpRAg91R!g~Lx&Z^cbn*t^MlZDkP8I>`ExMj2bk6~qJ>zqF;+zk4P zSw2l1PkSs;Hk=cVQ+PznPk{=q)y1fdSKrv^_IjL89LsaFEt=STQ&V{MC9Ohfg1prC zrTC8*Z@q13k&_wVxID7x@v?{*=TlEhuC(l`2UMDdLdtC>oOhfMeqD9WqXn3*f*oF9 z`+h}9bE=C*`VMrc7r*lpJ#g3#u^{umQfz6=z>BS!CLaa8BTMI=!J)*V->1qg5&tbr zhv9F^KSy_^(F>{jD}e0~=bH&5XTvv-T`_rRLkGUBol*X`&lrKV|8otAUQWo2-v#hr zJ-SkdUpL!P-!l3{lA+Ed#|%Xk=yywKkNQ2Ar-DFvbgrH0#Ot;Xd%?eiRPy1`Lvvb zW}Xq0cfAHi%6QzDmkqzEc(J*s7m&5RJrVu?EC8TC%qxY8uTJ?}81P?0{?F+QZTBpB z(TS6wb=CFVB~GgiY6q^FQv3Wa-a4Kd8UAigc?$h%^>E`<+cl5))O~u_)^xyI(zVgo zAsdcD=ySV|`TM>p#{Fo!A(vzEfh(t@b{;bNc({96RK&@WMB@SAXC$PXIX zcUS(**X};lg>Re`2IFh9CNye<3q5!=vq0S9>XrH3Ny2Mo*#w03K>UZ7zslf8QdU0h z#XnMyuJ~66 z=-nAYk7WE&VPeRro}rZ9x!rFtR5BcS^f^1Y)n z`p=GAtECw{A{jKxe27B)HKww~Q7uF4YTSo(#Ku*i!a|fs-FXJR0vLWdwV#w6g`Jj? zVx%0>I-uP5j0ny>nGVOZB-g-0JlJyRQQV{J$)EnpkNjR$K&eOHMf3m@l7`6JI@Y3? zjlmTbua?R$DLShALp5@fXF6#IxVHOLuh8by?gA_^VDHb_^Vmk&=hVy}hu@y9S~+di zK8U9RTR{JtLz3`e1MnZO#^GNkn8n26r=9QjHcaaTwc+wx4wrd4aISK(ow|Xkz)-i> zJ`_Yl6Ydai#)_pcB=5a|)d#zv^cmX>Irai;LWPf~Q>~c>3>JKt$5JbD=lGmevF%8# zw#v;UEbesH!4H3cSTZ-nr|`&f>N50&(n{4ko>1$qOryU62L|h=Hqdlz#K~Q#`24im zw-&>D-p;)dz8k^j-h%b}P{yp3G0+uRsdKI7P?U0cb1ckhQi!R6>F{1~hTbD8@} zge^wkg|`yb<1bbS!dVcMDz0+DEgRGVGhT9@Am*-o-mge5!>_5>LGX)h}8JOlOY1#ei-It0Vn9& z3yi2w^C}SFME+_i(yy&~fpWyjU1+z;7*ti##PmWf%N8>>u1EytQcBkG|DDLMYoi46 zghe@LQO0t&lxAu@!7H^+wFH83;7loz$vZqNvOK_q3OBG^+B?_Bxglj+PH*!F-GHO? z0>L#lp55SlJfLOz_0?56fP$>K;`_#hzIj`SF|PVp=n>;T{Oe zy(0(&L4HB&o%dDYzy#U+RQU(T?jnHV8C>c94_$;+{Nd&dW?I{l^U&CsN85 zFZUWqH|rEaHck%F@PJa@)dVbID3gsY0g6S&Ys0DpL476vBRa-3=OV6$IVsc~scgF{*b#;|f?&Jtqoy zo&=b$w2z-^SS}R~2xg+h73iUM{p_Ys679?|d{w|Q-y(~1@}NKNN?ci0JhuIuz>!7| z5Q+|$1#W^GK6fy9S#^-rzCHdPez_Lm5`|q$xQkHMoEVF{uDlNUP%j^xj6pv=i?=-} z)8D5mTYK>+k}B%SDj&T7FSEzksXugh~Q*43@I)$NB-VVfz16i-d`x~rc6wZNoWlR{!iZ@A5iTf z`gT>-?MEHYI#!{iwEs$ z{BFtu<4;*FistO~n&aHJQQ%Gar^;csE6s8lvHyD>aM)=7G^LfE>Pgc)lOVKf=WYD1 zOr-}A&koHeO*(Are7H36xN-1&4ct;5QN3|8qvG%<;WtNKkDtVt^<1w~xZDo5gdY+3 zy=g6?_+^uo8|*bimH&Akwp~tVg!X*mKPaFMMry>DF03;6v}Og~BgmqqL5*r~iukaR z>(c%T$924#L2|-<+v`!BskP;D{GgF5lvQ85I|kVG%&64g@b7SFf{4kDs7vhF)s=O; zl>OHfcC{oflHAG65NB?gYOfA6wzj1%2poqSAd{Dp0()-sAax7M;9jO)%7O4+(YU4N zt1Ezn_Kz7P)}VL`?xx>TPXkX_HIx%-xUwSaZ>w8#5V~u1jUt0NN(g^0 z-{cC-M0ELKsP}rO1i&^ImchIQ(Mm;pdR)sLqA*8yEjw4gkZ~#!Oj!5AXkQw?^pov($+$-6w%|>N75S=ISL4io@R; z&({;e5q)>`5xac{4Ne-LSnS&uz;o9wC{2*NV(```I*f_B;Oh5hdscy?*!SwMHJVYL zCMw&!)^#F1ePDDRAxh2jEO@IcDxLXe_`kiHdOf%@ z$*QukJs(M^{f&l=@RKJB$(jG%Q9PJE!~P>^WuW|i5PY%m{ov4LOkoQbW%lCj3hWp9 z>Zc1cPX@!|m#v6ur?U8l{bK>HcUdvXil`TOkIwShLNE%>OPhLtOV7C`79txpPc)n6 zDa1ZhIrs=-;Nvu*ZvFbi<*9<8*{6?-3D}u{oiPRyEX$f{E}bO`-(GIcjryAM<;$kW zH1S6Htw04-FKh#y&EfS%5$oQ6DIL0~w_t1(G6OKjuE#r3$#?u+otO(}7r@YNRns+I z9-(kW_h9b!cd=-}R4h(Z4`byVxVRT^GD!EEc4;cTD1Qa=or-kY50$|eknSzr5HEJ# zi0I2ALbl3#{F1$6j8w%}Kc0fVmas$Z&xD61Y@CGt>qv@gCgqPR;4cWkm+dfo8WITLkY!-Vt zGsttP#Nrr_BWEjXpXiqY#f#+!-mfqb{%Sev;1kz{RPN)G|GYyNniJ7M&lnGhA`41? zy7ww>X4l`kK#pF+tIrf83qLWY%aiXpwq?!7+GPO`8675VPZ{>uq{sX}zrbm#Q?@zI zJ8*=8+y5~|4AUk3cT&JE0?jj`2VyLxMbi%lzF7)7zUw?S1_>Y5;0Yxg9x(Wof1C@I zKM|P#mbs;=fK_p$oRn2W7Hfn8{a0uS7qV1nxQ!7~V$}Husqowe5LzK;s$vEQfC#wl zPXrzU`xa=b2%rN-H}&Y+xm=QB`Ky4tpB1IdknT2tCKG|1qAK`ID(uMpPmA0yg>Ph9 z76UqKmS8?B(<~2d-hf#mp?#9DTp_b@Ym#*M-S#UrFMoF9OHBTgws zL{Mb2X0*O!?|@sD>)U!aIA96TLyn3S- z`>I>%YTovzX5HAP&ec2f08H5uAWzGvu8_PK<=au`YtNlt_2YP9B4PGvv}*&QLGi}! z7?FPYRgq5PRIpodOg1OWE8tC)99%kUCxDyReA znoY&O@8!=q)hGR?E8;2}Y^Ib?T~+z->E^$&@Vap)AgM>SB0Xo^fiz|K|)=xCpeMtE#CfMEBev zk8YJHSYj>VTwqH~#3na&&OzLL!mNnG&TqYWq$hp6P(?Epvz}^u1iQ8aKa`BPLk>19 zD331M-Y1lthTV>OmpAqkSHpB^)rA_fG=o0}Zkpac2(!OpvM(Yo=+?F&{)_m9L#}gA zh}6%lxgyGzrNkLN12xKCs_S2mq6K=FRa~iEfo%FR>cvI%tpNcPMLKf1y&KVsTWa-0GhA#R$cn80l`vBI{K0vkZZE9A2lwFOxh4@7(~PCu5RggCtf zEi9iz_1&>Uy17=Kz<(0bjyF}OjVx9NJ9ZVgYQdtY+RG0}F+=vY-J6=O zd>D_8l3M*JAY2tK%j~) z@Wt?BI?w*l%K0p*!*L0~Ee;iNR8W$Y*LYHiGkp*><_NTX9HaS8atATAwk5QiYpX2a zf!l6PE=%5dnq0@nx|SF5WWpeBr*wHGLTfrgEd40b`bC>}bE(zt@kf`UZ>Kb=E3A#o zq_mrr_!}ERN*61BVQ;1$d_+AXqY0}X8SCtMfwP+~&D1lx`RhomEXqUj$SBf!AmuEh z2UKh?xU6zhy*En)S1qz~Vr0PThE}wL5yXpnp!%|Zo={SL3bKS>av|-Sta17Q(eX$H zQ7sz4a++pM$7;sa$ik^_|Fujzzsy-R|8~o=;}b2JCx7PHWh>f;PDI+$i|O4bhP0*Z zJOxqy%~fe3pPSao;pl7YfPjHuj;U*@cJpyyBiAeNPdyu`Sc-%3RP?y!s7hicP)NK^q zL`bJCi#H=Bo2}#>g&^!~MC`u#BH9#esnh5rGfrK3sE|f6iumb}*lWH0)U>^1 zns{gU6gG8A zX_ZXac(>!*G+nRov+m3YYjqgkuWN^g?I-@*{IBT=m7fcFefbDj)&$oO7`w7I{UiK! zS=ACX9awHJ=M~Y`z~!}gOdLlLrs#cqQwhCmYUPY`<@#0<97&icBFj{Zd?WTgi{5Pf z-nV|5(#Zj)fopK!)zJVtzy+iHIUKaUpFH!;)HLUEd}@NRT;xApPT4d%%`!vR#_r~le<-=mmg|JVq)bFOt-Z!>ho^JrQf27c}%-UBjs z2(Qkwsc$-T(C<*!?i3z-;dw(St{=P)@L+iS+ZEq`I*K37ul#L0G)2sfRb=<=;Lc7l zEWzo8=V9RjmU!*ywqXzZ0M|jKM6w_7#p7~it274tb*7~&%UCDGi!DgQKx zhwYk1s>D9(=pZc zH*BiK@Ed1n1ET-*MhD+ib-GQ2drX+Fc;YHEgh#6?idN3Jn2VXJyjEV zs|fk}AP3Q`;na<4_w922bSJ>ZY(k%7GHEPJ!;mZPPgqxQU(~?b&-~|H98^HgA6sDN zz{Ah~N>`-B`7TUvntZAq5R4aG3RSuG_tDP4`#y2Z+NQvlFATS{#vW7qgZy6YIxH$i z4L^_BW0&u{=f97SezB6qw9Yd*VFkgVM4-Ft5PwYnAhb6)kZ8+ zrouXBmmqwT+*ilj8dSEv5AW-oH(j$Y9;xkg{yB(ZEJ&TUP_V;@BZ@03WNwG3pYr`Y z_@V${S6=eFHD+SRZL~BOSvWMVfJOP0&Pf0bCvRq5SvnYB+VRagF~7HExi#+G{xa2h zoSc_imJRQ#@EBd87K9%jd#36zXeHbf^*vQz_Kf7qyX9)|XUdNs79w&CxK^WABy^T{-zssN?*oSjaY$6L4|9$*zn!?w@h;@1G2>8Y{uaj@LLOVfceYwXgpL`dzUhM_r9@_JXzN;!GO>vn2g7ci5X)c+7!4MkhJ8Mk$VI==W4+wZcROK~MP;|9OBs)p zEQU4RfVR|J-Jy}Equa|5%&!I<4BM{fa!nF|X82L+~%&@DF+aUs{dm{@1-g z$C;qlohTbK_eV!B%ewNN-q3?~AvaWjH8Igw-pYhOmoao*KX2$s7eJ2 z*fr%Ffd`(!0yiPaqa*}=O{iM_N#BmZ zgW#v%_L*7;r5jMPHg7U(;Zs`w&aH7&){*A)J2Ru}<HHM_<3Kw5%iP(=q;lSEFar{dOPvQ zz2#oi;Jod}+ka^J?-Iac{dJG|{X8BRSJH<;sc|RJC5V=Oe@i9%LIW-d-|)A$1ZK{| z@|d5qri*3c*sWq%798beLd=VPXR({skFo}~nwB!Yj4z||emT^_NQNzsM^D}A7M%YU z^(p3DO``1<*&gbh*5MH5k-#6#bE7SEs@RnXry)w|Ap8-%$TDPH*hsT`N)+tmc1|F1D-&*&P?#pG6}hoXO_ zCZP1;@0q&4zwj^kqX>?|N3DBY{C2Y<@xduRY2A3$Yuuy*HZl0gpT$GKK4B*P4W#I( z#jgda{(C5iEszh5fImrA?!9i!#oFErT8eSu4;^h^JxzYstE{-hu{B$>pEokJiFkE5~rxINsz$g#Q5Bbr%1|wi9jM-9w>Ey&4_j}nA;Ejlu zdtd}x=IHP{)XD92Dsa|P_aBns#Ksm0;2SrF(Zdmay;K~%a~n?qOtH6rrwIy{PO`S) z4U#1gfJSFB8|StVuUc=^VIadj=dBDAh(0Hz6pCqxzutiC zz1U|xY$G-S^nzLxs)rRHraDg{?~u^k{unM@(&VilBn|6zvh|?lQqQFSeL=z&+Npuf z(wdx$lsGtcUoq%I+T|Ffw^1d2Fj2?uL*mnz29+Onn3#bd^QDHFgAgbGWSa6_0Lp>z z=do06!qhORA9n2fMkmsKYPw7nd(laIMhT05|JBC!RQGi_36=iXaish82Z0?VL+JkL zJMyWw9vpCyx%%P}HC?*h5cLkVcWVwu(w{|~$L?BDC)X-qmp)X+s8lYPKD^!NC{`0< z={NVBf3>NUvtL>EtHAEF{yJoj?Hm_P$Do~m=JQgU3#LGrf_B3>2*N;P?lTYxNRkw7PVu$ z!3=>v)M<+B&AV1V&YJk6O~^2&+>C-rkni8;?+tlt3YG_fzp*7lt){!h@a`)$?~$f zk)@5=bfZ1@E=*I=xvekSE(UE@Omyqr68ZD2^gEkqcX`@pdZA8d`^l6>!Ox@#5sgJ|z+~E;*_f!%Pviu}c$*(oV>uVk4u6Tc= zCNzP{Fa>mm%v6{3inT%!C5-p)2d-RHytLoyk1?d-TzLX#r)cMU?&ggwSVRzYxabL8 z#rYrj-bl9XIImsusl4WApp(4CkLS9}>P(``GSaRacN$#E1d7&}><(2ZA;;u$@!~Lk_8*o&@gCi`#6hau^o|wYOg9ey#=z><^OQs%9414HA_l&JjJj&AH z$*b9i?ixfCzt}m@qA|MUFV^1!3GPK3-^(hY17_^dTl|bm<{@#!AUbTaC zP}op%tL=h;g$8LK(zWr$zv67VVZX6$^@!ahHtkx6n`t^98p|Lm`=GScSotb;XLS;r*2_WbAZuIsSHa#Y>u8Vj0z!#Bqf9-wu_F2OebcJ&OB(M1 zUmtJ~<@EdEhl%65y;$=LUNM4Z*azpR2R5`j1ZJdCa>dqhU(LlBoe@vG*#W0Nl+Rw4 z@D@_z_R)bU!52ssa*}UpVCR-0hG6$|mY8@L;5qY`-wg)M~cZmc+#H}>qvv$%{E>AsQ~h`EHH9LuGi>SM(!DINN8eIIKg5;%{Ha0hy8y|m9OD-? z{PbO%h|odnt>ZyWVJhHs;rT??f50Yro`bvid{QXw@^jT0`kv=~P@m>~@917U+z9W4 z(w^IW_`BML(+tR!C(q%{y><`AVLrv{M`Amki9PpJU$)> z1~_BRou6fb3*Zhxn=M1-Pz4&`b_!1L!h{7>&7h-U;jiB{Fnh75o)%_r`HZH3)x&rY z(SoIuutqF2JRssjX4B}thz;@2KI$FtHU&Y;+Gg%n7WZgkY`B27(;U6+X|wKsd95t9 z?Q9|c+f%lcPV6xPWhg(Wo){}zTW$Mzd@H7M(zcSiwXJe%LkYHtd z?-f$=ZxuPsVz1P>G4H|>n}B*RjQ8JHwuiqgoF9fVIO2q$?_`>x3GI4nR<&+dKodza zUxJR$@*&?SMlFpetNRW~x{QuhH|#u*z;aEX+#n2o{!SI?jEC*`^wCqzlfBYH99>VCzRwa4V(cHVx-dG-FN z)#M)3N_?>OZeuykdB0$rkB+-SIp2=~uxPZv6Y%D~A7|^rQP5q(!#d#?SeJpNrLsJz zZ$nBIizWxpd*?xxCy^G#SlQoo5V{{f1u~&sV=&7MLIwNwGX?^lGp=^j{g_l~gqANm z?{d-SJs+cTcb$IvcBHhZS>`y*$)+~iZ%Sgm>$G?zz6Y)0bhM@F*J2P;n)8Lj_nQm9 zs+26vksIs>LmuTmFk@mukC=J5xzt**bfBCamvx`sZ28r~2tMqx`{N&dez(s$_1C-q=1*I*Y^8T!T2I+!+NH;ZIQ=T62xC@6)vsZ76|z6x=P6}> z#V1LDvfsU^qDFf|L2aCUe?IQ_v_a=mZbXU#?d~fF^HZ4h@PxGN8J^oj9QL=% zW?A5#L$7Z+KA;h0b?jOVSkuvg(OoF8%q5}L-7?j91A3fp0{_L%0na1{^o*ausi;&H)L`1_ z(jaAT)p>Lu!cu8T`B{_hJ`}qe;Qtna-kS*an^OPq`&7NZUe4coOJoL)7~sThZ}ru_PGi?+Q*xo`W|dqg$iFqVY& zji<9Y+pU(j$5-I*Qp7cn=LuqNKH8#f)$6vRy~eMXZA+ddSJp9Bo2ODfw8WemH-9R| zs5X3Tav}iz4UjYtSL2$3B>j%1vzwQ%QAX*2Jk@~~23X&k-O;PLFUCgw2Z!&VHIC$1 z34!tci$_>g3gO(}ImDTgr^Qs<`D9ykww~;>_F8IhCTiJHGU7aho3iCZFDl2fadd3_ zAH6-K@9Vc;JzuxAN{~RR)4Ny*MgUAP7j4At6kUq%6ga3maS$~lK?o^Y{@fMHPJd#Y z3G-Wb0yB?%!trOK+8hf?gBu1ErTUw2^Gs~N-L;z2fXhS>6th>9q76QleSTxQTIt!M z?ZGh}+S!=d-VzV(yv{EyqDpoU+enYeTiu8ET@=>RcVHPqg>38qC?}`JEMEqszQIFa zAH!DY8P@9e+ka{2E3|3I3E3TTaQ1I+HdYkuk3F;6M{ep(3NYK_RZEqdrBhJ@J5Lo< z;%2_*$jAFe%yH0e^L~O7eLwXDQGGJgVH*XPBrUiyW!`n^^N}=O zJ)z>Nwb>)TlaQ!`Rbc6Y%np)#O36AE)PSj(f>*Kne->%Dh7>72-rrxXBaFXOd6B>4 z`aCzrjDw-SOn?n;{bQf@j2-6hwo73-9Z5A^B7~ehK)Iu7Pa~mB5(nXVD)8UAenHHd z#jC{9X`cJ|py{9W)nvbbddT3{HHGCJ0b(lFYS>Ge`m9_Q{`|Y2hJODdru~R+0}Vnm zZN1XD+qq@kP9;1~TD`IJ84{%qq|G7WbtHXqrykH3BmIdoA2h`19&hKchyk7Zgr@52 z;cYypydLI`O+mdaHq0VoeYUtIGe+AtNjnkj01k4NOb0Y7AhlaQY#LN+0F%hRJ>2_m zFS()T7Y%ixK@A)C<-ks`;IlWC9aDVC1Ys$X+ z)_D~XctG+z#e5b)rDHf-AdwD>(&5rOM_37*6)J(O)+bzedoYiT{kVf};mv|AE5^QC zmYb^9QAltCiW?$Dv#)v!?zwZFYwv+7YLZHue-1Y7U8iWk)A>*D(^1d93D6Snzxzw9 zz5%V(OiNnffw_EkI{NgZn=9>1)ck=oeEA{C&`^Ej)zatI=)&?Sx%6!r`JDha`4)-d zI9JRgOfTTf+5XdZQM5>s`;#8OWam2Ehnj7!+;hPH3d25R**s^&)aLL z3x?R)iYj~cWS^v$p=4>T`{U#HgxR?Y7_DMI(+IJK0QNWAzrG{hj^yFWz(0f|B0p0f zj;pX7!kv-izHtN~PPiuP_}6QaVzyniJHm%!FG~~m!!5Ga;#a)1n46B#oQ65aKHgv7 zbAA(0g%;M8K{Rl81)5uVy9v zeNQhj%DCxLN@BKT_F8};ILLceFXxgGcqgv8$YNOxCF(FFySeXp@EMCaB*lGVU0=`G z|DJ|&ss75Ymcfud@mg1IGmQCpC0HK{*S4}CPg)OLJijqFeon9YKN^R(ggW2n@y@b- z5|(*=vZ_uK@Xf%IwA^AhQo66RFZo#p`X3^^(GV#WtZ*=JWkM;u-hjW|h*% z_P!VsA#jEQO8Dx3m!HJBx#>Xm5gwb`B72Hn3tG6g;D_~T4kf+yzXJHe(yni=UAhj< zCCF{{*Fl?-PYzN=9{dsDjKE<($Nr_4^e5Gs5iA^oW-~}Tke73({ztP1~ zC(q7LH-0X1&`-a3^h!L)=mZV~{QkIP8?@0TdH^j!OJ#e=;RJHE_n+9L(y{yKM0g@=g7eA}*Bxhxv(DWSEg|Az7hN9wTibV3M~@3}0p{9*=vTp_z>aTDCU zVk9zy_VS78w^tZD^6~d(#Z?#0KRwXt;fQolU!5AQzk_R9a>TU9=+J*u+1vi%&yj?d zQTOh}zKE<~t2@msaEKg_bqVHea1D=v7<-T3IP_hwzB4Ah#V%OCKw-3Gap;x z;L}2Q<2Hq6ABTT}Vbfw0_YzTl%C+R~@_SKFnjIzkZI=DRm6I|-W0`vME{)CE{ZCnO zYX9DmMTK#fZ?5ycopnEbYAP0c5J78p!Rr4a-@ki}{m|yl0sO0L`@XV>e&M@TBw2?| z&mee#f()gDYSVG86n3+D+Pe&9CGEV}-1S+lY8hW$z=S&!P6qAjc`$PDKt^u}RXP?O z|2r!ZFI{^Kw_l2eT%@qWmgkJugI!W@V>*CkK=;_JOp=UEZD#X}zCSfp{o!eH{~a&Z z*Kukjz{h~YB0);W+L;W}#2YKC{8IgEY(auo%6bZJSL`ILY64$?9l_YehwP-Z+jAPT zP;EC-GfLg^QG7s0Q@o9Ou%uYFBzB&`ccUr@{^_~aMXZcbk6jU%f41@gwbf3w;f5GXY?7P8PdJn>H~Wj-Hl z{krOwZ*et?`wm0AV#v3w_XsK6vvWsUa??QQt-nVhhGp<-64*T6vLD7-mr=0k@$0QDR71$rXR05-Qpd=P;wXkrVLGA+C&m-+e6XKG+_`Kui!QgO84#cazBA#sbl zXX~r>e53S%K+D~_9Tqb z0wF~MRIm*Gg))heN$g1LAA-pnGjEH!w;pdOP(oi&ANEC0^_rP?4EnyJ-*HUq-xfWc zC&PWSMso&79XvGQShKVHgEc>PDcc^>QON)-YxzK#-KDrj`+IU7s8uHRlaFrS`uK;X z5B86&A`b7`pvL~z8?yRF=?YAMPnb1Pw|0OoI@#mC=1HJ~1}@Y<&1(TC>$Xp%-k&3F z)7MEASlH>#;QAtplu_PUm~VDFXzI<&Ybs�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%0Dtst1JjU6n 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%0Az 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

WW>{DQ1&>ti7rM8nLXe+3^?83{kx<2 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

aP%NO z_Bv@jWd1^Uj+eaX2(mJ_ouNq7jZ4s(3MOm}V-SHIA2xIRX|X;ZaTWA^9AQ-=@X>SQ z)=z~-Eodg5dh+tT#Q z-vlHkY`7$a+@-rX20Zn=<#C5oo`@L{NTRQ}c{L!In`}Mes{&*zvTeQ;r9vWxIIXwj zaRmViAj+c&D-iNA_$JmIYz>`vLp#Ck<2r{x%B8ZqzxlIj|D0uvqnR%CBvu0HA+~#) z#i_&WUtj-bS|hj*92kPoJCFD~2wnEOY6HOsZChbSz28dvYWsxshg$v3+Ip4Iv&udtkJ|5ZqAi6LPxf z0R%^TLRx08CIGWy0T6b2fhdn7>~mpB#EvC=c%xi*{s60+C3Wyk7eB>l?-kRasyY+MXOsEGv#I^r5<-b=wBC=6#>1wI(DFY;$T4Mw)NocB%bv}NA6lmDgSx}*t3 z+I*YcEWLw^aM<{68v(Y^;$ON7IrQL3L%G%}e_v~)vh$ZyVP&Q#g3kU~K|7y#9A@?N zEPU<1nNE$MfjljquUAWuvRJQA@8{zCwEjcI7{5V9bX%4RuIJ4-*{U0A`|_RU&dG&6 z=;VU?S>BzDBQfJvsX1k5KIAHmU2sF6XZSsjJ(%q9`N#scR9MMsqHVwsRhb!<>D1^w zvzBG<_*pL(0-j6WGlWw{U6WgvA z4fO9({OjGW4Q{Rp0ego~mEOU6TUnYb`clhIQrdgjF4b$5p0pyP%(^T6mt;Y;)x`A; zRE3F0Mz!i-mSMzq;#N-~bSh+|uBQ<331QFv)(3Q!SRHIfQ6V~OdNvVd4n{ZT&l{f6 zD`?p+pF5xL3MWZp5I$C^$x?vLVVIra6M%*IGY@=s65>9i}P?Ndsp%-@*=0tRif{pUbvz z?|g*e|HJ`;Ps5vJtFw>Z@!o-LJZI7LTg38`jt)`t3xfJox_@4jMB_8jaY~o0i^DEH z`xV%x14isW!lyB6e`b7q5Q8~Xczh<-O&T{7@bW2jjetX8K0NAAl_}1VvSIWk-;pF8 z7FomDuQ1}{1S*e%!cQ(xwO^68mR4MZV4ydwCL+EK@>ZyX`nZq5)&AWPsrGgEL?R1< ztu)wf+x}X88aH>uTR1EJ{w%o3ic^8NyY)gjD(qY#%5R;pXNR=Oa-9jTiP8;ODS~=JqzihIlZL0?2`6AOrS)wL;=Bnma zRC(?jY1hGZJ!cSd3c06}Ju&Lb@pVGckz}3tQ0U1Qd5ZEe|2dpiy>b+UkUD9yJ11VJgGoSg&}uAT!*`)k?$ zF(^OG&-{a2SAG=g!V7;O!|_h3ULo0e;0%NDM%mM`Uf1$)+CIr#{<`MZmb1tccWT_! zbWld$Urqv{92Kf5jwA4&e~du=^g6l42d*J!1eZnly?eY1!bh1e3(CIKK+bs2^yU53 zDSX&*O7d(P<9ypGyBZ@_%POYKrsU5l2vw`lFiQ1qFWR1W*0^hOE9zzey>_cuChEbwT#MAt7du z8HV{JVpc3U(sBCk9R8u-i+_B4y0U2j*axIt7?-Bvcp8dKLd;(d^Szs?#9^8vyg2M|{IN-Um)1*{%$&r)e5&&zT<*r>o*etfzm{JQd%jMbklZq3eyE#S!Ygn@7mD8kn6>a%@9a^KOq1r}Qs-+H) zO2TMM2fv{8uTew$zXZ+Zd+r!@J2r z2TkDz)bfk;@aZ5lLve)+}W0PlE8@Tcbaz#n?ewv4D=XgnCR7(SN|>0$&&ETL}b zwCyZ%4~D;m;bPFb#AO6?rcngrQHnFb^v?50!NbdP#oLloJz?k50$-jFWL>*0Yya!u zm#J@p299&vQp^tL)06N~2C6DaSl>;A$!DR}OLotD)WR9%#)!2=NgVH2uAj|I=One^)0*@>)P40Ke#50itA`zNPuE+y%G$|mG_@Z4sy`# z^;xRK|CZ+EhZ2Mg`h>35P)w983z)8(gDt~TNR`oqPYvZuTJ>fSU}X<*%3{}tx!PHK2zMF6o54-VZ0HP)Lia@m)Jdjc++ zW9eI)V+X^5X~^uo^;Ao}L2dH&8Lp=aTEXJQz8C$gy+ET>*`lxLqa>O@u$R)Ea%G(x zpk^cccjHm_pQ|HHTa8pM`8C|=W>oM#3X;OP{4NhGHT|O^s;N5<3CFeZFZRK zT4&HHa>lxojM&ph8RA#wRXGkhqsQqD5u2$9vHhnuvVS-h;=lNIr2AM9vPMP6vJ zWu%p|yMhGXgn8;1w<_!lK_H>ABmyPWPuVoVB!g@;8G1)cMxpCdsqn$uAL7cU)&+0v zrXe--xJ-hLCts58;4j61N;MOLhQ4P(23>=Z%FtamdS4p6>z-%DsMm@H&dnxwO_pz8jpN#TYB4`x{zJmN9+JveE@mEe_PYAfklI$9UCrXjr;q4y-#%SUx2q|tC+>MnxJCsz1=O!9>pjof zkm|>-F=4)^v&hml{@jx3S~g2I8B?Faxz!KrUI=;o^+w}rN2bQhynrY4h4`{nOvK>E zgNSE4`~pR^|hI>>1?Co1}Fj4I4>d-N+FPz7gTh&EZ4dyj(dM&FX- zo^U;MkK(iXBoj#YmaNy4mnu0S(~R5dED16l8u}{(j4j^Xj3wp-1ITLs&4@j}8~36p z0%{YGQ^=$0GbLOd?AUVuJL!Iz2Lo-D2satov&Y3Y8IM{-`{haPUTqGf`lmk=1u`Uo z5)c^ohV~A&zf(_NDc?$)x#4g#+R=t@BkC&c8T01rj*+^LC!QJ@tGfoO<@0frwZO&Wn|46+_A~C-)R)%BM(l8 zTQ5KeivXS*OZj`P+ni z@GhXl2^om{g4Yc1xAP}n;RkLl=jJ#po#YR?v%7v3a;#YvsC@7a_4SO;jMKSS<>d)| zRUZOc?eHL?9Kt&JGeI9Khm&?X`u$4SNAKKoZ-mX;NU$GwZ4fF~UKBE@CigzIp4cri zeGPkfr!V%CIk=(qnU4aT-X1+mv2^3c!UI@AH+V|}&h-tJJ9z$)dvr>A{r5`qVw_E> zkzwFak@2RVb|+t3fxzaB*ziTosbnENc1vc&^bPO@+6aShLMX=CmN8QGAk^KZRX*mU z;*q@!q*=w$frA9Ix^C!#vi1Z)tTB3?=O^A2-iUgo!higm9sxADkO3ko)@vSA>N-Q& zv~!$fs?&tTQjE?_@QeRW=6x-A7-zu@X3V`11+91bwxM@iO3pTyZC`*&BSjaP>5oB# zV7{%guaf#z7h@y8+%Nx1c@pq*{lkR}-z@h59Zqzddh6!StqW?7CTkL3Rc|b_QJQ{w zV4leYCz*XAQ{X&eOC>@4ojHT@Q0)`(CtvS9d1$3q8*vISgs-OmCTCNqX72A`>DTi* z)cL~_WSY>bouI3o;1H^DLjn_w0j6cuZxt!)usrf2DtqMKJBz#{qp5OH7aIgvOEdY$ zlJSg($={PHqA*nX_JDu8Tl)NT4QU}=9AOeb85Tw26>jyvb+F%;&*N*V$$Z#O58lQG3#gI$WEe9O7xBwE6m?cH_A<$d8qCer!f|_gW7n!Y$jt_WHddEj&JkU(JYatZr_+kC|gK7_s!cbvbIylx% zi4*TT&okNjInt>@N-WyPNs4h=IHdIj2@(moB?C3fMM^0uf0JQ}y}&IpNwk|KiLBBC zS*-eK0~&h5Nnc+SaGtL16lPc@t9i7FDXCOD=_fOx|84m&{v5Q>|EQps7Vg z1b^X^&}(llYT%Z5NcAYyvR8vVh+4DSD_#zFD)9wsY5eXkP&Re358x#$aB-qF@hay7 zQuB(Ryy=B{Y2B^9ed}hMTv(GrSG+PZqn`xbvw0Y>U_dFqcxn-5p#J8gnc|`2-y%-J zJa=>UcT^D1q_XxIEHiErQd^g@BP#ad1O>L8hndtf1@jO~3WFm0NKBo&ac}$bk(H|0 zOBDUTFwVkGVjY)>CSr|Xzp60sEOd|ux0wC?i}4NR(m4f5%EiRngw``M<)?p9ob|O) z_XBnzz^mXx$gLyw6)iZ1+AP61u{wcU#FSNVz1lVH1ntI6>e^d22`@O~B$x5iC%)|R zqF85-Xsq94^zL!satw=~;q;&@_SV{=a#?bY)Q8gZHt_t{ClJ36@>I(WYJBFCciO@S z;_ScH?%Lrpx5KfE;ahup9b>;&)@eeyMOKN5Ba)2E4Al#09nPgIf7;{_sn6}68aWI? z5T_;*TBz!XT$e-NKd6Hh6W!Itq^>aDZ5Z5Z=d{&yUctYpVTB9Zi(hpalBB_WclJ- zZMReMDR@HC`pO?9#i_}NJXY`;be`4pjrSa4v25!TLulI4{y{fRqyfpvAUJm86q2Uh z@h;iN5i;*3fw))b&=Kc}KZcTDlcl&4TZZ@k;J~pHMh6uL2uc;t2O)9Y7ga{p5+dT)-QdaN8EN>f0USCBFjZ{SK z$u@dvlWXxy=IfG`|)*S~nh(4q(D6d%U z0;nbZgkOp^zeOipPP?Fr4He#kRbn`b~D%%fKiQ^gFvnw_Yb|E(Z2pZ8ZP& z(T)LS^@nS}b52GApCavVc0+-86E}<>72bNT0IxJqFZWs}N2=nH zxh^I^#TMB5vafp>XKtqxIH|A>WZ!uAL%%71vCU7~)f($ab;uR1sEa$SU+Uto`5}lR z6M8nI_c+Y6*g4&q%uYm;q@Rf4#ztGfZb{YF5^zHvI30Z?N=FGFzDW| z0%wksBR)NmcFX==&%>2dSPA0oXp_^{i)e@(dD9-?A3YDuYHiL%%9np>V~`aLMC;T* z{OP&T8`1LP5J36Vj1FR0iG_ci!xnOQDTS6iM!wlG(8JAOCOv=o&%NSR(NXN0&1G8C zqn3pjC=G-YB&TOFq|OQWvQxjgRf)^P6O4MG+2@D}rf5EEg~Z)IEMt~d4C?i5e-P~J z7QgjYA0voWmh?4x6G*^Z>8SYdn5sTtVLBqS4(B(Jkp6i>nuu|Xv?PKoEWr|C!xL~K)mUL!^5x6-&pnr z|D}uhZIGW`8g%*ZPHzg}m@Y7e9ZukJJ}8N%?{H8+{`Q&xtxxsLh9e;Sa0ESl9DT7$>e|m2w&qn>(ucusR;~vJZE$&;OWAoBiGUet4D4)*Q!WqniX- zhK%Tml#@0~D%37hGbhm>u6Ks_XdhCOn~Z{T0jo-rOTJT+wg5Ney*d#8U*oTteA9L> zZly~dPecm%3L3xfNeea(E9E$aTKCVqm zOtB%T*5&Xs6BAtNPOT1JP2c(7YapHw+cjEtf?Cr_X2P3aB8_jf}M^?ME< zvN%foMtd8piRfA*K=UJb(3KIzi-1>{vUHp24X1Wp?KCoItAV4Y{o(ZDkS|-f|8bZ- zC=L;rIwSslx`&vW9td^iYTdJF7#?{cjOPp zTW;U;Ali2E(1r)^RfXa7Po89Vd=I?~MkTTlBGN z-FmzH%E+4D1#4uHIC? z86AgKAye&TZ1_io-jHifYh>=Lv|rRtfedkKg5$gZoM)z$jbok4Y|Iim^v-qPvHzQh zv})py9_TAr=$&hKoc!WJQu{b0ig=5``1lWxvO=lnU7bn|Hhp&1GG^44F*|%V!&$_| z^WOMt(>vhcF)~t*XtR7uS~%G2BR*y=lp}yxFTY${%6wd-)Qi- zcW3go*N~je>A6>C(D$J8q&l{Mrv|qpQOvg;a2R1YtaoF31OoghEasffLy0d`YbVBGlQCrN`{F$0I zaIp)2loz8RQ{TJbKXZdn&FI z-UdS3_SWhsV3E|SSls&cVTD<#r(d4vB|vLaNLb;K;;`$9(5cFI@wi0Ko7EQC zjf^(CY1@BMzN|Y{vJuu`Yh5b-PdbRNW3dxwG2;J#3&En|*bh|#3Y z4&lU1^x}_3k+uHuW(_i|C?t)H1AhJB_$yM3kJr^2Dmj)|al77`KDh3iO*1`G=VfSQ zU8;3Qiv%0nbl_BJR=+VdZVQ`FB@+~H2Mu2UG^Ext*H4X{W3A!TRHxH#E0&jAf5Yk; zTZqJ!JBUHp8BTe3V$M@}JGl){#3CPF{QN_be^bu3{MzZFS?jNR;bP?ni^vvQl8W+kDW> zrIzzSTMvu-S1#dl=}>k5#ehFiN0qSyzwi>2jvVrLhJ?ieD9BanaEqe8ko~9jK#SNb z+Joa%+kq%;#05Rz!!1NRl49)fnxtyOeTch<@hwts&F|u-sL3X@g=Hj|k%_bh~vGseV=Dw@?JMDPB^ecQl2`QPYR@n{k*LL z-|TTe{wj2mVKMOKS)}Q~qT#}aC-t)}7`2AxsIvm$sxhyZJte+U$^NyvvzB~|O+IHE zmO@TNQ(e#^Kn7JwN*XHc3_6{3XpwcHAOilzU^j(Pfv$bXL{pMKA192$@t|Y;kG@!% z~jL!RXE0g#VOaE2IDp;g%+Z5)td&C*Wi2Z9;}F z0w`(h&G4&RrjT$ubJ9v))h_ zmLI2bRS5U(O;s*(TaYXpYdP{zdVPo-Z1?#?b@fBWBlyP_(S`EWABL>P*4V0J~G{h=Wq zEBPo&mKewyTI@S=nd|NqipyHHzJ(`m&ekMTg>|7XF;@ir>EiV(l-rpzg)jbjSo2@R z^W(Z7I&1_`57@ZH2s7`(T}r|!@@^mqn8cAMgDqhZI~$Akz#b6;Okj*P4Jm_*zxR5R zJ`rdnG3a9G!Qb&VN1S4F;M06sgDrVdICk#&f(0uF5Zxs$Er0j-^zJ^lFtd-^>NzE+ z6`CCRq~O}c1vnuv2%hCws)(~?ENTO=Od6^oXP4wAerMXA)s)_l)cVP930^C7TrcFh zcMh`DjMn#NER!eGSH9*|>-^sQ+y9fup{rk~P#JVL?*&L7J%~0aq1?~z?Cc$Pz zk@u*&qtUO#^C}uR1Y^~0VCV&);7zbrRm63%Q757`PZ413;}D>|`d5%J9q53)&*O>Z zQ+qBSR=SK11QgcXfWVC;FyY3=^>n^mNho&bYbPBv0D6Qr`>vgKSKh^f@y_^mht=(& zeU&N89|s4*MbO7eKsDiw^kvwS2hsN>mf0%&zjCZThlare8e|mt$WH$tQQ!?=u^HFC z4}|W*@ay-{K<`gLA!;bz;ue#Bc4#^TTVA6&FzC|$Nit=`#5PRE0KTqjP+(NF)=$Xx z>eVcDzZFH{9%CrOeuO~p*HaYso4_KK{4kTYQ{Du=daCTbhm(tFS02%f2VKjQb$r{O z8mYyWKKnc^UpzBUUeJ+`*`x!U`uq$`N6yFg;p{g>BKrG~V!~*J&qTGxH_0rW_2DJv z;(w{}@jZ7}Vd{VKMqhGg22Aax)vWN}s>cBTX&sWF+M5w$9^jtaRMNxl z*D7aR>G2r%(-Ln_f7W1$J9=A-AE@Y(sjlCOHR7iw*DL7mGV6696hk6D%!j2^?-?)( z;gkdgM=*ke(5xeyjW`(i3-MPJN(+*G6!&ypyzdlp-*U&A{J#UjiKc8%5#Lyr4<2K^ zAJX`Swg7G%_((n0J2^#@aVGxq>kQ`^mB5^M7O>Hu2+()jko7(_I{>O`xy3OXG?{xgHh9_ z6BSw7*mo-SN2~Es$u}WQx;h8UALa-t4!FiRp(K_fi)|=SB1dhR+V%e6J!G~HAJJEl zYxyL*kDo1i^oZ3|gY}XZOW0(O`gX{RxAVEis7(JkVWD8IJrveYg$HSZf+iNCNnQ`6 z756H%8FL!Dzko;#Xv=kE#>)#Ry;C32b}f7p?~CBC>CoWr`v$CnxTn@0qSI zyY=%r`i!0iwh!5RKJ1_>9ly1tHC%8cyMk zb$?_G$TB=pBn`KJ>3&wxZuyQ-5-$Fk-UMIBbioJaMj6$mHWYq+!S_czgY#1qabYAo+=sAPO+MzMdg|g1Fg;VFw8})^Z!Y^-%pjMv`(^KH0eS?3 zryIy5b4d5(0cGG=Y0oOC?>M7_B#*uNTAZ4}dkRQeIE3{|{U=G{!r!R%epN(V?W2PJ z)bMhZ`*6h;aC0+*9h>H_{)BnuX~@WYo^^c*R6Q5=rKoVCY+OR zCQdDfS78N(w>_f`e&!1e*3b*sv&5od8#%9h zg6CpbDX_%Kh*MY$G8)G+T{{!~bU8yVi9pK9;2FZ5{Du#k6ijkO8!|35WwqQo1>8#5 z{3yR`EDCHNsdRyKCFk|Y4$)1cD82;yp5(r=m>(IzFF7%U_c0tiiyqjmJ&GH8j+?tv z&tri>eoP2egnMf7ts0vxh(61(!04akPv80H_fvXR1~OpmEsb+67gaK5bsxTV=|5g5 z0&(L3`UYYXw|* zyt6}(hl-bfYp*M6QRB#A*}0T1u_7rWu>=AZnMETprh`5X_&EuI_D`F3Ri3H6dOW## zv#}lTF4ISXzTvvVRkXk_f(rYaA5=57w?Q<5hMC%>OwH~gxd8{E@Ue|dk00gV=qoxF zxfJy>q>hUm^B0GMj3N16{l0rltZQ>&hl!@fe~^VEQ8t8}(TcvXlI zotcI|gLttvooF!1&`TU*y1XOpyE}yf{w{wASM;-Tncc7Y7_?75GvOdP#O$TX`M|ZC z@-Rhw;W_OJ{o?*?>c@B1j9ICny(CfMo$Zh3fbO^oKXx*@A^fkb#^@CL2T?E6!-CuN z=;#|2#jwLQ8F-a1>NAlZIw7a)Rf~f-PQ!8xqMIJ8nBkzk-9km7aR;H-(I`+w;1S$n zg27?;LShW`gjK?FHZV_z8MAKevRwFhlQ&YLc&ul6Yfy6lYLtV%taIyHYE~fY99Uf~ z2W2BD(dP>&G-;sdyCg~6{C|hwhhWPU93GJE$%+U)e&?Qx)7JsT+#0B@r`%SG-kkmMmulT008MI&aR ztqb_p>SCH)GHQ<`Zk#zN*AkANqMtDi1Xj1QWM^ zNEPn4zpgJp!6&?g#;=r3HWa9=m^n0Q^}8`OA+b%iL!;~wBg|y}{h^7CvCUn#m9(Y@ z?2(9O51DQ6N3>tJZE4keWuuxS>;jYqIOUwt7a62sZT0Y04^6-ow)+P2S4r!@Kjpep zWbicRzqXg(^#ZdlTvsr8>v#B3)N%fl`~OGNnfNpPz<)ecD%C3aBF7XWIdb1t$$f=N z&Xqg&EtyT^K9(coK0lv zpLJMVmC|my)Q)&*yH=Bb173ojTKp9@(PWLoJyCwLVCes}I}-MTrn|C6V9RdYWXrx9 z9mMgcKl@hOe93d+t-?@*AWu|peMrbi>*J%P}Xw+1-2HY0+(+KVUa zi2ht=%*(`I#|rwfB6hH7S&!*Y`Bri~osCL=y=&OkA#HXO!R~)u%W5;K>@KxEFEAvf zMEgzw|7HqCm`(4+u6IbI0Ep03Ud*ClK)w|)$C7@B023HDdV*UU;cc2uOqWC z>a1K90=sD;M5=XttUOEot7ls;DsbR%%F(vN3GGNketR0_(k0<{3Pyy1LJ(9Wi8GLX z8_Abh6esz0p?+|}qzFL>zn z>nEv>C?!VJakabY&I-<5QMou-QqjB{2U7s1GC}bYO}VyDIO{A4ot6w$&CG-v`I7}+ zN>tb$_x&3E(wgjha6vArB=Qr`IL$iR_EbJU#?PKIoWT4?|HnpO&lE{-?F{W^+7q{! zpgYTYEi?w!o|sdRVkcM7J@Y$tDC0o!HL`>n7qU0*+70zR)QjwW2-fbrDga%DR+uhx zO=ZySF=Cb^w(p5h>sUhP9Ex@mQecismvV_g5$t+jD6u~`A_+J7!JCmRtCpu11_|Yh zzlE~15iH=KPfO*m7nbx(#IBbO)$}RTdb%H`+mW#DAvC2(Svje0%jsdJe;?^@L3S$$Arl&{ z;{V!vgv=62+$1|6oP5mPeeM**nI|lq-!5!Uj%3mKc=kcvF%J4WEo15G{*3Oa$>9fZxzY$npQ)6OluaAEr25^0A^sp!(t*(; z;*wB7ciNR|yt}WF^R4Dfoo&_^)J9_hd*pWnU>9{(3YX@FJ|d3Wd!@sCZ$0eny%VF! zm8oj>`06UEtKvb1R1;`)`V{lQvp}#d#Kpk{S5%O<@8GC4ZL4IzI64aY{k)P;2izU7 zaeu*iP?*4gg|M{6Po5|yf~!Z6U_g0P5$GlKUl}{tx>QH2th`syyiYe>fBvm|7asV5 z)%cfS8$q+KC!NDbN@iKP_d9qbjO)#udHSU1)<7b8SQF1!KEt|f}?7JXIQkMtkvB^Y}pd%Xbeo5_W;Ebky_ z*K!Ee40f>!(;1daxC=5QrR2D8m9gay?k%y~UifNZD)rp5>Y44xFP09^2f=DxNWFAM zl-Gjo~J3MY=!$QPaGL(kuYXd5Ht6BB+V zEz0K{YwWg};azQxzU|%F zn+V&kteVJM-jA_XA4h(Wm9MzxBC&6=UEvn`{bcfg;=gW{8Gk4-jSIX080Q^ME|^x4*A`F+G-Q4u7-QX{1w9mU z*c(`gf}C7^vxo{lNdd|iPWMjITUvH$33_1?*V6ArGT)}@9bOul_GYq3U?M!7S4{@A zhwa`TIzc>+*Pt|w6evlSPT|=LZenof-Fh=>Fnqg-^+Jlep!f>MMINzd{;b!H`(vZe zY<)3GTwYUnZB_MRQx<{c`7MuOelw%M!`33~_op>es=eEl?4rs2Nb(ZKU@LYqj_HxE z(Yu(yI)bg0)Xa6?7!Xe}mcI~uDQLEL-9(W6Y2B9(4(Y1hC12JK&EBS1g@TqE#|3gY zqA0-?|1V{m*uJaB|?e%K3tI z6{-hlQL#=;Yn1j%vMY}Wo)!7;i{n*Xi$`bj*vK?Dq$9?g5>`OaGnJ#?CB3|%0Juv; zJ+%D5wEW^`;vDms9Vn~Yf0PrE`56&pkQ1*@itW8L6QjPP)st8ApI+o;?`zP<4y;%F zls4@zQ;t;8_XbIGM<2kSN$(*IVa2-7o(I?~0+&V$rQw^ez368G0_c}S+jV+uiy#dz z1D-UZSYQNE@T1I|!D{1UY?1<&F??FMe`Y2ul+mJsfY9;*(M;kq*s}w5@AzE5O($BA zKu4B9x4bwJGyEnyCqB(}-Cf0ah~zU;Bl`}YPK5G)(%*;5eRs0L_ai7@A9l)=e0rmC zHzjDGzzoh-pKdG9}c>1k9eZEg?GJ@iVR)SlnkN~?j) z>1fBWlwUD0FvnW{z)G}<3>p%k`mmm_g-@|E_B=knORG4rp4|{6qB}APmLdH3+|wQ| z#xs8Cu)HVwhCz|>q#3_!h9~p&i#M~2{e*{OAw3*Ib)rvFY;`1>4;G$DNdZ04UF?SF z;L^e!G5lEB!P0#??CQ12rF%a3?l=os7EE}U&!@NqkSIrKhY@#a0)`h!2;A4mx+6ka zrKyWVl7uiLiJnkpARwj5T{;=)1XmJfFI93v5mGqfp5ZU7{q%KeO0Df0iMVOH54+M6cQ5vrod%$tiQI z^$J4>O1Ams$n$uUbA`VX?k8LC^Cgllci^cs*~x2Uz8 zhFomsZ`8Od9%KIN9{g|j8{Oxpr2NiYg27TKVr9&~2Y5#6!JeU=5g%s+Tdn*2_S8bZ z_tet!3{WBk=&1D068HGpa(J;uyg9mDc!o}qq$`S&${+5%>9cowI5nO<G2Yu8spD|XFpmzTzm06SqHPwQDKLH!$+_W!mMo60g=nqD_wZKju;*!?29M2wbLX(V|4B@8!k?p@^5kd;S-@rhk+TbBqdS-r| zlN_8$rsRaR?N@#B!AR&{*NpdTM%sr<5qX6DXt6uzB4$v%sOPWB6x#(Ps+O7S=ws z3uA0Ms}sh~1~=iIu?}1~Yzds-znatgooyn53mauOrmpLU@(#&wTL(@*Ct$mT7r?yGMb8hHq_7_Zj8u*mI@;%+$ zZlHdCF15t}N{Ac(gv8umoM9T8uc%2D{FGfNKqYRv4z|>i>3XeHz7NL z8|P&}(uL}r@)6s$^i$Cex*J$n04Uhd6`eS8hi(@#5V32o?g2= zFpzU?Yi57plbh|e0vM0C=0cKTR@m-;G}(85*48oj=|JS$87Ir#lw(ZYX=5X8V;6)W z;*#Y3%an6I*3419p}BzB^?pnPqziEYaZo(>b*|xR`oTr#)%6dJyMdvT*5`$e>Fot> zx#_!l!|E5kI>UZeyAll>sFSw%&JWQw?F$pLDi0=m#pX7wM zu3*yk7{{w$ILjbmD{`SBKIVbLd$5=i&&6urYnCH9Dm#t#TR^J0+ zN##YzGE;(Rk1ne*uEd!I&Q!dVX5O_*zG8Qkl*VgiiNgtTG;Mx8SCHz;q;_0>Blr1J z%TN7GIHMw=^aPRaZ%!F+D5X~K%>w2guOnK4JAd2iuPzRs8u`fmC6M&SI|vTGK!n86 zmr^(sJajN(9Q?ZIMK-})`eKiBLyQsWiiW$HFSvZFMs(;Pyt=9fF<$I?k>|!UVGdzx zkJcR-o&Q4p6<^8|(OaNWeTm$Y%hspo*aEcl5xbxAdz?{{D?rQqmgl0Zni*^gKRoh#}8j;uXYAo0a%M_JUd+iXRfpR!OESuUeD7?c7AqF)l5Jv;mxX!m0ky7#tLP)6{=~@BaiQb9T(n>@;iu_e^ zko_j$ga?14pepl1OSnck`t_s%2RVju?Y~(7tGdTz^Wk|&#j8zU!yO=)3?Y zr;aZSRiCoH8LDo&KJs|n(_7D#oT{P55&CUwBF zd9%IL$?kTG@_SvKotT8A{gx@fKAa{(<4Emd#GHv~)1QlsU~%pPegYO}eO(St0<*%i z$1#+w9wA^mqA0a#`r4L+FNL=1ZJ!h0Zswi)UpA1!)YZv;4dW5^d-99Iy&n0GFx%g@hly)nLm21Yy`Z`Soc#u~q)O^dQW5~Cup zvhNr(X|00t`E+H}Xd~)Nf%XE#l1eCZ3cc{I8NmY7%6C2iti0uz8`rYA#?dY|;JSROteBYKN=OqLge+*Pa{3C{@eH zSDY69>#t9Hv+ygdE;sZ@Z6Q=HUDVOgi;^4}R~%$d*B@hZyVWjpa&>q`Vi}3ELaN?@ z>p6i2CEf#T&X>Q@7tcQs3U8tlx=FDer!M`4kiTNDt==eCqCP60qp|XymIk6}NLm%; zyJMC&^>mLyTTu2q)#`aU(;7{hvF`Qk5R3(q)ffzFMEC0l5XHlfRXyw||7Y14_Iy{= zZA1D&Pt^z0MKt}MuLCwY(0M0HNKyE^$YfFW&kgRKg;$BPntb$JSt(*(APvOgwG$Fw z6YEz0@O6t3zaS}ud}o#(-JQ4)fqb;KAW1p;Q*~J8{CoYe(|!vfE-xl4BXKz;Dz&fK zl7J+^Uc6h{?=byz*oZ`mS#~uJ0OxRnB^?ta^jm4Q=*M44n_`5NbsbY5czo5D+Cg(A zxl#LMgDsGG$F+pOan|TKnLma!q2zV&#>+&kfc3Mn1k{&6(82_+xAAc`N!lxlAgJ11 zUDC`$ot%q@hVcTAQ}ov~56iNF9y4&~nP;;8M!%151<;Cbo3Aq|JN20TC!@_;rP|K9 zpVn7Ld(Fp&xl)#rh4_oO26eQzZKa7%Lwz5d$W@b*L7p?<0baG4FD^%HA^FF+YUgL( zv{g-Hs<4%~hOi!G{9gaec69nFs13&qxc3pt7l?V@?TKeN04i0U&O2N7Ihd0CqqHz<)ZBSV#T3>8=W#=%o>tnWWb93Xs*WVtQ$feF)qf?#uA+ zE-<+Z>h1Dfs8rR)f znS9)AUD^g6LJys+Gm>xy(I8H+tzL#c=D>Gj#g3|RSjGEeO^tjKX?!ESGG3b z+VLSoXZE)|J)afw+o@7?8hmm}(zIEUmJog^8g9t#*YnfucPw9r$1WQ1rlLt-6}h)q zn{M6txV3r0G8BgXox_Yd!&|E_bw~vG)E0kM;HB@zpvG^v_}+NvL#foFF{E6$H`mF2 zQHs%y0=m}*LvvuQV2yDuhHWAl;RNNt;=o+G{XcTz))SL?(~+iTZBpW3)NMNMs+C3i zomY>6^6b6XJ=wL2KXy387(u5+ethK$befd-csnf`%iJ!Ufo^Mi@BFjt)4Bb9i?<^+FM~FLYLBrqBds#aoRjzkHaAv$cS;uAm!DobLdc$ zKMJMte)ILOR{Vw*`>kTJ?`Xkm$IbQsMFr8gFuojAci4;?65Q4M#gD7kXLNh=N%f*P zMMujexxU)uavSBUv7c7vA!z3pX$T|)N|EyOfVF(NIm6i`Jv-2rs-jzUJ!Z>x$u9X$ z-#Iu!A?#`*w-;}_(}N1BE+oTx$EDmT?&KThGcB~-ovw`SQ$}k)ET#jQQrgk`$rnLm z?7i6NWM?m+BWf!s(L~D)W{faP4`Ws9SP>2L_`P-QwcYe|UV?I8k!Jdhtd-WP*9Gm) z5<)PYz7pzt>)|vWLQRAQ^podO(V{~SxEi(WDDDOa7=pddncJLdQGDBT=``v*hf$(? znpe4Pa_W~RT5;;$WKaLVOe_Ygv~n7?Yn*oj>XR=CDIr$Z6Js1BBy<$S<477mV%{Te zljKbKU9)4su;hz_1}w8VVz7FPZekw1Oz1+QFJ%#^9I*Vvyiv-f9# z93+d1tvq30+dpafayKu6Z`|n!SQ*^XOHxY$(J5xn=-7xqPDQVvCBh^9jZeRH&Q_u^9Qdw_bwhvM@C9ej z*ZCSHCUr{h0w2O5QAddH%|oYqbYu4?#bxO;tDpgOR;0ulZH}(Rl@hv-(d?|Kqs!k7 z=K;N`Cd)2F(lNrMvNYo^yyT%{Ns3J!li9Cd+n4wM1>X@0?z2foBSINp!+72`B$Dnr z<>gRPJrfHj2i>{CI!_daX?yvN_`zhVVyGwY)?>gCowLe`Q52{k-Q)p>^L4M%kkZR` z{oRU}MmP{|){kMZKo7sK3g{1ezn)z#O@xeb1#}oa?vuv7+h0faIarC`X=CqugI}#S zU9Y2SBnLQDEM!)8a%A8dD&vl#;i`iY5HrL&O4|`@B%PX_`Z!Z1rl8^dC8pZD&QFK8 zfEc6Z9vptEvF)%Ed%!xX@P|)l!QErq#R8)}^-HWN&HeM#`^B9#*@Q3g4f)4LgR%CU z|JL09@aM^$Sog>^-*jGTtn}zCs^{KGb3YLd)(1CDm90H1Fmv*?OOO*PP*0G9MT1%$ zls=w!KCuW~X}sKApA|JSvwOy(ZR_e)a)Y;a!%lnY=w4n8>W@cGjR!cNFws`wupxid z|G4Z_Yq)GdN=ysospM+0C5u#xX!Z;!H}isjvS|+jc0(FbP#b5vZ&$w21Gul>$+AAq z(Z)*cySynbO*@v?0X{o?yndNYZW1;Doy*; zj_~M5W@!&Ffqpx@TZ24727+6Tj~4bQb9;*+SXS`g5LyDlx?W-?1e*f>OC7I9y;PfL zhln$%kt?2c9mb*zSJ_o*>5B)+hGQC)2wl1b?NEH36KEC6Z#6S|Xc|yL8RcluZdRp9 zH>_3oKNrBW_sl*-a6fhryTeocbB<9R3t?RPTydLdlFMNi=qHta8uVb`30_b( z!VzuH!g%*qqbsX_J~B!bB>;&{J&SJNB3-#t6Zx2zLSxC_fS#r7^FOE93CyCFCfGfH z2cA5U13O&G*?)W2yl}(Rj~Cc%R=*?NmheoZXbuNT+LHL!!#p-H`GUu9KqSZ$b*)Hs z2W9&gG`2>#J}g3raMa(>%Q2wznfJwosmQr&dYo$hBM07vVbiUtMwBD{oF3bF=eeL? zFE-W0D1~=vpFfeD+5|HtBr}aK!_mMdJM_+3jpTP!1M!@>QdxL^1GTC+0`d8FGUvuf zF}F=MBr4XmFW@tlPS}6KN67mCroF*moE>w=2G08~kQ<$Npntl4S^>4zHKMq@V^lcl zGk44!QIJn)Ssqh^`+`??5>VWN&;g6D<56_fx&2OdViu`ZvCSUuR3Cc-eTxK9pV`cD96RLk<)^E97z;clC+=#dgU64@akAa@Lw_J7$6 z6&Op903=uSmGQ>K`$kwUNYN`EW21;XiIZ?!|I7rst3|;dM&YqefEeFFlO*tx{G9t3 zn#SrEk`u-Lx^mauExG4)$J`_Wt^;q@-d8@+i{OU%MsYg=4(TSp`7RR>=TU2a)n#b1 z!79R@rILpRU(i`iul$p+VxVi9dQfJ)lJzsqb< z>UP%wxi)sefD62bh8O?+6Oislv?wjJW0ZRC!)9M>Q+i9=Xk0kRMX>477pe~rVh+X9 z6Ua^A5#28FGoWC#?Z4sv2>hM?yHQX)d4Gr%tMxT`m(pSuz7(*J)W^kc-xv;pG0x5_ zxWMMvTeE8qGl76{sLP6ol3XL95{f~D))5NLoN25X zZ@5bT@{WKGo`k(_4C!$#f0#3d1vTM+7j|k{w_b?r)c}76nA-rOqZj>^seT`|jxpi% zs@}=a7XNM6+dh^4`3OhYnsj(z5?&U^yG=BbU^C*x%X0E0t*2na9=#38*q68^#YH&A z?q`+}kRkoO$rX}75e64cDV0`}&tE9y8dZYZ+UJTNqjDR&j2K0ls_xT9I29XzdmMkE zhjKH0=vBZH6WWtt>r9|ig%+aDgOuDa`2+RhFx^49*2B%}xjy8(qRT2P_gvr2m6Z5F zTFsI0x%}2Nvm3*uyW{(#7QBnM-)-HV3ouO9C|%_$@^8Tels2Ec+cEl5nWg-V-4&iQ zYCaWu%4b_^GN-7s{}e1UcE_Ik7pP$%WmlzNuyd z&%X^d8d@cXT4WC-%%+{J4n3k&|B5{qLRWr?vyKA49BhjXe?5;mvq`6^B7$gd!&%KD zHi~zL-ypLj580Chb4Wod?g~~>N6$hnmIPK*gp95NIF#AVlySr`cp!1a1 z6X{Q25HS2+3uDDXU;;))_}e;(4*PgViX=$V<>0gRc_(FgIZPb;JNIM`yq#|$D5mX3 zR%;iXZnli!yz-DRQ|A zKvx5Re8gpx`v{3`D<6sBK}5*Bp2)xti{>#?>z3>WB|#^yl%u8`1&KCO2Y}6*Q#BrR zD`|+IF+vKb%y8RwlqGcN_Vv(BVZ30QpDfrBc9M}PGoM&Yx}Glx{~{zS-#|E&{Jv(U z9cA?YEPy;Mc6yTFE@vJx{^errGnutKtj2)Gm8fb%fi-IS_x*DYUz{kHIPZ_3Gvw{b z-X{Mp6cfU=&^|Y;u|kXoPAc2R&xB@b@0%Y_lakh63f~pTb*J!&YPo}jpl3p_hOg^-HA&Q3oA4OiNf@y6^nE4V#4duM&D1Go>5YL#9mDQG~j- z8|qjyORTf<Mcm*~+Ymd>J4o`NY+tEqK!V=>^5l6qMSAK8X%;I} zh3Uk)qj-R2D%ym1uVHs-f#JR8exB6_lZNiwden4k%yp_PO(m?sk^Xi*vYXJD2dR>P z7?*eN`7*zTHH;J-ALaZXspfA(y!~7h!QG`}?@&T{SeQ4ptAX#bjGq2Ig#haF`C`NglsVOgFHd-&gi*kT7mrxAo^WMvQ9z;Yu}b2o80 z7$|#%4x`VaXhzhTR+_Wta+VGG=|`Bk&i4e*s8WtW1WBDTlJ|0m<*5E1jnQh!E`#{$ z%z2&-et7>uQ60FT7Ar+gP&q_Eetv!-Z5n+Tt+`cR!7?PHW` zWTKU3skG+Yf=(}o)@6~LXiYxJ9_L^AA3c&px}Go?Gd+KHk1wA;`YDx?>on3jy|@R* z?D@3$e!DiMh_P`UmR%>(?c)x6$EtflD8)<~b5HEZdc!`JtQ7})3;#azi;iHh+jOJHQ*BVR&)vylXs{|cnDTH*K8+Kk$uA30`z)T$n_GEuXttTUm+*;n-~Ok{ z)ujqaezwOzO(sgL!{^{}$Lh*AlN8J>RcM@jy77lQx z?^Ecx9%2NYyB%D?iE6H~*lZ{-s^HF))Pf&8cl0kEygxTSgt(u|dtRi4l6L5L^XGF>#e6Ld@_qjp3-=(w>!?jw(tonk zPimEb)ZEXQP~pVW1`^kRc2mB;7w!1x;av2|1>geaUq`o@rQ3*$XYpUhJVXI6?&lc@ z!IxHkq#JWTH&&GvwSAbL-88=iE}oLyd|ZjuA8t}}qDZ6mpDCOXf9YX@%MdQ5#@6EQ>{++9XaZT)!s4<;TOg2E#@TZ=< z$f4MBI6hd-M<9jBO1WbRi(Pz9=!KBdry+s=%)Is)WY$pAN|ba25OJof02R2w$Fp-aQ-2477>oEyRgWyE8dRs=6>KAi+HsihvGbH9;s%SzYhO|w7`4zqa8C7nzBr*j&& zn4H0xW(BVkYON~fL?OiL!FDMi;epz8$2_1O^}=Ps{w2hIn&Dqf1P2J*hQO&<4bBH>=7D!NMouL3 zG5zHUkmnd%_&Z%1F~pBSHc| zN{%Ze0SCn%y8@!^pcKE>@_8WscP8* z*d5-25c1a8U5%nbo}#7@*>mf?bH5NKlvmf{au}G>n;x(~+j+D6W)gP?OsR)||`;%^B<5xMISHr}s zG|Pri2~!|~lE$(xR?no3e#=~QC-5Up~zPx@!@*|ETXo&y^0!i0<8Cn{FW!>9wQeA~x8H1uJInXVfgKA3obSX6!%=9QT|9x!sK;fEa z`y05>yFL`=ZO^2NOg;MnecP#E=^Xr6T6gb zC19il2(BB(+#MxWYrNw>qMv@)t6T{wYRY$Hn`O_;27V|_6SRVYXvw)g*;J=aMPOHw z^D8M?ZXhA|{J!=JO2fh$;6hTCgZTvyVt9Gyfho$?DTYIc@Srg!hzsZ@Gn7}{L*&pE zC8uAsan_NK>-hC}?p0wn!(QQ}rsqWpwxw@AJ-ikenVyn{J1%_)=~JCw#RjE5d03U3tZjm9YS}lslHBq_Vw- z%K2WtJmlMFkA+b^4`Z#rcd&A6#z(y{)!%6Z9Q<{}3dd_MnwHwW@q~0P`Erb!B9$_S zkqn`*f|0ffIOz*kbJoX+_;i9dO+k)mId)b-I5FVnV)Ve{Fh$;;XoiNg!TUlleJqy* zm2PRVh(3u(BW-busy-A{BI3VuxYP^WTowq3wiNFk5LZy`T8I3y^0R5vd zZ|R~j7OuZD5yuoes)Je(rqlCvHjz??$_=rgTs{V0Sf2hVU9#)12n-&xL8K`C7-`b# zbYWHE&rb{0}h3%lnrM+YdJ#c>CGeJ<2on8 zcX?4=d_fhel^?&=PFmFcYE5RIBNa1YxPj|%(s%14aDZf@eEYrYp^2E8Bl(f+JWM>% zPZnjm5r^Mytjo;!!<@HX{23GZkM;Q1`cr#8+A%q}Xcux9^%&m!=;5mu*?lVP?9nJ< z%AwN-1|xhA?<)TDvn$LW9uD_8(i9xYbWxd%@}~T>gbyOt5R~)vU3qUWG?`|pnh=!7 zFW(SSdfwFV;-Uu%cW;VUEwqIBYPq*?k?d4G`d{Pl3!{>rEl|}Do9TF@UEeYMcd^@+ zs`Y_|Vb*v+UyEN$@5e^Ba$o(nKXY5s-d>YuEsN(=BfmS$HC#Co=g1bhqA*oy^w1q6 zYgd#FpBt|-GncU*nGo_i5avhE>=Dv_@cK7$s(Wq|m#dE0gOH7exWs)Ay^tyTY<`~k zs%~Q$`B7spotw(7rMWyeGF!4m<{Bkk8a<`+q+*{eJ4cuyxARfeQfj_C$=L4B%81=snd;VbxY>9HJ-R75 z=wE=O6jIQ%FwdObx8p2_*{6if>D}N;8;%galX+#->vt>sx%r^n)vCnMd3!fnZGORx%GUgjyp?_Ru%;F{@7K-BDx2w(R7WU$vFhkUXY7u^k!uymmMuq zATgkP>IzclO{UGsc=hMSz`YQUYVnna94n~W+kHctc8xUsa*cN2Nu1I6r!pYj_+EpJ z*R|xo9c~YZW&GJhf)gB{oK*=3Wm_k%6CrY$m8K~*rW7jVUt!F;b9%KNAEOni_1?=@ z_|xvQQ5Rp_i!&$Oe+YS|>vV6-hA6GH;=-UbQf)PpU`pXH;c+(V)-NMO;z7zBzRTu64f$j=9|5W&mV{~G@;7IkKJ_WlN z-xmQkb-kMK1p2~VY;AO;Sm_SOF|+_}LE?fxIXF)nx3x(pt>#ef3b8;+e13OGCiZoL z9}E>H(WZ6|{C}8q=YLzJ3mKH7N>bE?Jdq>HqZBbj*b_-BSJk=9YT1$2bE5@S%0ijXq6kF(^n6>Et9%!aYSHbbep2EY?^i*O%#a1?&1a3-hyEfl}_~I~(m2 z>qbj)LLVOvwK@FS!7CGPYlSQV4Z8G7h5Q-vCOZicpFn#tl-P2)7u68b z8|LNhLr-^-WQFX))CYNXTw$s4>O5NPaxA3nwi#lIDQKy5A-E^sIJJo0|kv$JF?NUefyw;_bJO zzV90V&(?S*6^G%e(X51aV?MPj*7fHT_%D`g{o0 z{I*h08=>FHO(F#&f=Fk9Uo<&N`8t?#W+Ryr1Tchkk|3SX(>+rKAaGhfU-i@+J;Yt7 z?FxXQh_5b!uCK+wONKYn4ngtkxzkjQDeyWdc!07kua4Z3pe!P8wI)+RQSW_d5fI+D z&Od0=6xk0JI|x`WrVfYH)c99tuY%B`d96`&T5z6l*HfBrEXu2*_o|~)_^qZnA{n)c z&Q=iBc25t|0U57ty`?N~`umvEX8g0&FW-umAQc<-7B*OwPlInM|A$(6<;jw9Cj1ds z&Sdkrb9oSVlakYZU>l<|>yq5LCbHAo6=3P*sVs`t5k;m7skkSso@LHWPO3LE!;ZhI z4S%uq&AmwT<;w8hdd#IEjL!sRe^9Bz#Z{eCwf2F=>pVn2*NY`?6@V210nnkZ?wHBh zPdc%6?y&s$qjv`AKL&sQUcxS#f73isc2qvjE#y5`%pHnp50aI(X3u~M$irOXO1W>ShFpHZHAw; z_-?B}Aa`-(LCsv;9{JL?F|E*{Cy-3I{~irLByVwFgLrI|a=JFm>GMl}m@8+&{|xz! zG{(N6{w#PdXxNLRTI``7%yjRMKQhkklDm}j)j<7R<{{ZqJv?dsWr(fH3NzCxecSMa zBd3nczQAdsnS)!Uuq|q!5z85vi}8>sLS8y_kyDGUX0y5`WMp(4dGF^}_n^(x95Gqg z5gC1o$rfG#NK_$;?^NPGPG0jM34B+eCMniGSur{wLc1m25VF8y}=Hi+p$#c%l9Lnu;i$TgRcc z%8YX0I+)Oa-1X{|J;1rDwKJH|UpnWBS~vxq7WdbX-;`;W3>PnVy8o+Lz?e+=xLn$w z;@Yv0`zNXP&kvk!j9Q&crG?(wY1pgj*;Dfe^3e)Odz+GKv;K(5|MlW$T598L<94H` z8=C&)*Q8e3Ao;-FJpo%ovf;f0&I%19?;*bly79ok$VNQJIMiOk~{cuys#ghysZj>6JgL)tb%p(}ns6 zd;vnW0g6z7@U+ZgkMw2=%!cBey7ShnK4~>UqF=3jeQkOw70=e|LD3>rW90Lx`LML` zRf(GAs?*8UQxS{F>(7?Wyl9g~l&`^*OCzTTSDbmpEj#r_UA@;tKAYVO6;MOe!OJIr zuOM!REW}R|tWU_5I{mG&ivH9pT{dr%86ykCU18*e zP0M)7>DAn*FB;JCAeu6q_k9{M`2z!j1JEEz8tt>wI@nO_k5z&Syt2Xa+xqB;a~%Pc zCl|Cnz(bn-?!&Jr>tBhMIYqt{gc5M9j~Bv};IHBsnJVIRm45$ub+HrWEE#J<3TLAw zV#I@f_j$Wybk1ZPoCQ>JEPz2OT3;GLDb|a3A_QI@_m93R*S_%&o6nK9zLNBANr*R> zh8N@bmR}-GJf^T?xFJ-L?mQyMoIy^U`WgDxPwzhHh*5ai8&3VVn{x>AKebuDq}(-k z809gN7lAo?fiw+j7cK)VxedR$^98^APIE(_BMwu*>qg=@hq*|{cofd3lEwRwm`t|U zExYGk z5ZNj3i><8nyHBrtuEP=GZ+aC{Tg#1)&pd|JP2K|LHlpe}fQ*Vp9e3zpI65*acBnlRB=TQoXrY zh^+h#eqr`Mg*EuYEG8Eb>1&`Qa*p0xB=LCuV_XG*BlHStcuCQS#M5EnZgAWnQF-G0 z=C+8)VQZjXTROl*%gj-m&Zyi5I@}WtC?5~EJRRm%bxEJ)sr?tRp)L$M-z0yz)UM@8 zd)DuF&`u~5WQmOQza<@t)myG0oSyEIkOYWDf2t{tW z-*zfR(!|-a;G%W7Mm%wZ$Xaf<3NIKgy);_vBX{`O1d--e+m z^$^;IG2f~v#85cRCk>XsUOT&Bj6Em2gWySS)|?!^GBljY?xrDhV0|DKr|cwYL_PrY zCFasFuW*g;gT;_xzi(;^p>Ex4eJ*~8=y9GRdHB~&$W}S=%YT2S(dWi%Pb<5%Nj|Z? zU!DFT)r}|Qe!n(QibE+7s261h`WHsaMi4Vx34?SFV>7R6hZE@&8h5d5pGdVw|AQYA zO`Jr5j9*HhOjj-YI=#J?gbsh+u(X0UHj}o2PtM!@x|6AMYpeu~>E!a^y)Ry@X8bX<`5o@b=c z=~@4}lwy9!*JiU42m6f6yP^Cza#?vp%;EePqC@#LP6*zRrwe8hfw{nurPCdCk)I|n z6MsF2H<)trbV8^$9H+8BCvX)!P!tT=F<#0X#+gajo(yw(BNlm_?@$d;)V+wAq$-RAADSy^w{8#sLS2qoKe7w!}gwpp=e zRe`z1W1i^o{4!e+sgvZAAgghkl3}(^tfYRBW867$7oDMiF*7a;A6|XrM@Uz;qu_ng zO3B20PWFYly!(E}Ld=Q3N*QyzfbU|y57mI~ZFhCiXy2~Ch_NtdvieFH3Y&9yne*0e zDR%SW*h*P@Q`7F1m(@@DwbwYj$Jsvit_Bu0NQD7?`~0JW7I*x4{6>a9cKkHbWbXgY zKQGp=uHNDGQ}b8E$pFZja|uUyi0u z3o!)N4|3M?BkTwWPD8C(*%wV~h``7aa|+n-Q|Ke&Luimz**D?W|Mu(ORi7W36%BfM zq+YviCn6qTf;Yw>JQ=~v&tx05i1a#fu3Nc+aoX?foY2={fT2;ZVIFtHBv~KpKhtq_$SYY%+*d^EPnJ$(~VDoqWN2zM7^G?qbYZHryFnJg3ZU)RA^^U=PvCl#!62V(36~C&4Z;Q&A zB#3GnSGYSAt~w>1FhuMurh#-MnwKMjJSn_TP&4_XJ{L*@jK{t*$4Tqt=;VMCg;9KA zA7k6gIk6RXaA3Q89aw;S*{)zaD4 zjC|CF=$iROo;Ch@Z(ns#>CK*>vx;!5RMwpsOFEK5mBH>PEQWX!-wUWJ_DXOBc5XBm zGk<*^HjCwoaPO$r?ZGh4CG{(HrEgZv6%>gd!{4&pzlTS`U^z)8D*NfLNvbWBZv}O; z`11NQgoEE)ccV!Q7{C5SRqOJ1>=RvMM$>)iPf7YdzjFrD#hA+d!+N%=Ti=EN#yMY3 zeu6XojCa_p6b7*yXRI97`Fg1fpn@0S2~LGjB*uetD>0J5_PJj%ZRy>M_YFU-ayGrw zr}FPW!4E^na^!F7`>~GD-_uB*EAL23UYqHlXF8D5t?KpRm%9|KjTCT9LF_p*Lrnp; zvUXjF@18pLfwk90Keje5(={&ZKRBODC3X6G&JdEfdZ9PJbSv-7z3)KN#ctS1*0m!RFl1SbBbZV^sYld>YV!Bcd=x zCQIfX?I90JZaf)+Fkzfp_z%i4D+%^u=U$#2VV%g7Laj^yggKDdGO$C8(es(`nR`l| z-a&s3;B?s?$TDBc@^r>9AUS!(Mm6pGQK{Fobsl}qK=~a6qLA$gCj##kH9i%e!uWe# z26K}VjBc&f(XmYw^mC_DT15PICKqqvBId5iTBgCps@-nbr1PX+s?C|82ibfIvX+~d zuMg75wiLaum7ET9Cza-Zww>+=UZlMkd>Z^7<&f_(Y|2fOL(X44$I048^z|Z<8QrC0 zgzIw`RB7H!cJ>pB-wAjP`r=C*E&sc}Hy-n88Yi|PryG8HI2tP79#PW&^aXbWB1TJ<+LQ6ARm2vLvOh!?s@N!W*C$Ju2d}~<3p1N z5t$RL3h%55M!Q-m1O7n?@7qXBZEA&38~Uirjnqc!KDGn&aEgCG IukFO)KN#-e407 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 z�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

f(8I$?ils(LPVOmh>bY+ss;}?dVV21T8 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≧A;i415o?0 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

xvEOi%BgMu_Xxa;7H9& zn<%U*V~TEr31Z2-x&uIleB$TP15;+0ih!JqC>zi>!10O->y_)CPe2ai{vFcW{0^*x1{M2S^6G32mH$`GdaAA4EY9$Y2^?tGD6n6R>i!esbJeyhrCiXQNbdp4i=s$?`Y z0JaQ$_vQ+mRZdaa-gUc4W9h3$B&qt2UqJ0WT^DlcF*()=_CIPV-O>4GoTi|A0?d*@ zQ94>0si?i8lG2QJr^A)SSq^7WZe9FzKa=$WPPn$KIc0ygUZHTZgRWn=+N(qAd|w1f z6f21QCJ&_(i#8+pTw+6b`eq&Eabal{R%~s_4a!Vi z;$j&9Sp`_6cfs=-h;UXaz@m>4!#C*~+;)5r2$|3n!f9LKGq7qQ1Y+D=OQ?xvcT^M}NBsw5D8#N0;$>$CxG;$D5F*i6^MlpDzChpJr z9`Gf9a|D~z6bgRa<${#vNk9zm zJ5>Ms|KFw1B1w{Ul1hc7EM*&IE8A2G*(PM)6T)yv$T|tx_d@nv*0Jx5eP=NCZDy>4 znRCAL`CZrV518wkIoFxj{kre_`FuP(`ls2VYEDVhFLeFGBJ;irN8qHPyxO-0Eh5s& zJkfTeqW7xL;_M~rKR-;Q!bq-Xl$By5u0NGy)CgOf7=!r-c9kAE$gQc#8u_Kf!a3#S zOk=z&K$_|7h+b?Aofp6!LKJr&YIxn|PGzNAeC(4?Kqu3#@9po)uPb zA9BByRm0}wz7pV-H#&TI)5#7*X+X{(O&LP#^Bn&<$qzq#q&GkORzF-JU)M_Pr2mwP zR<}>bDbtUOsx=I01^L|ECbUbu0R^^;aJFX`! z{5K&f=mB5dks@+G>UoYHsoGX4yM1|yDR(naXk75$Cw`@bJ7aTN&3~-~f1T#COt1?P zvCIro3CqFIZ^a1Dz;sr|Fen%x3ocbMzx`r&g+^mIgd1$&OOp6KK;K=TCaCg?YL`L6p4LRT4oh$k>%uQ@uDbNGXxUWw-!L~S;jWubHDHDvHdZY$u% zKbm7+>9OnngfaCEUhn+eOB6nh#wGg3H^+$JEwgu4s=?l;fy4ge9IJmgLcf|9mAJpo zDW~$M?Ac-lYptmx2h2Vyu|@LNp5P64Fq!o7n-G8I;7`lP)jY7c)%zOTdK+@yDo>k2Gs*|O5DO8B+d6ysA9WT~R@FhBefEYGXcEYG&_CI@=0y;M zX6O9?5D2HTdnLs^x(d@I!$4>9r`JIzSZE$DgPOm9WIXBHpTf}EMu3??(avRH z>I1apK*2XVP?sI7Yd4|xwnN|A9asww#R>dOA$$VWMR9F7&jT`ri^dXo?$5*=f{iP# zg+xyZYjKXCNs^7yodYUd%vNq5vr0K^_Q7RI)c34l!g;rtQF+mZwBzYyI%;nm7Gv$!N$@rY{k}GMfc8l1rXfUwU|_GMIQK($D8oWW9~cI%ryOE75^Hac9V#66 zr*~{de)h}-$y=8D{MvRLM52#VPm0J{XS$&T8c9Y zIdI0)RIOpVdcfLJ%x7~i$1hvuW1!+N zatO=3*KExE7VycVd6s2u9y6PAZqTNa38On5wpMk@Smu29Y8nZ+cdVq}LT`6#Afa$9 zc6Z2sbg#-{+768wTeUC@U6C;D*?Yfy72D%rs`IV)cXZ;qv4x%eW0AeB<6o8TlVo=< z_vg&zF5>iWzgX^0%HRsH&L58%I7T&l(<*Iu`G#PeG&h;_2o|PpGcnp-yVNgPs6Y@u zrfCgDD1!pxa<$Y0{haF(h4rP%2+O_Aw-Qwjj8e7hs7k+K9q-PSgla*lPkyj*3ie%#!{Q_J)h_FHd}-sXZe@#Or5rV)a8T2)|2% zM-=A3rvls}hJw76i@0(jwnWRSHf&q_&wbq?S%^7y^X!lJW9*0 z7J8)2POp~cQb5IU$pu>~8gnq**fYyJdfen-^PR=DRVy#=Dc|@aEf?_daq>6GaR(8$ z=2?qO_UAYI?>5>%?>C~kY0~5OckA$=g@1anZP^WfZpum<#Cuh-F#lg<*oy#*5CV03 zd~>e5;a7bB7RE4)&VPKhw7haICVmZc(71%iD*cqstVH;kA>1HXpsxeEcIytANIuMOw6(-WZv0LZyc3U zquS~t{uy;46W;6#$fUp*^yg!y4*btfj?k75l=Hy`SH0ncmMC{#>B6@5tL+HCBr={6 ztJP9o$6dYjZAN#(`5fl=Z9Fr2LRKuLvG7ALxrnvJ5S331$U*GJ;!&|F$MV(;xP#9^ z>F_AVl_XBiVpkg~w&GfDj22M>pn;|!85YeQ@2o|+`lO!?v`cklf@|J8B{ zqKV|J1wgiYCbCKzZ*S6&{&iV2-!NJw8@^FU7Ti=!GBChERp__$?+6>mBKY#{{Dvs1 zf(ResLYx1~$-_Q)X_=WRl-70no$v=C@`2x@bi*)_d!doZK%TM7=YewaI%!n0VarAjE>C)&KgNdbbAov89MYLVG2CSK8OL z+B0gMU4RC3*hO4&s0Z&DMWYW3;XBQ{E5}DVMm;3rM6APPrF_Elk(P0>a4#f864lz`M17&e*b_7AbVH+iHOeVi3`hN- z2BCQ}lKlK|gJ0jbSuwb;F$mK7Tslq?!~f`+EGI$p?JK|!a-%0r#qafBSg_jLt8l_f zd=`;AeiqV6g7Ojj^JAa+~j$yoMH*Q1UX4Ds8@Hy02Ny@HZc83&@oHvPm9|9#KN?qwx|O zwv(&xWk$>!d9hyIGZdrkQ{gQD$psNr-pWi+s znSX@nohOKY?sZFZ)UNQsG=>~+L>_(<8yV1 zwYB_KAMe1>E2Na-ap-L5(d^-V`6l-bVtR#cbLTsaB$e8S<~c+3a+aI10#YAjL|kDn z==3j&SBMF)?KbT_-4m@hiStPgffA@`51t?UY%Rs482KZ9B9HE%(<;;W1|^9O@VWI&N;dU3;>qf3BX3 z<)uwFa;w#mad#VVz(h-A{mOc6;M;PI2hkLZK2=YrtWHF-vcCunn0vTPdHstGmI7Z? zQsXIDc$jrNxH3b9cc5D&-D}0-V*a3YXpsThLG0Fm(Q*2_Z{x?tb5qHW$w3GcmZ_@y zS7~zt5fw^A@!}-(L!Pf`ZGM%ku;%oot~vy(D)v!QaYe6mM1zFxdr@A-avy4Q0N>bb zf4}R}a!2n()wR2;BuiYf?1gDrvrL;>SeTuccMxZf7I`Zz z(kG1k$Ez#^`!wC(2}(0>3L3C39;vgfj9%K!CtG&!>ZDI&X~}>EXUT|p(;z5ttfW%C zq~#v;e!5x5vKB!cKB+<-7?`U34v8;sqGf74^015{`APP{I?YlR^851`h0BBv9_*u* zoR>BjF^ePaVoMlm-FNf{QKTQC|3!<*v;B{AHa_J<;UqD_5&ZP0I_R<7e*vOjD5=WZ z;UYZtgHj7HWayboYU`(~8pl*eZ#s`cFVz^b_Vq51U5py4n_`ZmapJvvO+!oWRcR>Y;IYr)q^m5Z{9dj{p2NzX(#2#rVxhqRtiO+RX zL0=T!M|k}d8TbxY`xicwHfskV{X}k|>b{{xD^#SkuEj@3tSOGAV(*Q?zFo_sZSmZQ z!q;9Zl!Xg}l;aKk|KT#2EX&xuUmK{|iFvi6HlU{{K>pQn+ioDY5e^XK{4-TCl`NMe z_2L4!kA($^ZeO+td`sGwwwHw+M&=0Qo4!_3#MdmC0uKjVg}sX+ZmI5xP27?`iJRJw zy7le$^Li*Ql$-Li#p0Kt1?QaEtW)AF!&N(XkREOR_oA!zN2j7eyG9@fNhF;rlcXV)N}RnwGX&ep+Of?p>0WH{bBS(*ErBLY zlc#kFNSHhwbfg1qHAm7!cpOpzsNJ3=gSwN^KQ%0a2;`9(OqenwS zyfQE7ddfqR+=na(8gs5y!Jlxm6CBsS-XWROza^$V3R1j%`D?S;`QS(2)j@BDS1s?k zpYXB_XFPgx>i+rpj?{~%MEv-_Y_mPNlgNLC;i{r&==r!C&F5}iesNHbh5qJjk5CV! zQA3(Od36{gn~|KHtQ_t9k?2qRbpM|PK&?jtGu8rkf9N;Ccja*7vRiBObUqNd&&`#_ zdRcLUfyBU?W@a2t7CeU=lBMlH8h><`CLi@|=R3HEto&Psu2t45xvfagRGsWh%@ov- z(?g_;wme&xj=d+r?sdi&Zqi{e=gs2L-9CWQzCC)(dt$SFP(H(=!aZ9@6XLJaI`aLyFSv(AFiY4FHM)=hS-fzC-wwl3e@@nurnChBl z9dEP4pxlC;4b=g7GDL(rGQ!ul@1$tT?!T?e+(_RhKp?DIKxW=)rkOXH#uhziSy%yG zN_>5K>~Xxiu4v&AT2#~`lzjzG%>N4uHCS+VIKr-TkHSNvxH#eFRwv*J$S+y30`-vu zLrck=VqbS`xPjlXbjmgl#NeANk^8V>7Owy9JE~|2uh?C#2uDk(4ZJ!9hou*pKq4#@ z4$$YG_S5)a4d;9$9Tr5d~`mm#s6M5?v6Xer*ihRh^h=S2&hf=p$DY{1X6`P&k77R z4r@6N>6V9F3VY6!PtGj+nOps3O3qGIO^|zaBs23ZW>QIo7omm-t8^(3sXXpp4^0%E zw>lXK&}Rpfri=%^i+++P04J+I;0S{^@y^*rg`ujM852Qzr6*|aW=bH{QQh*Kk5?8zr8y^@d_xrEjXP7T{NJ zf^)lx(ENr3ai85JJm1-z1um3JRPIqBhrjmtkag4C2{hJFmDt6E0R-s5oL`?zF*wsh z5fC_!!*f_I)bOJ+&fb~jy^j(&yP@KrH~%88)gm!y$IOnXoyq6>d&NnKk}o4_a8!_+ zj~}D>5vZ6(82J3Phh5ZtjqsRCCdS%(-sH=s`H%tFlMMX+a8IuI=Ibl)pZvAV>64-2 zpdSuxHbpAW`A#DwjU(r~|2CtN@!j9PW2neLktCUpgAWS~J2&g?XjuBV1=hFN+Jz}# z^7WP2`!yb696;HVm)Xn2Bzg0n>*mxy=v-R3Nu_ALP>K<-eZD^9*VD+ylVk_jCiDoE zeusw@N{Y-(Lzawl3j6l!Aa0Fu1|=rVHREgjB|mm(VI$dJ+$}n5Lp;?TXA}nOqM-m_ zB7^q&f;GSJF~|koK(xyrNf%|?q8bvwirP5+nDkg%kc#^ccYKLWuEOt+=@HELTJb^@ zQ?W9N=b;B43lzr{!~tuZt&sCsD5eJpm^{(G zee+>Av98dL0XDSN^aI+vhYEaX02NLGEf$9?29Xf8{=Cg6{!%nMU0CvYT+?U zS?4>Rh~jO_Wiip<=qNU_nZUGBQKy5Dz2r!-)U z59;qG`z(_)>PURC1y*ed7pKQ}m z;5cCiKSwvB0x)g}C9e#A!nH-4axD-r(g^vJYu#h4{pPzOR5VgKc;@)5GBM|JA{!Zg zoB^NonyLh9szflGCEF({d(@d|NC5md@o8|sEDJJlOL3@0yMrJBEI28owatT7h0@O@ zv#C8=Kt;_MhsElC_N|Rq|I#+C5MI`YKtXLzJ1`&8cc5YEScm5!tk%zS)f-=awSfiq z5H-x+M1`3{sNrxUQ>%7n*|nE5TbKpG;LT%~LN7bFg9}F424PQT7M3Lm$#?4=s1*)eq=ES$ z2Y6P(?yQ?{^7XXwS(+;I@E^N@qp!mcX^_n7@wAWD>Gj%F$53Vo`(n$GD2_V5EsFN) zS+)8kW4zsj_8yj;Y@}S7@i|!6S-a;E2kci>$w8VcxD>cJM05{M%8bZOhHU$mkhWZQ z$vk(41-tKZ@zC5C0f!K&*&H{bam(I_jQx9`r0x_8Alf z1kzq^#?(*X+See>AGsGnoIE*xm<*Z*!?1H;UQ-s}DTnlfpBS^_Dqf_$d<@J<3Ix zh8m^$X=UYt3LgalUw9St8wG6QbYNUiQuoc{YCV@84vV%Vyo$O>`R2uxC*QE|t4fCf z<&#dDep3IroS7ifP_1~w5w!@{1!Y>-tOd!}+h(f!f(#o(R2Uu)|MY-0k2JgiC!R6c zP5>WwCN<*Ypjqhj*W03hgD(yn;klkaOrX&aC{+)kyKpgZ_k16Aoh^|94!d^U%Vb)y zrbcH)hBBG^2XhEloaBGdVi7hD@F+AY37Jw`1<+gGAS2~ksU3pEQ$Ilk#(~3~U9A25 z34PgxS%O$GCb)Dr>YQo5{M)aNV3=l4%oNL2(H;Uo%T&myu2?x|iOYi7%k#e^^YN4a zCUj-%dxSm9xQZmbsY3$7${@zPh6MGneQk{;S^$wuk)rJrqNA9F5{?AHFsT7XEpwQYlSz-84s(iXr7_AUQ5z4a3F?{O7*N8T+f5qnrwoPAMrj#;2CNE9hM};C< zV^xO!Wr(CU(GpG+`HwEYvG@)=xKQ@D?7zILpKf(up-2&wudq9hys!utp7WzuPF3c= zTM~}RP!MSY~P0F`zKpXgv&*g48%e-ZJ|51h9!bVMF4qF0lhCCu)#Lv!M zXf2+f=1qYrLXj0O9e2qwkbZ+(=uIP9EH)!4PHlO(YQcOjPDRx~Bu6+P3{QK=Dl+Xy zI_^5hR+BrNqVdgcd199J@unsM-~h#;1Pt=zZ`s=;2f#*CoO-!;Sy2i?6=5*@ zPkU1DgW-=J(D)LNDL)i~=pt!JDRS(f$)dkStuG0ZUjIDNT1!ZV@{z$`!;IE7DQ-)G zPY7L1mJ3sya?0i}y?jMv_uhp%zz34X?ug#_w*xzADzU?JFziLGQb4QMIcJTR(~SJj_;bLWNIfPX;UwSJv3Cu)+fAg_x}@W7jultd+` zv*tyCRB%<=kAQ__pLTXWEw{Aj`~9(?-^u z@P78|>_#XjDHa3ST%Lp~u=Rdeei!^o#Us;+V`<7W2g%_^gtvTQD@vWNU>W4Ern*7p z_Bc{j!gfMi0a!G-^K3lLQxuZjKF3RBnZUC4D@&%njJde6f~%Lf^Wx4O*LY}y1Z*5( zX1qppyrQG)h;B6XJ(bG{7Hs-8385G!{2*h- zbFWOitIlO;atHud#J$u9cXkHZG5@4jxMzH{Z8a!$Hxpy6&61kCHm(G59~PRbF*tr- zy}Th(nn*GbN&c`KTOOsPf}(<@EOOF0iZKfU^sW=1oCM@`e|@b$2)%2*1Wsacc`PIo z#pnn~ji_>hK69bcI)Xyo4Inok!Wh#F7BGC?6SE6yA%*XQ{PyO}TQ92iH$l;4K1)!e z4^IE+un78i&5i@-I@>{f3OOt6l58hApS8Wfaf@v7x13avNS zKiA%L4ncIqk|uzt?lz7~K|-j$POD4JHN21uxu?>}jTaDeWSd(nD|{nFA%pLex1u4I zC-ZIK(88F}Bl*LCi48gMX1jNND+RI1IDd%(aW}4{Jxr+s$*}2L03Z7Wn`;|kWkv^G){6T{RV-@=X?|D zjOlsmp`S_;{?k9hf`O${yRE}DzZp_{%Bo{Mo-!c#`Y8 zS6X^U>z?uqA32bE=fB)*{+N2T;*%ELq-e{^M^(G9;?S;s*OwYg`rzPe8Gq?~HA!*P ztuN&lBE3mzm3EJ{CHh{Scg$+wz;_dP-@ziaq%G9E-%KQe1*pKv2W7i`}CMbX} z6P5C{)Po+)D&Bbfi){ZwzN{$tPO7e69;brEarp^(wT)N4L1vg1kp$iLFgP)airhQj zm(&K0n}!bwKEal|9}cnhKd2=??=7^La`yHi+dM#-Slm}M<9loLSj{fQ9ly9VWTl?8 zD|T-opBX>gQ>q+G@QHvg0-vAQv%}P!{+lTWuW0-O*WDW`d8A@>0-IuFYA@Q303V4# z1ffiF%a1R6AmKk`y0J0+fRp`}q&G2&Mgi5g=E;XveHlfZ--Cgdd^sI{rOK;L(zY|a8!p&VUi}W2c(Zbj`{fh*O#GQii ztd5+b{?)2usmB*O5il1={w;=9(qnqzMVaK{*(|~Ec@x)P~g}xJCUdEhd(n<0aGfSUcS>~)ERE51q-+su&@+_;KkR2`sAMS?rWst zz=gyqUm7FsP2z#jE=>}12MmsOw&6Oe6ePjFk!Piy;j6oZ%L~%$S`X2a=7BbJ^QI%t53os$_tWpUiU*w|&y5ZpGwq|MT$KGAdQ)$%PeI!UtsA$wTis zWY`d+iW?X1#(BjM2tn|u?36jhBUhle+?&*u#d#!#DMPF%A&8jQ|%BCj!pYJuOh}keNX79@eg`&CrxDB5{GuTo46YQ(3>CKa;fmxqTZB-zGI(LVH7IA{$1eE*)La z-vW~joSw9h+l|}aEJcoc(T3OC-o!xf?3yKEhlvf3?gZTC7)owcA5&hlQnzh(Ts~$r zCA%nHKO^}y>2E@{_k$FlwmIBdbONv=01VC?f`^1YncW^E>^l$PW0&5d#Mmyi=wG8#T1IU!11gq;*` zBf+DT8qXQeFQ+*_8?7B3k74R-hIk0$UK@U}X?Xe85(Z+J=|+MK;ZQg7C};Z{>%u*& zl>S~%fV}QpZL_%B%nLrO3*Q~hc5)B0m-pO%T6jQFN#$P-NdauAR^E)F&ILy#+YyT7 z2@(Ze&-<^{g|Yy$?+W|JLGgCEI4;yq*$lLbZyY6cA^^x2wY^acDS?M4FrOQDwMkTk z5QmS-#vO{d9_Sr8+m!nHqQky^BRgS`$_|LSR6$y(Q38?LT?=g;!h1SLiKa$D806T! zDLu%I9o`2|i?qGX>yI#q#dPEZ(+-9fgF7#-+=uBy`7TzJN?#ablbSRQ6nduq@ueEk z;kWL$Q!I}uS2cS1oRZoR#k9BE2d#AGoW2Dq&eHZBDN&>)g(H}QV=Rrevk}M@{X?;M z&xs6`yk1}Ec6c40{^Qj3Wz0Uj01(*4=#!9v1gje?_A=z~UZzdxr2y@AHACaqD|wuy zB@nj?W!Vccr2vW0y5Lu*>Y(9p{ys~uu|BpVLiDK#GjJEZsGS7;h@xbULB0rZEixi8 zo7!&uu!Yq2b|Oat3ZSyz=qribfD9W1er+8yr+jx>$ZZlkUE@Qz+|b!U0b#67cMWL6 zvS0N(un}RowCmn57LOH@-P_gSVsB>N7msv2yn2q14KYt7uWet2uGSrZxY%otg0Rt@!18$bpa;PEPWcZh1suMSMlZ z@$l6z!G*`I3Mr4a7{t+kEEN;i%1~!@T~3v`}#wbNz}R#x-37m~Vs z)&5qKa}YCwnOpZ@_}f~hE|I#lraOhx#w>Gh?K<@!Ywr`?WQgXN;FG8NZ-0Av=X3D{56yQ0ij2B>QK5*B z(W5ty&hUqll|*Z+CbGkA><6Al2&T@Ze$XkOQ5b!~t699YpqAmfM@PoolN-I|Fdmqn zFfR=zbIF{)J@8K2p(Z^Jz7n ztV7Euy=szS`2)#Tb1~+jHI^yO$Y{FFMKi~cyx#U+)+sSMWYP>6~vsJ-4Tqt@8T$_KH&7#A(-Ec z$|u#veBm?>04r}(N(3ItsO3XJgfd)e|;C%+Evk^=k-z*AKq+lp8ZzlqQ^}+l00t6 z`t?+e)EfXU#?)z1ynz>kT?3CAjWXcwEc1M`~kmd2JR|dep>~^orXi zKhNA3tvCTfH!sQU)*OK;u?K0b?SegJ#SxFnGB)Z>r^$pb(%gANN)V4tn4(Y@BnrZp zSo^m>#Yi)}^@AUSt*$RrVmWH9xRejbo&C(BnVN=fkD2GMwp(r_@VfMi9smvO$h$8; zTUafd{LH5B_q_`&)Z`6|)`0_0Qh?{c{fIE?KJvQws#_j+D;%Eru zh524TJSsdy$P}rLIa1H@jpM)DUOv7S=0fhc3>0;}TCO>Smp7WAUa8*s;kD`V?ONIU zBrc|uwJ@me!S%-UH#JWozg50IOiQ1PRJKzIxax{Y4t0qzayr8^@G3H_9{no1MI_;` zLhgE3Mm6YjRn;Wtd4-89`8{CwhXI~OI|Dq`i`MrO0#gobj_Hr?*4Zi}Xr*_>jRB;h z&lF>(-1h8R3%R_0m+U}u;>WziYMVTPPzI4*W^39q;AHWfr4A&m+V*KQMAF##U1!vY zHH3J&KfG;5lZe-KnDSCrxADgrYfm8t$_IeLn%pmYEK0id&u!hxgf{=DC99|#O=*t1|d>{LUcy!@FFOrO9vO!a~2@;SBd?#YtGnq)% zWM*BncgfAzSFDgclNyfpUgWRoesJ=|t*s8bNxQ3P7^2;WGpcRi&US8#EGn)JWITM^ z;mpa11r%(5_e8BdC1IL(r$10{jCOeno7qi1;V$2oh|of8+k1!Te`ff2!Y~V+8i-;) zza|3TeLph>P{$IWr&xE*Lv{yLhGQUCe*twbt48JA;W}ccBCWu)t&|>wNbJA>wXG2H zaN8TGTsMML{HO?MBFhEXl>|&dErjeJPu~Tn(%O3uj2i!P^4=`HoBfKm!NejRcEMEh4_uh9x$-L7@Np`z9=31Mkqe7tpnf`R)+zeMtZmAa?{L5S`zYweLN<{ao><>J}3gG9OEWQ2tM- z-12O`his=*EehbTENkt0fe_sIKPJ?uDRiVSk$(WAr|=$- zo5#H{Y$m>~C?&0UsrD_1vgPj>Ibwu^W9r z9}pEO`r^$JWvXMCKIAjcAQslQaIb!I)$WpWv-!oAKw?w=RqXR(h!d--BCWmC zf7cE($-#!s$k;-d<=}Qwv$n7#`P|chzTU)OD>ZF*ZI6vt%-X)Rg(pi z#TACi3b_&anbp^Aj&hhB$|KU=rg*4kAo1ebW$7^n`a||7`Re6WYoTwv)~zc| zh4VjG)b>wujfcO2HiroAZ3C#{gJQM)Gh!?sg6U<&-#M|&i>7>=`+GJYwIB5CP5A|X zpC=2$&cVDHUzVa{JY##ukv{aQynnh={ndC%+Qu5mgIofQEh-p^p9jq`8qZ$*AsQwi zT9}AWk>C3Ea3(wCwon9R&(~!Uz}vJ~J%$d~&sln(wJ?p$i%Wvuk>HvLd1(TW6k@qT z%YQplHoGPVJuhA{EUW&0=(ymV^$+eNO=w}gnH}~VjCi~!6T2%C!rykY`n5R9Is;32 z`ysEbvT#A2r9ZuAKI8SC3z>WtdmjaTRd`{%JxXNx$!5fV$>jR21|o1RMb|p_1CNfn z<7YP#Q-8w8EzvdWE{PPqN;E?8F&o~5{gbt)`lav+_YFVAwyzg)~2m?pE* za_eiUDGHhjBmy--g&pB)3ko0AN3)H-5?)`&pG#DCAz=QDp5PXw1+vKJ=ZYZK_l^6a zP1U``Abwtd`h+;DW!74qeeQ@c3RFX_jAu6=x)&0CbDL(sTzSpM*%m4R%gW9x- zqGCF9smB~A2xjXEi}0J7&{U(vY*#;TN~?C;hRAws*4N;d%xWGBmq@aLryqLfW16sJ zPb#Y)z0LfwiYcuPup*=X%ZM{({duVU_=!U;_{=C6cD~XNt;yb`B<QvHdO(8l zw)$6{2kxt6^S|PCP8ziw1!m7SX|GGT#- zwna1*w}(MWm~*tJe|b6CbF;%fN@;ZMYX0A~5B(C0>3Xx*aLA zMe)qf`l$;D5?k4g8BwF0{^IYe*V?6G7_9FOMpA!oGPc>xd(yHsy9(yiMk@N?)H6S{ z)?0w`Ll<=sSxh%=PEC1=Yu@grM zB*x51v|R*klx&Nv_j1l(SicIV{l!DcC~kAAKIBNIN39+pcF|%G4?n+ zym^*lh$`Yy&GxiP7%~5a>%6GvRWe>6Rkmb9?#Ni>50*nr*GG@H<1cv3D$8{Yge2gm zC2fy5-+Y3nXMLjqD8P5;i|Ca6piBC&y#mjJLeSdnum)$SZg^OO1-uVcs!134S!%is zX&r90{bagFl~dGopIADoceskcgy9RjIG%aV0(N-2PUvaL3P`(&2j{``;t1Kl<}Iuv*_ z1@ft>`^2(}6L+vBF_{h{%2x#LyvT`HCuhFhK8%ajbW zm&dau(5CzF9HI&Z27Xuhj61yLy)x?J0Crf{UT|Y{MqN|;=3@V(3by=MoXsD`@lhuRi&2r+iuk$% z+wvD}!g5tu+uB3zmlAB5rW$k#NZo1=(|=fnoQMp^3YSdL&>8IBDCnW%+w%;Mn_5Ia zj&M-~rpX^}1}afFIm>_t^W89k?lcnoW~XEq;s0OLWokzeC7HT_Bikj-%~dQr+CqESeD8Mjf~lg@K2v6D1>v~ zXD2EiG*;HIJ-zNW0AZaek$VhRrhQyV{_OYI_YgsI2CI?gPLK=AncFDdDG?6&x6N5! zQ+Ec#da7Q}bw}!6C~<5i@Qd2x6^|!uL$w|QaC{Fh58r% z;gl<2s@e|l+BqZEb4O+i8#Q`hg>aBb2p7TMlUqMv*jyjVRNvstDkx4br<8?gYQ*J+ zT;uk;=+#8?XR~wu`Iya-A(X-9mBau08oCwRp&EDE=&dN?p4sq-JOY(Xir{K6V>E?y~i?^Fms6At91(Y`|spr$Sb)2@UK(gR#h`!Jp)8Hl>gBm&IQV z9o)Bm6Pb>5ey+*@!LGz_Sz+wKQ*PmeBf}+PD7s7gr%h zI6ft(=hgF?a1jTy*fF4u>|=X4T7CxOy(S$_L&mwuRHCt{$iklTrWj+@jF{Gx`RXX$ zuQ24EdvL4zvkqUkcc8;pg1gc$t6!W`apZ9$w7CNwn{4nsfWZ8!nBFU{urtWT@3MTl z<&~Iu(-3h|zRzf<97NA3Y>8}f_%XI{=v=}@{;xZ4riB1~KZ95Vrmy~z4}%oHAMKQ+ zMrx0zzNB#Qe7UWtnF$d+r4(Ib6DYg+AG}OL@n}tj=dO*)Y!`o~`yF?~SC~#cGGvgX z19V$%uEQTv#j+Z%rc~)qL%rPR`sboayg~{mKV+4Y^Z>jq5gu ztu_bw_^A$_6O1&i2|xGKi`3{8?0zeyA?V1WuDZee)Vo^-f(iUylqu2NM~w!nxw9`+ z+JMj0b97Y?UedDi+7D}`kk`XyyL*DHI&F0*18g;~)`c>37W7|oM38T!zvJWyZ(^_e z^n^a%wV6|J9Q79&_fvVLXl89@LD8H2lGc>(kzbXzEqSe}c7k9XbjGj` zW(|>^K?ics_n2wZFdv-@w@IWavDoiH=x?|5fAtny^G~&;#C@o{@qaX(hd-77|Nq|- zN=8Cf98yWrNp?6V2`waL#!(cqGvjcwvT0aFjuDbQBJ-H%Q1%{i>^;sg&pFPyuJ84^ z-G0A+;GElaJLh_h=kxx!Kew-6+dvB6L%*2XmdNOy^S2|VP`jnNzRjHKK$z)5i^^Hj zX6(GRU8zY!twUCIzygM~``C&;?)Hs2*aw%^C6D<+$oY1nK?eKI+eCvYHYH{rI_)8@ zzcHN>gNbhLn{YOvoV1u(vR=_A*SRBfZgsFUl91Pjs?vU1$;EiSUq{ME;`f&+G{Sve zR8@{29r;jTd=cg9C?_bx!iDI>McuY2^CWjRdXfpnKpRa#ucb79HW35MN1{XzWRM@$@! zuR8!7MlfyCs#jSnTaY`^wOjHgiujdXqV(Z9*HO9dxDTCI#=MR>nPKTOL$o@5ob}wT z*Ytbn;qZV>`Pu*}%&Buz`^$)OHB*CI6(x&q%tHT-7?O!YVDgGr!v6~XvHR;dz;OyZ zL;^$>aNO=K?VQ}wJY#aoQATw-o1XgF1NBzYHx5a6S+)^>Z)r2vHRyugEegUt}~n})63HZ*@9uh`-5t!xsJSpMVBB@dTTJ$;;7e*+walg{DAf>)cSAqhDbq!!wLgT ztpkiU!&bNLKgT<$|G5^M(97-H#Qg7VXEbWy+wPi)1LLFg!yoKMo404Qvr+FnB~mZ> zALzIBP7NM3$*r|R(j z?-;PB5gflwKRkvaUHhJpq=yDyO0nIfHqV*A0W7vKKW-`YwDwD(oes0hk7H&m+p1O1 zb5kyN3)BtQDs`|@NcYfdARlfkTCt8o^@z5s_X{)M~Sus zO}o9L`LlFg*rgsCPMbchL{i?vksQJJZEWThz@iOC#ideuBKxL~?v5$&I0J_^uQz^z z;ez0T(H^_LM%8}SDdZJ4@-P(yp89*=NOz-0K34WSX5J9|S?E|~t@G&To($Q# z)xw_i8z*BydJKuZWT**&?es@r!zKd*e#^smLHux^}f=ycq$ieiqa!qE6k4v*o<=RMp39 zK7=AmdQu5K+6p~ngrE(Qja`bnV{H-!bTvk=jn}FcE!q?|FvSMO!ZNGU-+7m0o3$pd zxi6t#917&PMdDG{-@4b$iI<-(HTYB6j=yNbm(PzI^?AI4ee)+D_**{r@?Y)eyv8xl zwzl3&ZA*_|PgRphixy7jUzA<5y=;1hLUZ{E&n&BzYpkZ!UcKmI=FLK^9XT@SI|njZ z@trq)*$;k2$#-rw_#G2g&PWWfh{Mn@|J(^{f3tW0&7ZUvSo6y4K~mS%9T>gptvEiD zmpVQg57W@i0}E%QTK z>bsPqcJK(L*Drn_u2H(9rQSu5u+;$s8mV|-l5@}EI`_C`n}GMM*(3UJwcN-V?{PoQ z2ltSmBe@mebEdQDnupf#G>$cZ)^PzR8+wrA<0VL^!4J#KC)mntSq&-vnL9pTz$H#} z_N7Ld^Zhew8~#zQHgX89n=}5Po`nUd$8K}f=iH7#zqw-jK{N#MowL#YS}b^$uiy8l zGb?+cQFzSTMcin#B}W6PsFkfXSLobz(wkojyVz1O$OLxp-A$jWNZ0H#yKo9ctm@3q z2$O9#nupsOCVtyGLiQT(j2PV~-g>r}UQ}}zbUr#4m=bi!L2!v!E0Lt`?DItKM9(cb zL;1X$`+R|qmGH@2TITw-L#v0xAyktGrEf!bE<2ZAo+kL7uB$8*DjcE@A0_3B`O949Ee1FzJ`JB&m3k1l1#@)OYsQLo7 z5l>#+cX9Gzj0;UxchV}B6(RA7L!w8^TjYvzorg|%^Ckjuio&B3`?orb!N$)M^byQ0 z!+pjEg?!!I#3_v&6txp{)C89~oy>m&1TIEMuaZr=+OSEuHj5>=RE%8v9}xe=#O?gE z@&}}DCT{Yr{T@=$HRr0>=S~w{D>F#n=Mt^YWhs)P`7(2vJ;I=c8C%&czh5?J3iJ7( z!VIY;(|xV8`X0*X4~D6H`t5)e>#U91t4CKZdGh3a2xoh{VM=v*;VE)LrCsy;dGka` zjk?5Zt=w(pgGv%VAc{g+iR4(tPLxQ>{Ozhcb@uU}Fwm3q4)sIlAtl?wCAikKWX5S1 zE+iUPw1km4WnR*T*E|U;U5%)ib~%9RTXkE`@rsUOXSIv&m%sS~WQFVe9$uG4&ol)y z6}E2Ynz$(`XNdl$werB+Lm}R!z+?ph=sq+2B?R=+H;n3BfhE^6Y4wjA(zViS@6s1E zr9l{t6z+v2F_ZU759zx|AA`W1&vkj=zg{o9^tZWGEBcED{oOQ^dnCIi*UKvRus4mPBEs4V1Q3edigF7?FAVXd*=G(_3bC zu}~tBAHv%CeJD{|J{S|_b3dQ?Z~mI{QKvNf4cloHtXm(}Ljtm`IG_4#LJiaA@N)g1 z-R)oEuiKF$q2uq)#K+YsfS1#ji01Ut^Bi;b*|aMn(NqDI)nqj}hyH{!Oz{DUTtNE= za&Y-8utKAGJau%;p1C>_7K92Rn%0V8DUP{=onr0{ziS}1FPPNG=Bj*F{jHZcTWlHP`KCJI*6aF1ynPuBHoh!U${Ee=dQZIOO{c_J zV`2E{k*a8%@9P*u&dOjd3Bu}pKgcvF8q;H**5RXc^c?n^w7$$c6M|(ZQADG}a@nGZ zizsxTLkKY`%h2flHNSaD8;FRL(>=I9zG0pH9d7{~%a>d8uFo;46UA2L(FEb?!Uch= zHma35n*y!{;qJ5*h*`JV{JDOqe69M%%t-fhk9k?wU$c37(q=S8D@9sVvNw51Wq+3T zwqL`OS$L9|1JMg8Qe5!&`>GZq?k=CjR&dt+)p2EVp^sSpL9g*+gIj|=GjOH54-g40 zXW4m~$|J(QJ#pc2IYxy^Fi+aL~CTQ%qw0*_-Xn}KxDTd$9(iwRu3!G)f-eaD_c|>%O3oo8QAaFw9Lqi zX`|N%pYu0{JA2l=OStb!zOENrCqjk2eno!yX?Ng?q`eHe=(PEo-(?Mi%mMe`OCZ=!+Txo{??N>-e4zB8e!P51z19Vt2c z@_N=uB5jv@V-d-|_gOBA%a8dwsc5M3fPPf$(;%WbWuGaP5riXC1rAs-v-A@3yZH5Z z>rq47Zx)cdDEQ+Zjz)@5_h87Ufu|o}yB?r8>%KLeH+)rsu}xcM;+Np7M_g+jO__R_ zE;|1pzWdwE;fRg32ZQaZO~>!66!`1Q58vAw4X-QYN8$*q64mUsoCwu*hRJOPZwA{_ zrs8>yPManUEb@GLG$l(-2)gz#g11Xs57t=~7{T$uAU~&CgapU$K@%@&wsD#ib(v~l zsRd-S2AsL?6%MZt{>;Q2R{}l|UsNK%W)6s!${FAGWL0>f1wlsTp6w3u;Auwk44aYb z;Z1sYz_O3G0S(wzJ{xY*u#-6T*Xm$k8l0TkItYAvfynz9ASsrt25#fP!>x~g!sCbN zlJu3FHy<12=}fD?myAX`x_p1xHl*)DS#^)YLOYGW2Wdr883EKy7rQSo>POfXu!#bm z_D?9kJ@yr%(vn=P{!(yTUeFzC`rW|~UBrFmi~#iBm4^*mdHxf?3v-HKD0i-3T_jb8eiX&RIe1sPlXa7@_p*52Uu zI73-OHO|-2YUh)HPs!V1)sf}OuNxRSu*{+v_Zw&dfmX4HSJEMj;z2QHOdGa?qrGVR z&VnXKAq%I35Iy!?P5+KPi!L<{P9Xh0Ft97p&VN-H??a)V;K%4dPMv%V+v5<{_MxPhT z#O_j7mtTV+C(|XsBx*^j*O!>9MbqsJc*;yB$K!sim`|`PhjaSiI=00sjN8DrDGd}( z#AyNDM>wHC2|bN?M0PYZloA14;CssdHgjq2Dqo_X8YKtzhyY7?a{uYe5|ZOqE~WlF zP*KZNWW__AlnA;BhHi9Gef$8elneI7;73aaRf~h^ohNEymv{noZdf3NsyoMv7QE55 z$6C^JNtVu}H~sUzS{OhOl0tB^{ew%5QgBYt7}A)LcS>SRx!qvIgQci2Px~d< z>W%KrKbW$8EKV>5O7&?l6%ncj+P9IWMh83rs=cLL#%_xpS42i;Pf9@{_x)ZBfr@=H ze~$)n18c1E$1IUQ94EbO#uAhkKbykY;5oJvft|xDjI5`1Yjc^`CZ2Vx+IVFF3r6t`^PHSacxti#YH5RGgK$e8T?gtHaH?5uN?GZ{%TBW zuVvy4BUdH{)*Ms<$sTi=zTfO#8!w&S`c&u!;c$s2&v@iB<-1A)LQCNjp|1B{e`b@- z%-dtF`qeR3J9EmSkLi#@$JOm?5MxPS6w6KV{r+DJJsI`8vtFlj>^k?`oLRxOg;}+~ z?HF>qpL3H~!e@Q;d*-9_C0{|Cz4n1gD2#hF4F*oN!xs736!R7xtto@*J?r43GzFgQ z6G?b}xn;{3e&BglJC|tLPt=;uRepGd#~3rZn_Ky}>;$_8!y#@LE@S)QSNvQFx7U7`(_TqiHPb18o5CNz z*F;llCfq$HACF0K+Ce8w@ztWkomg4<8t=wQ|(7hjZ@_Qo~KD;pcZ(OW(y`zS*@)jerSV zKS0(OIn7^yjVo+pU`T@Wi{A!R0nB>6AZT8|k8!)q0FwK!`L$yooV}WT%j5IH6Un&K zPwT;JbCOfrn52`TEkY!tUrg_*Fw|slHkP0AJaO!k>jTQ0AAu5ljhW6Qn6h$42#+$W@HFx z=zM#?4t@HOm%Q-C(tw-+EUl9pLP`f|v; zc6aZ6t4|x3M5A4#@0{}TSBVjs`R#~5Q1Sv(N0C{=-VrxY`U+|^p}KI_y^pB6TldYQ zbg3n(rd0re3P&#`H?4GlPWfe>w7*k-u!M5*bPR`xNI54v9!F%Ja8q!*d?Oe8;k4hC}0e zkSh;#%NC}n?D|mJJ2*w?##xQPiEOWT=RiZ=i}q!`{5hn2?TUDQZQt8?%zv*wB2LJV zzF;gt>Gzo>PuQAHz+hahpdN`=t?ScPr4c)0bgSxY#~-A~Mu;BMm`#IOW_AOxUV^j{ z&=ee^Jd_~8R|SIAP6j68~HrIwP(ambQUe;R#PSzxV$h0vO`!d)Bjf` z*y94Ow!&}j-;=@)LFpdS)@_$t%sLnM77U?b-tixEMscrt=@lGb&BGk9l&-MVxiJ*9 zG43$eN^i)W&?AutZR4-DEC2lfdW?ccQYAARp3LTLjumv&DtH|1NS*33pwJ9_erpMB zqq5S$YRqInf04^#YQyi3QMb`B@HfFreR!jwajl@1NZ%wHr$y?I29TIxD*&wotgRdT zwlx&cJ+<{pID8qgtU8rCP0{&v;AMwf*(k;_#hSPQ>;9rtwl3&svHhzfc@!!EXh0db z0Q#G$ci#900juUnp*C=_HmEp&Wmy-xo`@64AHt!4GSaqgIt40^Z&nW0+CggW6#Oiw zt8ai$XM3`@oyJPx)&tz$1Am$aw7I8F@8P$m4-pkdHA|h25OnjZIRv_M*M8|PnV~1z zb*2YI^}BkZ7{la*iV#2AII@2C+f-h0P3}=`2}s-e0SRN|taj!E*7?(x7l6F+BrV&M zU?j<^3nO02mEA@&sEolSyTrqzUyL2=@5=s(04Mi}gI{_l!1LfT*}@`A^h9eQ4>Z(U zKwNjHtWo`SAWu(9%@3;^jgpJ$-SBoc!38p`e_WN)en#S zGfl;L(8p@(B`1w1|2=%HrmNCI#iW2jXlHiDGTbrk5Ee0ew7gG=Te~)ABi!h`2D;1> z`vBLd-t7gXk#fdMdZachd>7Y)DQnCd1(z;9<=ZwtHDltk$RBjsP>DuoDo7O9z;icb z5}i`~C7a-%eYSVQ75*q6V0!O`eb0~m0@M{TlQSPxA0)z!;nc73O{L)c!1J`)U!3H@27hXYjs%DnU30;@uCl{3N=Xmd7jF!Gy($9&57i{p|VDEZwIcc188 zzvws_VtYk|C&}}^CKJC!e;y;L#906}tZ1z0nPz_o4JuRFTr;s$-;+7fZlXR6w|eas zHNB&m_mD8_;9%6aYPz52wHh=HB*^|-(8*7oiVAnP-+Oy(V*C)2pF<(Eoa7&W-G0NO zt(^R1r37pl-YXTcV>5r{4K~a^g?&$s9Zi1zjwIOr7y1|Oozg3vJEhwxq~ykSLFrFv zI1l@1FMe%Rj%y1sdjtQ$!2A6B`fl@J8143y%lstYq;vO;s0lfbQ($!*EK9;P?~MKA zE>&KE;5j1nNb^kZSGY~gp8EfvyKFXM0l+9##kbz z=cL?7@+ERR@>y)@LI+40_%h?CP^tRx^uN+`MH(ZSz#Vm9|rDZvMvgW6OyG6+LdlTk#(~ElLhjL6vK=3&Ub4Yf6JBFBt#=Nx? zn_Dt20^xS+^YLPDS0pd(searws1&-W!z4fsPdFYMtrQ9JVbwbn^aDOs&&kdmT|#9` z1-|TcOLDYG#5DhlR^a?9n6;4<+*K%=yfA8O?`Sqr1sTVF8jMrPW@fW!tzAf ztQh-?n=?25vbQqxS(&HD8~-f_hEAli282yKj&Xf!`?070`Vd3q`{GJBdZ*SHN9rFg z0eKM9Ejbgfo21_ft{#H73uo7)kb!SOT@)>zr8J(j%9MR3(3KOWCi$K}`FaPX5%@9} z&e8z3h|c=!FELzYE1Nsc298izVcmaX8M z-@JgU2-1enw^R{%#+1M-x@RD|2s*+47I+pn+a%2%pNnxu5fK-X4KE3Cwq`fqdm;zfCoTQTt3 z!MC~PmY-c_Fi<2KRDMF|?xYQ}^K+f5^cJZrK83}v3ExrW*QmPf)L^N)CHEpBqwGS2zG>+>K26+7jQkuCvm_N^+ z3>3DT^aE#&?p0enG2Es3doq&cPUe?^k=v*3|5Xf8aL(u#AqWB{hKRyMaqztqL*my2 z0gIb=)5iuLJ^QHj29rt|3+-Xm)!5m(&e&Z}E%ysW`Zm4&(T>z6dbP`_%*U&Wa&{&i zk5vI{c{KCe2R+(p`SkFAy~xI_F7KLIMNJ_4FrotZ+8hVz95B;1Mebk0FkHj>HWS8= zc_OF)oLY}Rfq$ro#o2yx@K&ps^p9gz+5=N;;@1Nol!T}o%2^o~*KO5qg~5F_5Az_= znvU%&*g*Zr8~r$u-t?oi<<}9u5Vie7q|aqkooVxbO)&B@v;*dtU>^U!9`X`=(g*=I z9C$^S%&5;#A3`*Cfz(GPO*%>rTCH>uJBAJ#Dp0Msw+;fpYNiZz#fBSp(mZP6f20b> z-+gKI@Z`0u58jOd@YOAxy?AI}{ELIk@g_B8dSu*wk?SDiU|SvczdTz_hH5dJjSlpw z1^dS<$SLP=tPFS>Y58nQ^=Mw7bI*^&bedhg1 z>^|yc5CZ54?cd~!0j`V5$rz{j^f)MOwbf=#^G4d)^vO<(zkEF2lPI=3_l<-%alwFj|(G`K5Hgo_N3ht zl&X~p1dw!DuPV1MxF;W!rG^7O-}R?RU+BQRz~iCRx!z5%g5scNlRD46K0u6} zbNrLdrl-~_rEh=E?e@Aa7`7D6HmfV7ZW>=wlQ3p{h=b%RX7#|N7?5Afv>ANr+6Q9I zd-rqT$)Gd|tw>~;hajMNWo^Y=W;fn&Lk(!-?ap(Y_cFMpCG*(`3BlJxbds$C;baQ&l;EO!0K(J)+6yBK7A z^XD~lvI)*b3@TdZBfO4URdQUEAd8zh)mRszJJNM+#xFuNR@%?-?$e3qpWLa~svEbq zvB^BLv38EL+8FpQhYS8J(l?Xb2;AXrR#;JJFFd2`Uc-~By^$VsFd+R0pLAwA;I>xA z-XHPlP~3C+;Rb8v!AbkB9i=5G)*TOp-g|e^)7y=Qhp3brpzVKOc<%KdrYXHz`T*$K z1iSKZT`9gIH!M*?BTInRUan`6jeMO$pMk6&Ib3EX_I6lZJ=^1_73)W61ivhAa9-4^KYDS{5!&SoJcmkwmx zVQ3zyF142JLvAOBW!RA|Td}4XohTOgMfmzG41h#A_h)r_+UOw$>T`cXP6onsB`MaA zU&m?ivRoMnL58!VFpi1=@cMZn%BtnUr8roV?nVsx5BL-~R5mp2vAPLHZfY`qCF4v< z&#uu>l)Kc{1X4iNq9XSvvp6{74RiB?ht#}`DeE5gy?(36EL-aS)tLu!YkcRf{TO!_ z&+v}n#;(-G!Bh%J8m=!q%-*BszYuve(;jIu@#;PLf}xs{36>+R?_f?m?-#Vbvs0Ap z&w#?*r&IPmr&K1-ram}wok%&Gr{wqPTe|^w$45T)HWioPUhQ*PzBST;-z_Bat%9~0 zY$F!|_>4{G<^Q1K>g?o6>n9QSeRDXPl#H}L&z*3WPfttQNoz4?ZCdUWYsXw{eEz}6 z@Yip!rAgfTmr#_mgk5yE7ScIV;<$}v?kmR=A?mEThbYQb`F$9xsdDS~CXp#Hqw}#_ ze+5rq+og?%s(ksT1|@gdLr(j-_pbYsU_t~MJ`|o>Cch(>)?(8#5A7rzcIjEOV&Szy zI3M30OlN|U?O8gmnf&tel8pyQ#z#svv)|QUsiJRZ+o^R(obFSPLb{)SCpKw=W??p| z3fF(4aOi7aDV>@t}_gMIcMHDV4{ z`x2ZqNZiT04Gw;ndKuY=WRDVrE`?Xt6Za)*WvD_ z89XOz{#1G0@t2_(mvft9T=d9>4#k?4gHTEf%97S2zH@!crOEk_~Q2TKz{=nX$;O zZ2*Mmr%#!Aa_nMC)zDk~X-daAQs-M|zz<*rwVDZVcJ)ZzVhbd()eSaoRqi3(xK{GT zV6?vQ+U50oR@;GxNcaq9G6dI92U+qb$;W>&j^-V`DMa!?wGTXC^=~aREvGQdc4IgF zFm+0FKUColFP73U6^^7l|Hb8P7dWr%ZGfQ->S3~7%#ZXM#CUJ6Z}ZnmuZQyLuM{VX z#-rOLYYPzWPa&|I+u)koVM)&>G>ZzbxbsL5PHPZ2T79YRyio@`nwlaYyz@Ob|7U4A z@)nk3jE}b^CCF6qeAIMU30-sM2(;-Sc-yqm#+n>P+!^yxw%f0%KhfQh4Sj9w4-*qDgYcc72Mvk+qvGy?MpY`3PX&me(!bn#Yx~7b|C+Ff%e7p%?_di9IteF0red?!=GGIktz7gCoCa zp>3VX)YiAZoI#l(6@&-yH?+*}d_wG5?A!LUTo&IzE9@?$$TMuONyxUm@jOc{9KuqG zIP*yzK!mIP_{lNZIsd@H#?^gYS2!YyfJNruVnILj$x9KvU|cenDp(h9aAH8h+UX{HK*^laP?p6`mB!!byIQ^?NU4_#4JC>aEfytpNd&Er&8H za9@;4T40$$**82Ou&n+}C;+@<7olfSzp22aF?&aYNpDe<%-*wYw0o0r`r?P5OU#p=}vD*nbhr&ep8zHdWdJfzUoc?E}1J#S6ZKDulVa*uA`96~AOO3|ek|(zG zQq{#9b3=Z3`@ zu$#_JkGe}ihF~$dTu?8AEc^{qcrCa(k8(L#cI;FmR>o;k+?f;05ckaAq33~~IYve^ zdCUVIz}x8;PFzHT%0H=)NfKW<#QwbtpFM7~GR76SB6{R14rKWT$N#$K{faZt#%vvz zGX{3ntfW*BK_bj!FQu z8Eb4b6Mg=_ybSK3o-#Tr3|R**Q87_4#L5Zri~Ql>%R3z7lDqe!-``dW{TCM+y`S6< z*PW}^(${f$?}m?!OH5`;k&Ho{SSRr$PfxXR$I6{_u&8yh(E)4_jxL;yQl1Oo*cR!= zQqaalkK30Rc9)%1hbWA_Q0aN-AdM9bx^NOkIW>E*o6N9QS>|M{9S{QdZ{NWY-iI&* zK2FS^BBNqae_eTC96L8opR&6ZXWWm3lNId>k@w##r@6#oae(gGEi@o+j|e(q~2VZT{seX| zQ$=MzG}^~r6=}S&k7#}rHWhKfZievl)z)u76ccG(s?s&L{God$pKaFTo@U%SN_rVb za_%`!q)XCCZMg@3ZU9>%r8+GaMFmJXyYJBFZ!jZnZT*?*I!e!Y56>E%mArn99t?+g z3!X<;WR{Tbqdhpvz4!a(=Rt%lxkpY`_ zI+VIK@Cpk*+(jQ(g?YXBcht+-~0Y(|bV3jvRt79*XZ@%+;N{9*x!FM08)N ztVRQhfn%d<=UoMQaY20OIx-i<7!-xr9~xXk~uu z_9UI&>U2oOzH(**7e4Eg6&bz^n=~JeQGLTN-;z@COg9xgP?0kK!OX&6)sCQrNz^7k>9g533)j{Lw+iU+g#(ecXYYmKFRI+s>{elg3w z4sH$8xo3TLKJYf;kM(svR&24q8}vKte9<#?a9k`R;uhbF8*wDX_f>rb<~L{UX8ml{ zC`Gq5j~s60^IR@XYt=x}LcWO15W2N4pR8d4{TqUQaUuI9Sve8LHw5tR->O=T_98yL zlmSxs<+y>A=!hSep%9B$qc)wnCw{6)N~uvOV~$(UpYMkeT7*FForzwXbGu3=3M>f~ zFA&wxX)6^&a-wrYU1BB_!AJ=|aatA6_R%|C@;{!s5a76a^EM;i)cqV?*A<^T2Y25i zM1;$W>?L33Y}{>_i|1l6V`!C1jdynbK^?j|5nZ-auC+^)suP`;a%WE6g>gQtUObjM zcj!4eAJghPOBgImDai+~oBB|RGugd}%;UPs&DCAv{FG0}MJz6niCl;g>oGS5)AXbB zZ;BuVqcaxi8MZn5cJDZW|mlVTmCByT@O>+AwEf;$>>!n@i#k$r@f_+=(-4 zCO6kKThfEUeCLWO1h7np__onyW*ZM9iN zW|seFV&T#9c#FLGA%yi=TTd)MMQ}L!N7(?_#C=!PjMp`@!Z_oN2 zbR}5!t6X6e;*oXD^Ww2q0tN4o@DwoP+GIMt>pIn>&1#(*9u}0G@FC#xz>>MUOUVmR6F&1M$dZO+J4gzAtth2W?Q<9Ct z^_}=W15yHa$L|pZL%&j*FIz@pId?D2&w!87jvW2Yv(*P+kd0C!a0S5vsANHsO#}w~%mH8`Sy$SHM5pCZn;ejyirNbmq1Ovf+LEHF#wgj)i{fA7-M&`$)f@JJ)Lz9L}x{8AW$& z4g@tO0wryPn(Ov5z~p}f?Q%7!vQTn>OpB%XAIK$tqPQa@l=XJ{?4f!r?kPP56Y`M6 ztP-yWw8KT)pPfB;KVTY1l(PMKK?D!z;zls#FZpYJ0lV9rVFv0cKC^1qyoSzwioet< zpJ2)v_1|+NiRF2kZs-?1R-g=&5Ev{yj#`+&EYHO!dQc!WV)O80`nhlW7z2_;L6&HZ z0xxDZ9tTS3^N^mBQA@azqDOMl;ldcwc@DE!c>Gc2xeNZFq2`|=I$$tgX!1$>3af1D ztiOfZ7+dEeWdL54lGE4gUO-7|8+jPrvAa1F-c>wjW8wkWIn#FYVr)Seoh|ju^1q@s z;)@xl1<;u6v8@zweGlH~v&Ac`lQjER6T5t2@$GauWF0;E!r9)m^eK9-(6=Za6gZ2( zh?B?HO9uBH)SE)6ar{5Oo_x|YVu`691c1Pv+=H5et z`FKKr-SC?iX1*tY@>+AD7=UiFjyyXdi0$;CBo`xNPJHQQK_he@&f>yYvs?lySKP0PX zDX@~@eXeo9a&rxuCL@zR&_58Z(@6jb+5`m96IKsze)XVVOtup4;W?>u zPky+Swex(gTZ|iZOy}B3FE+Kpyfoi8K8uMac$WKd9z{#Cn)8g#d(N&fjrfyd@P~#m zz3(3@JdpdZ^J&bNq6cz=lowVl)YW!|XRtV0vaN(%{%gy7l+@e7!^^x!W z`;?WkTt!OHKK;`b^nLU_`6^K`;IMHhBcA)`V%7Y2m*ev2>j=|wkBbPyd*gu&n@syb zia$FEGfEOAJI~wbI{8p)YQRLcw&-!Emyy!)cYOb1yhJDS!8m5E*toBwPszmNu+&16 zgN@_Fo=mNY$A&{zQr=g)4O0!W0obl<- z^AtlZwnm4R9Xr#jmQA|)wRFwqtbza1Q|B;XRI>1SOS4 z#d@P@JXu2EMalFKM-t=!Z{@?5-Fb+vX&oZ9O1!kk3zVw-$ImU8mzjR{0*2+S_}Rn= z`~8ebH&-^8G&^CKQm!+KTqnOw+=xY==q{iy(?k51KH6h|)lI`zj)AfGypkV2e5H+a zXnNGh1g2i#YHRY>aRzc8R&g^VUSmY{j`sp4WILmG=0V7At6ZgH+ldB#Y$PZI%d*eC z-rSImkDx3a5=(#0)twAn(F0E*9k&%zvTmn2@#@B^Ep;VoiobVVD#y4*8{F@j3(^Mr zyMEU|&%2GM3|tFhBEI@+jT!Jg5*o>U7c)GT1~%8VfULd0^)Fg@Budl+do6z`h@N+w^n#dtFpp4-z1yto+0^HcHMOf4TnF48@>Ae`^C;0 zo7HB_yCu@ajT;n!-%a>;agn|BAQrZW{`W~YY(C{nt-=qvM`gs|BZo%2qu5MYlDt^3 zX(Fgaf6iyWbZ^)h5Df&C*BNK4`Fi)Kyufw$sm6k7yQ=HBB}_fwOKr!Gf?=2;_o-RC z#`5(zbv*g^RyT3PYc%lR$MY}7I_Z8^+7au)Q?0HzNH{5#WrOZcgHUg$aw;hWe_ThU zdJTh=8mJIP?!k94b`(|!MJ;>u>+)&6)+lWI!JZhH=&0}N&P4* zY2N9=XHEK}H=0)=K97BTVew+5JD_~z+KsuP1W0JBd#;Rn?GcU)#y<)=9GQ{S?`&cPD>>|iRlKdEm^xr=4 zuzZttxT*SxV^!$&dsbDp$L}OMy{)4wFHed~GY@FrL7Co|J5FW0%BFPe?giKL%$o21 zbKjD^sAJuKpvR?eD}Md6>EjQn7ld!VyBGaffse!YeU#+uxXOFx-|zOHx7(qo=lwL< z{xyoI&vR8-*gM=Do*I6-|KzF3p}GI|e(~n@bGw)xTq|Y%tzHQ-SrS)kVEHouu$kYY z$N!7r)-DejGPh!*l#d^Mdx#*xFe=Z7S^5_NwKq3AFw%$SNdTi4rFivg@_)Yw1w9`^ z{^(tz(k(1E@`e6zNoHQ*YT(w(Y!sj{HB?jRa!O(IJoFnYm&iW#%JUc@VA}siud8Kj zU);FVGNiA$j9dmk)2;NbC{z~vLXZz;?YD1+Dl3;A@OA1e@*9@7S4h2Dj!}73x<-I#rlSUmYq_ zbu@Ml#;%T5773f<%$K`Hoy$18T#QNxsUtU6jcN__%trf~w#yZ#E0Bo zUD9)V-}H^lcUPN9q8%JEYOMZ>4UT%oh#ZAZINqdv)$zY3J37BEY~}lPy4+Eiac$Rh zF!g=*g#8z)e0uHnN}i}~ z;f0B_8&?n)Vl;`VaT2fTlS0_0OM#iTB#;z|3@&VqN9FZAlYx)i!kKax_x z()dLVB^bw-Ptn8jmusN$H=Uzd17m8d!dC>Y84s}SmR(=#&p)OgCkekfFWma?NZYzv z|BvE8OuSmy>~m3C)TtUVN2j$PL?T&$^SAP(t4JPwU#@{l(~jyvb%5ije^$U1{&gTCQEXE54XPM z`_W~StJsLg{~t}~8JE=i|KZHiEYsX8<(p-u=E_wrvSp@aW~Ql0Y3`l5QOdokEVojb zTFx9f6Wq8*?umkXA)*Kh=luCS_&?$y=XG9i&V9c>@B6v{MFod}8~J@`QnOtf#PUT9 zvCd+_E}?JXG1c7)d}~N(t*rr;w1Fep(#>Xs5O*>B#Qb?({QN|NdUwR28rSIg`2rbR z10!nrB~oQ)$wKOKi@%XNsnz`1l!H;44{L+=Txk$J3)J5cuzJ&bx0;8i`!02PWZ_gO zeB^hB7-9a(iAk_wQDc`|1=k@!MfV%0xM@jfA#F?S7}J-LIieUE9Q zXz`(bJ@S}M2OGf;rUE{ZM@E3wg`Vne=*Q)K4|LNPvWO)AYc*B#~%G2zsx8y~fzJo73+p#}Z*W0+Hp zq@}NRuS4I;laP=Pul={mysm!yZf?H?+MbgYkH!TYsyKuax#`D##PywqH5Xo4V0@_$$faf<#(Dz$NrV~-)C;qC#(e_?bs(Te&&#I9W@}lej$EZN`ZQs zoBS~1f&3l)Zt8l#v7Drf33w&bsf2HdIoxlH?kG^bsDBHza;cJ+=`9WVj(E^sea?H9_Gb;_wzQdX5_x;wWd#Fo2wbFZ zz=w)cre5I<;37z5vU>m@;tmuKJ&&D3{2tghm@KryM(~_?tm>3sm5dUjiq0{6ZE0lS zc7`TEH=)lRK^{L7F4(~e$3~w&31Gv-`?*`_x{nMe1aZ-8DA%_a*fw zSL$g~s}io7YE{0@mOaXIRi)qh2-fv|jOAR9py3Z&>b{ta&_eG1F>1ymME-@!Z4LLn z;9}^omHBY}z(Z&Lb%|U?IX8SKoLhP4Uk@skRh;*V_o=w98!<6~{G zZv0j`6mjmA_^TjRT=RJn^JGgZ|LlPF_|^5zC}6rwcRO(Ii=rB6u<*J;(YH6;WOHCd_PbPOq1|^bP7TDT5oTMxrN(1$ zmlWTiPf8xX%Eww%4VL8B;xaAbkAS|-WMG)Nf%xPU zaz0$-qSNdt)WpQ=Nte63pB?Bgc0?i_z}dB!G>f&@(Isrzz=zIYbCPAb>d(yD_{TKI z!CPp43@r$pPCzg*(xvY_h2$F+p_&e$F<~4w>4}$H=B2<+)qpvBPK)=~;tknH7-!*o zX_>5bGIAyN{+%{@ySO{G!pahi&_H%HBd;WXf3^(yCqws6`f%!rjEevo8c?SmDjC9w zvzY&RJ;}rSdQ_Rto$854C$h3^$Jqlb!5r@3#ze>o+rlT*XK}WC+$7g)f?AI7KPoEt zilm8QQ(op@qa}^Zt->@Iolx0}{Rwt=bAC$)kHcZ2oJ-^FW&4=-#LvVH4L)Ye-nAI-v7E^w+Y`lv60}2Bgg$%YqSR-=U*$2V$f14_3Mzd-+KiJ3^AiS!G zW`!m0Zq$O!(e9I=rL0^OU9Tae;=!?q#V&{6-t%?|3!T-AdDLG00F&19V?eL~eYyQZ zg*K#mTGiAkaO7RzPG*k5T^tpgfoBX0g?|JR5C(`TYj!b(h6 zz=HqP`x{r-ZOgX&Ke)Nh>36XIo4b3~v$4KY^E%k_L$6ZvL;-1`Qf@TnHDTN|^m3*k zyuAWPoDfd5h~}D?8yqsDRvTJ8Yiz?Eo||pKH2|Y(3F&Qapfr5hcXgtPH~>|xibg#$ zim{lq1|ajTalCkLG?hQ%tFdlOBy?2_5YoNSH28G2+2xcA8RDzvk`}+Cm0}XE=_Lqv zW0Mw+Qn||zKu;cb^NS(_7d3MRg`~M>z<0W+Am!?@eT0aINVqxhym2qP8=-2dz5;!MKC=C?PnF zSUYn9qyjauWf2Jj%{B5lC^;(PH~94!l?2!+RiUyz* z#=)Gg3{V9+0tT!iKK5|8yQQxcXF%)VJd(?}dk}}I>+gv9rZbhBQo7pu*XN_DjM_ZkIBXv{~ zr|3GpvOycLOwQzeL^q}q2Vf%R3x5kHdeeNgjGoEo9G^%4W#G=Q2qY$hlbPCkSGz(m zA)Rzg6g8}0!U{02uuJVZNx}+wW}4EmE9)lTCnQKP!pcsg`uud~@e$7jeVJVTU3?Ig zUbUcsCdC0a!(bn#px`LJL>@Y3=Gdf}HLoSDYzNTAGi3))Q{l|>us3W?#I?EkfL|Nn z6OPS5dXh2W%)S}|^xkH0t5}wM!B_c|#hH?I$J@nqCmZY51v7oXuby#*wM+P#`Pw*uc|B^@ zOTv;#EM^O455a6Ay!Sk63UNH+)40K@TQ$ELj2rhyDkVJAx8oV_P~{S+X%((FWQ&?_ z8W7cP>w}4mM=0g)UPyP?sNF+PAYHe{5iM^s@I?87KBmWVpdQ*&-K(b~r!Aays&D+> z+p}lfDTlu4ftC_EbvivaQNG#9sQq55)mta4vJS^$-^Vpc`2!WM=w;`+^wi9@puZc- z8$k~})6=;;$tsT)jK@O(R>+94*J*rvst@6#h91W53Hm|UnY_AlPlZYjy@xi$Z(ekh zo%-*MiJU^LE-k|2=FvYP!vSEqNvvfIlQ2^CjW2%oi{$!wr`ne_UF(4aZ}z$Bvm88k zPxpr%_vXWB{31T4r*3a@I305I_p%K)C%m`;AaxHrYgx9ip_{jZB8qd~MO|B-CFd`9 zLN8}4_uY87;$0ynd0FyhwfP709uc#@AHx$)+^ui9H#{S#{*WCgBYph%gVfW(3g@Za z-{hrRyfSrRI{oMJMIe-j@Bv;3K;%kl#~%%o;i;mP$5ZF*0n**bihfmWA5~vkk@=xyODxN{V8Ba8r?W9#wGjxtt{Sb z7Yk?H-+ClBObJO)xkiN2ZdyFi1znY5Uq4)3uZSjcr#JDcz5ifc>+R?YZjp(CXo43X z+|6(3{Q2gZ5K(Ia-}B`UJztYB;XhmR!TF|Da0!e+YSKNOcVdQpciy{*(UWD)T@jkU z7`tfz-{4{Qnb<&NAinnFxj*Bpi}ZA270-WP})`W}##eoHlUlF#!pe6{tONOxEs?u(Za7(}Itn~gLgEi)4&qm2|J-kVVCJSx@P zlW2KA>DU0bY&+8c&d7$xj~1dy%hYnL4ej^DTxgKO>yPAt6p>N36R60t1H>mxJ-BEY z7$Qa$sOtrJ2M8q0T{sc+#w(DznzIlbHv(1LF?3_UEDFlE(i$|tW?vUulp)UlWJi1L9BJ0ZZv>@`nKZkC^Q}#a$Xf!UUon6k`C%*1UX5%A}=mwJK zF2zilY9+nnf^Y8rc$I$r*%9$N*lUNsq%o-f90PjI&5z43KpA_(9f8Q4cVqT^2#YVh zzSbP1Zp6TG-FS(zQ3|b?si?m=4)u>jK5QEzL7Rj^%pAa@<&zs;y{-x$>zY7&Q06@! z)!m@w;?i>BY)u%>XNjRfLEbUdpx*1A7Ywb_8wDH_hw{w2U4a8Sgxc~|C$b_08~3{Z z8)&QxP`6n^!;(?g|6w}x*=Rp+mjxxlHU{Jhci6XU`z;3vV->*nqLNSUNVLB{cmOVL zqxGw7<`yx^_ZIznG!Te`bs|8XmBZ{G_?MySh~uST_Ll$yo}tnXSMB>fcg{-3C1L32 zfim(yAs(^ebSY{KU~j+WABJ4cC1N_RV$h_TIWR^4X^ykT%uq2EG4Q})l901rtXa8! zGiR06@A3b$05+Ep?QU~m{{j&U8!zh4W6%xJCuAvax2?r7ZzuhNBQ3wIy2BETw<&c$ zEGG0*7%OSu&~olu=BkF?9v7QBF=|s0IpFti1=DWJC$6GBF%9mlS)GQlr4TZK;IhH{ z1E4H`r4u($WB_LcM|^x#?Un@!g2`*ya)b+KG8)1UU^y;-6W(4kfidJ9JLCU=%zc>% zkv}yohpk3ZX03$Unqd3Im!7+%bqc0@zc`4}X-0#g5X|c(_qUOOX>yr~@PjI~o3+=0 zns-F0KWPhN66iM^@tZQe{PnVXh0onhqQI0i3d$W+Z1MlzRVatIV;M7@nTi3;cAj%n z4sL;-Vqg|%e8}Nc$CB6{n{IUk0!OF}QxXL(>1fQ61O=so1i(ZxWJcb#PSf!PJ8aP2 zrg-p&Awzy4S>K}jnl&fezmvRDt@znp2;0cxr$iJO8!lAi9_6kGMBxe48Q%6 zOXm!=J|^|4i{eAA=)5Wny<-@D=(1G;?$cYtvbrzpb{n!)wjht`A*+PpgBrBue(~3nJFL+e9x`hrWyL& z_@<%k?;8%T-h;9N@bWmrho8?$*PB^#7?XTJc#l#iR@z&ixjeIkKmba{a)DCBJpW@yfrnudg)IK#vi~?cW3D*R_u^# z-i*(#eF!{A(Vs&Huk&GS)tCpO?&`3(Ybyn+fesm5i{YlX;j|)iwq!)kC}-T$ti=y2 z+7I4%@FhpL66n(07v^L_=?Clzi7@ZILn{Jy@fv)4KAA3rNp(3Q9*Zd(gLq%yqnyh_zV_+X$4|b(tRw8knMIv-A@DlD{#Z>srnt zZgZhyW!_BRIuZwaEZ(kYzp<}>z7e!SrcDV^&64mnSpjYaD3w)nU?nSz%hJT}1w2^y z2otTy$*Cg%zDLSu)yvddkNeOVK245vS*?DZ3b~7>?0Ge3!s5??}|Rtwa(1vQr!60f$@96g{GQ()$>YM&>gIpBKRB*-q=+ ze45mJZDBgmvbL%bQ1htM0V{Lv0T3$YB|E;8rWkJMHVUIK)po(Ifr>zS-{lepO1dX! z!qKw1rf8i;uUtTk6;_SY`C3jZNYwTI@S!d~(>*rVlq%je-kWg%8Pb;P5X#!ZXQZ4 zF{F!<_h&XnyeHd`KJ0+u{M;sJ-Ym_)1xSoCUMWMa|(h&Bl*NGlV!3y2+lvQ;Z=8lFUyLX_;!w0tRK0oh)aK- z9`B^fCAh;`biNH}Lk5Xsf4_w9i3Dedi$r_pl6b2k?|oy2CehF9ee^ZdtuEv?~599z)7&7 zS(>aQyYvKJn@#>wTO4W6C|^G~KoI~`i?omQ#_{7`6;nPRNep^}JH5hoAgwsk2GNEO z4LQ{em){`H04rDeB}&I&N6ax)2w+PIqHawAbX3a$6Er1V_c(Cz>ua!Ug;@KuobL!s z+>z2oCrJM&(}(9Lo?B}QEHcD@A^^5&__BqfBh1TFH%u!d6|3X>5t<<74^@hEp!uqB zb9Hn_PSlO{z2y;6EkF}2jT;E8`sZTQDuHHeHyOUXm4Rit0J1@;#z;@nKHZ$@Y0(O$ zf5Wt}J1@ZTC z=RlA}Px1QG{eAEaL)unO&*Va_LD|?@(JMor3B9NDN1dFSiQ1>Z|NUOtg?O#RLsTu?e+vYO>)dnU9%u+KHVT4H*=#(KlzAX9gI<}n*2GAOhUUQ$F0?$!88I-$xB2bNMBMFO#;e%5`Z zV2i=fVH2o%+pTfI&#{?~lK9XByb$KA^w-fxN-u)7UiKKURZdt$#O9a`p1miKSWas) zv>(HJO87VGwYZM)QLn^uJ2i-gy2bMtUb8-n*)&uwjX}&H6i)r#$O4-VFFmCyqJI>` z&FTeO3+a5`XEMEd61u|d0m!Jam+mOE`Iq$9#vOcouf?53Ia8+g@dhv3ML_KTx-^*8knX}KlFuMKT z6^&+n-6c(Gd`UHhx#|Wq4)^I8ew+~gQr{N_5{xsw9}tjpIxXyq>TI-p>Jokjv6`#gQdnH#*Rf&I zf{toBV;LX1Mv&(KR;Rae5%I2{U-qNdzKBD|0 z6$zRb^t&&x2eU$oAMrM6s#lzWoC*S^#$qtFToeRZ1^VeDsm>{scG{JTJ}`JHy8d@YX4e-ZI>;2*;HPz`+@{?RNLmtRk>TI%2dnDN z6sd{t!`7Eu=s_OnK9azfnI=`OGK$Ut20!EAB#}Ip+6L2p6rI@%()QW!)hhja_ltWB zwP~`OjYB*R5Mrhz@O0uJPmK@Y3ZFux?u$Su1SrA0_&=VK#(jSobgSyE?M}NhbsGlF z>J7(ke)glEwfvD$*-A!sNOh=6B4bx`f15tJcCWSA^s0-ZJKV?TR0#b<0dwj3=e8dC z3tAqoR8xv6w*P6lGhO{tpR`l&E|`%Gu3tZ3>NJ16Zi*JvY;bpiwuq-)8& zTylUb2PfNG83Pq%YN1-YmNZOsnXt^Wob_BXj6`fQ580xYk#4v?y()O>O^iX78>^SB zD?C7la7p~T<;(^dcgceh0=Qr8kdnXgcIFr^=sO1!ZIY)G7t+yjGQRvhlJAA3+4x=D zMxhFQJfOdCh{T3Iel2o%q2Udkjj1PJ-qaH6Rt;r^)4ms)SWM3`b zkIr*rLv|CILWhqM(|=NwY0KGIp1c8JdmTaMDdw@D#N}72^v5zj_@!SSC;ZEziF60B0s#BlDQFBjC!IzT(5)F zb3L^7Zz#_!+sGgA4Gkl+Lh0w?_sEKkSGg+!B40UH)LzKF=NV2M=Ov14T2@?Hl1#2N z_WYh73wSWp=7rEZWvWq0g17GdeX@)>yz|Rnq~qh$a$OhAyuL47%Sf(!uPR=+if};4A0!g#FPw*hu4>`v z`TpBL+W{cm5Dg4MrWu(CvZ6nXap=>a$k;CMAHdKt+h|(%xIo;-!u0iqOIAHwL)SMD zEo`8!?olT{BHBv}P+ZF|ZMcHeNkPq5ZSzsB04;NVhCU8h-R3jau@`{g*;GTrO!Gp- zY2&J+fiC0MK##3<@7UX)GU3f}uM;!+O;#)>XKCZr+XVfoDlvSxySr|Dx5SQ?jIfs# zwp)#^{~z2z2~qY?Fk-)ij)eMota9$2q*lw+Nx(0>jQ(g#93|OlNleY{LS>j!K3isa zlPdqa!yKpt1AKUq(I4$Wp1}3+8>v22KVMc18C$o3dw|0dLsrfq%Eh&|b%0HV_&g!! zLD|}EPzCM3cfy&=7x>jx@yh1RSSro`Q0rw$qt`MO(rWxsQi0YUd3w^yGcr*(z|>jJ&lQ1;z*uKVO>hzc z`?#4$yzwuDbQpWYwXD-*^BBCN1VDXyyGS_X>UfuvrwotSU4B`UlY`Gqe33 zl=kdmSdB_uOFq;+=*ujF+FzGJmje|)HUIaeCyLry+l$t{7_<8TR@NQU1seRgz=)Fb3x?5|tf z!FjY`PItvl=aQ)_#=LhftpEI-m?C#3wUBwF=;aI+x=)LL+8319l1DmwhL!lr%s)$* zy&$cqVWQo}rh>d*Fd3q>>gBxZ&c=V-T$8dR{>7pARBIWzb?))g`|J-M=Yxee`+2@8 za5g>GR=Xph$XxkqR%}~dRy%Z=5(;z*n*3o(O-#LSv0xLEcXz<(Lq#ZPmvY|L`cl!^ z5k6zx^59|C17Z%-93~)6n(pqL99?AO(L6niTUxyPr%Y~?VT3DC2>Yyy|MKq)sO!>Y zQjEe<&Kyy_Mz^)=YIoYeRyq+r-J$R5Rx673(&CX#O=<^Kfmxa-;h7bKs*YjymtVZLpXo}xp7L~56*K0)#5l_m904=1<0N5t_9e8LhZ*@(YdcKrU>&CJp^wflawsjd)rVY zf@XzrKL&6LZHjH1Dv8Dk-)k}8ZQ^BgOb#=ZpI%Y7xo+Xjg|p_}le#A|F58Jo2I6tR zv$oO*m_@;kJ64a*vzWq9qp*dxqptY%=VcROX}EX{_4Ba}dV?zP(zm~NH5-ABDkWmr zH=SaNmZ`$JU3I-`VMk=dIF$^jfF~TMKN~ca$}WikdqC#N>LlLWz_MobZ~5IV!z2g} zqvyqeM27ri;950W6i=BwbC(|Z)L-n|p;Mvs&tHiWZOisI1W3PA=={*0sM{ex;``h9 ztMi5n0(@bmL|{g)rf=q<9b>N((=6i6P}3lLC=G5-WwPOWD+d;5iYKn3pcS5Bg-=E) zhjzr5#;aX=H{H9mDO{mpJ_V@xJ-$IIM}H_&PVw1W@UBy-qWb`R^?H5*wy8CqmaqQy z@v>;>gPuOMy^d^}15h`sEzcf0Z0R}tb2=&ig1&RQhd+k)Gq^Y-_;M$@_8D!-wJQv!C;Q&xUxk4y&$`i?EjFWZzjr z=S|n31LSDmxW+FNjS^QUdFZeNKVyY_qRQM~kqMx&GjV;%kK%uG+OpzZ%2StDM-PWy z=7`SiF|Xc#P5L@!{(WbSf`ag!F@>hfhTb*nn=Oye+`hWgY&v)Y7BhbaIrhqI^>trU zHs2-N#7}2NZ_}~B4yN)b83$^-MX`RHR0Q`lz}DGRG+m4pM1mhtlZ{8^;7gQ2V;KOf z_+bt+5$*^3&_Ux28y>^}_@4o`kqn`01s~5~zO1e3NE+%4YIgghK4dcFJJUmcVfI}I zERA>R(kDwcz1-$fN<8s-{@ufSGz0dCxzLX9vT;!GY0m{-NeC_Dfy?*T1up0zv zLMk1)gb3*QC}QLH>OJN>lhAQ6<&3@vtRyyV%?EpwzC*3yzZ3XF{n*uSk6k&6>zO;> z2FekvhIG+TPX5{TY{WEmV(v13&>M0D_?~PeaBVaY?)B+aJZRnyp${L5X%bTNf_A{4w)VZj)y?6d6w%|juurmdT+wdwI>__iqBlN z#mW2hhT*p=s<&e7?%UnT)30MyjzsFWt|>9Roif1f$DBgzx##oHxd?Sc}7j}$E`SF z2`ml`=!AQgTrsompRbYMg8F@HWDsedqV#f6=3UnaC4E-{2t{fveCLo?0mqyA<>{5X z1MXaTv117kmT#+w>G8iAQ@5Jqeg)i$cep%t?}?cYVZlX%`_By_keeiS=J|rF0#~;g zNqTzFcM9xi;D?>=G1sW3BUqt&^~p3u31L9;0?VUCQ1x<$+%kkWJGsh?YZtmG1uDs? z+n>FV3*04XyyNOdSXZvEuFrYHYt8 z4zh(ls}gwKJu=gVc?m-Of#+|&_x>>siEIbP%bQ|0qgzwYkWitT%uIjofC-Z3AE|!< z+&;^%Jln+WlNbGW_o+GkIE&CeS(~xsZcH+#3zEl3$b9=mw;7i?Pj*T{{ZGKyB@ ztb7mY@Upxh!3KGep5RWu);jLO88%IM7AhM)FAnqeQNBdG++KecU0hU{BW%iY+_gCjkQTj`h+s3uKS zrCjrD9grxXyW_tJEj?G)$jL0MdCzBfcDr3I{zOGG-v|ky3A`sagW99Hx%Y!Q_|^1m zHt)UItA3AeOCm#Tf0r>*7sDz#YDPM~(Gh1!c2AF>eV@WFAY)=>txhN6PkSEG?bgwt z?d~e?XQe*u6~U#sc{}yx1lwC8KkqDX<2OY)t$=CfuHhF$LnYcB)cMpZMm43U-jp8e z|4kTftnoZ`X@)lhknWI)fQ_5lz+MU65PQ>iXgB z3xqK6DmBIBrx&f$CwR8sXZ(~qg1vcS8k4t^vXkd-@c{TWp~&+s{NH(;Gqo-36kLu9 z&3Q*@A*;)%*t4dF3nrIwr~PpT544l^6}<=C9a0S*aUJY)uM77mU*8nE0WKZY+-Wx= z>-FO1h)~UI$-~(acDGJBm0px>X}3-&+K0to)h>ochl+zA4sf8+27>#3c5oszveo$J z(Wc7xwrKeMVYb2!L}H?LcZXk(O=&UHLsDD(>Lnmc{ZFyLmnJ7z>}o?V066DZGJ2C>*sqlPH%%7l#U%q z=dI0({kJQbnfNw#Tw1QNh|)UslER^C_i>bT0`S+Nc5a6gk5GH5@!Z^gq85jW7^+-D z0g7hqU-oy`(@5{2m76g%%MDB;d=8jSR68W9KOS6Xa%-2nn~M^ZNz&j25rpkK2TpC4=QXANQvI zd~SQ`on>N8{}f@tKGa%{EVCKQP|I#ZmaeuUb+s%PIOsXd<4_`Fe+Zt#TqN-#iAvZ@ zQ^CaTjOgpKa_V$X4;Nd`qeN1HuUq8$N?Bc}>B39)Jc4AtJltwn_xsOfh~f0J9s^t! z$bN*}cO|IepRj^WEm!Bb`aWcl2kuCm%%&6RKB6wg1dnPCoUxMJyXto8FioSCCxeV6Lm_V! z?Zu#kJA4kKOMH!1?WuD|zVCn6fUFyDM4@i!XJD_*s!v1#XqcCP4J{ay5-)|;fpH6C)cVSwY9V2c)Rxn*19@Qlj zcBRelogK9?LT0bOGuRQ;LiNCY-4`143+Uy!fRmQ3 zN=vl0=dBd)Nd4lq(l~(L)9P4FhB1NCHSYR5&4aasH<{Eixlv(EhY&{v+a6Jqx+?W- zi1*a8=oig-%zl0_5876Dd60zF6&R`0qbIvYtR2IZ^A+@IM&as<{gcw+!l36FdiR|IgC}is4h>%O zvR4jTz>`EZZ0fj1ACxk>wLk7U7GIvs=-wCzYg8P)+U_D<_QoXjx^ zP738yxk2auysk5wVUn@f$TCgk&q+pC5&=lWd&Ng*^L|&Ha3whXH%#66g>$W3^^aE+ za|b7 zL@K`3WYJS-%+DBrUbNY}E8nZXWAens`8$N}H*`A@>66#(d!j?xkME#1@H|8($oHSQ zN+Fqck!qNZL&U@d&@ekc%`{_8OEGo|`c5`r93tllJZ3-$&aJz{EdTv8KHo=enQ8#G z^j24&ffm3tx=~Fm-knESN1gsFK{FDUTp0;_<5b722x@C?fe%UEnId&}!C1neGoyfE zT>6y0#NDH1dQNA{HnUa()Nx`F_j=u;??7fiPIX%R;wrtHMsfw6V}8E|FGOc6>clAU zU;ZHa!02NjW4_12o&O?WPJ$d1yrBUXFAxPrg5H;94MpjV9aPBWqJ=`zc;N>xB2*s_ zx+e^*fJi8_%EE0!Ti%XbVZy$*#$)|_^??GWSG^i`~&D*-cUNH&UR4o1$|n1Soq0K zBca^CFx2a#rUR->F5e%CF>ulB6l zp{1XMXO3Sr_Cxyg)=jeiY=1LOGl^8K+aa@eq;^m&Z=tT-jfj>N_gy#s)xBG z`7wuC4D?#Dv;4T-p3F!{WhNqw91fxSa5sK$u!e%yj5j}hwD?tV5TAaok!m)6E2(Sh zdllb)K}RaEQx1J&O}8bd(s9Mh-W-KH+SBxkmkIM_}i_zo>ry{ZhW|T z4W6>we^+i$1%L#O!{$Q31G>J*2x$Ycqv$`-$b8)WSOzPO7-eKrq!smGOCoTZduwjL zAK`z{52f2p?T{B$=`<{clIjNoHH}y6IU)1o|G*9eDT%qs@@AYUFy{Jl+&mpb-$6Vz zz8PGIdbf<4&mzdR>wspaq~Os@I?k_GA9$^tRvvGX%Sscjvi_F-pJ-bnz2-50VO=j# zr=zAkZH)G(L+FL>gzniuk%goyTe3Qs^p^3N{4tLhdE`dvj;tUu`~`M+#`121Ac0My`(^S{>o&Bn`mIfyX=RLGIKur<#X^ z-yOtSThj$G4LzcSALB{5hd=XkW^9`ofK)`W2na z%!G9z7)-vBM?OtI_rXJZ)WZ)pYl)zMr_cv5S3QMx^|g71|5uxLo+J(oU8_k5B5Vhm z2Qy1zJ6kS&6lD-OucgGoxx)syu@nohGRF1fS=xu^W_fkV&NZize^kHwpQW#givDoz zDr0$ld3Ca2jGBi3N2L~m&#_)bRp1qShFJ7eajDOh(5uQtUWTtbYDCZIN9SEXM>O2Y z8&Tmiwf%JV_ho(#^Yfw@30*%qo6VKae?T#+I?kI#hgx{bZ1dC?52ukNd$%7ARK$5G ztcuNjRl{SmuagJ9>5+MSHP>CU39`IJ!$f*U?;siM8hZnB1LT8PS$UVwI**mW7u0^x<+FMV@5!U zG}r*yIp720xEr9c(^#s|h1kuwv;25NvBb3$u?usxEw+1GGy(+I0wOBmTs4W15GPM~ zQ}qGV#~w5pmea|M+9Ib2tSgOgVAO>zUi<~Cz-ekD7&y>L;rZkp{e22*Uv}S-#G?Op zzR0!ipE=3X@sO?R;0>$jkthxSPwmKa!7nigZ=MyBQ{XBZeTMY0GbgtHbl5)EmU#y@ zlE&~q@?^>t8Ju&103H>flvf7!U?q@!>b1qusF_=Ghh~}*s_eR~2`jR5SMx4hG_>|$ zhdQ;jwC8&}9UsXkrwoO#yr4M|AzVX=s{9}$()Huli;rng@G2aA zJ#Zd4Vz7U@2;9cKj&yQdft**&kE?78#>ABj7MT^>TbhaZi>7Z~4?Xe8-VV4BG;r*s z=XF2rTkr2NJFv67zuIp-TEaZP(F^DUr$Pnw%fJ3G5du|C^}8|4ho98XM8}h?e!bqh zquQB$hQ!kl3y!xj0W=b=P#Ud(FvF9Z{}wgo431NM4-NJdV!@Jyp5^X%3u9QdAs7fw zK0Pu`Pyxy)xOV1x^!v?xBWlw3*nZb(4GJtFRGw6>&XfD4VowF!Gn;z4K2$P*%ge2G zF=|W#7ZJO+$=B%7A5@21QEV~yO?5YDgsMK?fBd?5d3oVza+&G`o`m$Uff0p z4Yx5reGI1%aI0ISV)Zk~5sNpC{ZT(I?Oh`)Dr*NaXJk@5mH}x97=_bCrcpT*?o5XR zKz8JS|1OTYtg#ni!?^Pof5Xoq`g)Q5B;ta?3aa4n03HuWv_AMOUy z`{PVkaQ|lWpA@x?y&hYlb~SG?q=VvHv*!?-bALgB(0=etkp4F^v6~3(E~1nkpjhb< zHE8fusGS}44+&X}X=su*MukwGM>b@!EFMFotj_&-}or zYipMpZw$UDBK=HS&hr4Of7v%c$@R0G?5vW-iID})K4-;`r7%ZfdV^v`&a)_chemQh z%}UVNFqIsz5vh*(I>7L_?Fsohl%M~$AutnJKOMq1X<5D2H?3K{x>H5(#cc&=BL2v~q0_K*#>-{ggC)0?bQKTbC_H5q#`wWEs6aB%Ya z(_--sZ#>_NkYquwKNFG*Nk#@ym$ev?m9#$5V7I^@f153zWt`@w*ijLO#`LY(TyF5( zRfYdWg6rEb1Z{lQrua&x`p!7Ci#8Ctf!XPEl{u<@(I<{-UDqg9W`m?preAke+2sJux$Nea_OR(&2?t$ua8CD^tIQC=hE(GrgWWr z-5dP_$1aUa5y1R=65{7xtUV6u(#kolr}Ln^+G9Kta`c#sPpZCphf(TtxYjfu5BbT9 zYmFYWQ`fh&cf_8l`h1bcCf29{TfL^QwJtT5zkr0 z+SLnNV!A=Oh4H>n3JYj*{d4u*XQd|nphla94O}@z8n=+V%mH(MaD`777*n^8qF`!Q zHncn!J<@w~Rge%dpynp5C zS6r}{^Vnjl2;69muY-d0Mu8{cH@6AWIBo`tWM%3UUsy?Pu|>|tzrfAg6{@D{tX6~` zG~XH9Z4gpu&eOgf?%@%#n3a{il7 zi$0>ODnu`bbntXq;k5%?PMr2(LX>Y-y@=r6-wx_9+op&_$ZYGM+jOobfgN}LH?mvM zg^Qs*N8I<~4FpOD&rUC5I1=Q`&y!L$__1Mr^-B65v0wQhnku2B|8g%=mzAvux8UEoUz-Tc=%_hNJz<~Bk+w=HSUv-pI8brR#y;rPwnEcM2A3K34i$PJ}{nZ48fMHaZ zvDfa2Dz(sb_lshXaQxZV^my~fjh;V=v}n10E8rMxf9IaSk}W*V z{lcwkMcpdtL>ws!mKQzSR&*P_Qr95_PPb4npN!65S%l$px3BP_6-^D7k3o*-Va*`c z2)?!dq=5G6Ybj9ergr1hICA;L}zG_-IlfFd%;kVFNhi_2qrf{X;! zYxjx@x6A3cT?7Mg?Lo-@T)nKyHM~Vba6+`0px=Whpj4g(QxEuYRsiI=x~~`t`WutV zKg6CGX*&0`ewM6Q>U@Y*%Az2G!E)I&3Z$n(`bimEP2@=1rJ@RMV^?(>S?SKYbPz4aTphF9FNq;jZ~pMrZoqO21!mhq(! zdW3lA>Dc}Q^sE0zr~;b_0X5X)zh7)naTnoPKHBO#J){t@t{(5RlSs`he=}!t(XaP6 z+wulC-Aslu4h1&j-@Ku^9jeh`?cLszFJ+yeF_^Us7>Yt_usf>nQ zrZ&MA2NqA75}~&HSFN^g6jb5Ph5ZEw1KJVKH9FmdB!l>I3#Dn}5uDlBos!27Ht5le zbap30Q~6XIgh8sUlmD#y1(&CG)M| z#;N!JXgU{vrr$sA=PRj%B$U%Cq;g8m#44d2Qpx$Skn<_$)A&>h5zDzE6UmvJ&*nVm zbIuIIoM#T(u;Vu@ah z2e5)x4Fb(6t86NOhx;c|wOPoe^KL;*|HE?GG)JWte^xlh6RSQHldVKdRC?_Q9fQd9%vhN~L29+t|shQ?MPN`>5Ar;L)9< z&9yz(QF<9qb{lc7Iyf%1ZYSgZAJL4tF|ZoCw?@}7lg~ZFtu=u#smJO>-A zqSaU>^)Am3Dd1iE=PyTEpEjC8AO3>EWjuV^WN;Wg!c+M@@QNBE!GRAFC|{djr?xAIlIvQoO_D9bxQcfvYJ6i#Ypp-4*nE%RKn|#0Ea|AtpiZ z2{u^|);X}0#k61OYzovkn-T#ruCRXtC!idG0Xu7NUJ`+j?z6pLlr<(vbHGd;&=JR( zKl>2iXp$y+fdFtKGxFN=X=Pboe&E35-G^qoADka)h`0q@hAeWjc%MVMolA(cD26Ur ztpdFO{jD5RP9ASsh{lwjhS^KkeJTT+D0HMVvZeY)Kra|6 zA{x65@L+9)joitoLTD*}3p#t`8u#r|HH%EQehn=RMWGkU9_JuGANV;Uuwex@2GjH1 zfWFyU4h44%X(l7eYHDNH(|w|9g&t%*c_K+D33$Dr)--kJ;}VLYNw#C>TgAdmOx3R6 zu7D#}oCawVy}8#%oNWi{f7N?k@*MjbbP6r`Fk=e#yuvC>K85qK zX^qd(Xbz%1>{CtEpy_TV6;&Cm=iT}MvD|ae z6a&8F>!~{zhHhChC(OTGGn{33jOT@5qN+Npi;S+PXCa}j7d=?4-}?*Sg!4;^>*V(0 zTv?jJ1&oJ_bW3gS1tQ8?0SZCXpZ$)SR710r=<{~bxv_|S_OEMyx%6?U90SJiU*Qq9 z>_ZydBN^&pqnfS1yx36l%=6bnapP4Wog!Sq$Xi_kcP~x%E}_J$?~EFJd#e)Nq~_N<;1; zJH%$o$(3L?_e@@hbpx0z9_nfS_~w^<%9>JgB6d)j|I-mPTq-n`EANoDIfx|9gP2pm zWKBD&Z5Xy;^%1&F2kP}NE1y~c{V#-4IybWoH|GQ6$eco;e?V8!@(7&9hk?ot`p~+^ zQAq%cb9LgsC^hZm!<5Qv+M9yp9yn(btMJ)_Ri*d(_VWMLR2ZZgo{%|lUl1V&9-L4k zJsRKZ(t~^Oj`t$)F_6Pl>k5jXto3)C{Ps6LI^|gb-P*GNQ=%o+tAc$U^zfAq7NYiN zVeg2?xqr@W!GUTi0Dh*I7W&W9-;8j`1kUb2JfqPq#v`e{QYz+Lz{)0YaY^p_*_|>l zFj2K*0Y^X|P2hnFs4foaYQ?LP7ut~N7 z|9g}g3?-J+>w+tpqh(C%{dAd!rH(0DehvGj=t6&bs}B2PO__&u#gqN95z1~HeyOsl z$+PIF&K@op)^zwt%Ffb*&^qBWotPXppFN{`rqRhI-S$8ZV+16*+P@DU5Dc5F@J*%J zKU{d_^vQdU!ukvtml+K%{5j~qTl5Q+0G$)9>3U(GR!avRUDP(*WzJw`ajV5uJV6Kr z{mq~2bKEBj9@BN2>{k-q4fa1&qaFPL!3fPoCkKg9U-|`UP_n>+n?cX?+<3Xc0 z=YD(3UFK2>bB;toqL10$2{EKRJ}K#)H&n0*8RYc__^glocz9E+dGS9VwHxJ z#>*^AVPm^=^uoFq8_0-1I-!rRW2!wkJ%OB|Vo*eUUWgLSA9|2JhortoO}wpl2t7f~ zQi=QE+#0gY|H}kT>}v?eSH;9ZfwEsAPU5t%n#T(->9+Z%UcRAw;I|d;+`d@LKBdPT zt4El9pgQ>EfHMuCsY6BtD5YGb9R`&;hN5R}w}A&fi5I6>ekLUAHyZs8qnRVQzTbN0 zm;~@vJPUut9ed!x1!3VzeJvYmdFu+)XllM0e}X7lGCx5)I(cWzM^lkkEbElIhPZ-y zkmEcENLxigL|3v7tavc1>kCif464KNXO;Clx%jvEJ3bCwz2EFFz9@O!kYhORlt$OK z%MtJnQpxq-uhvuAD?>?yk{y)PH^yKQ_@g({9T!{@=z~o0@!BnBS{rPEH}=4emgOA- zeaD>?{>&W~4jI1E$De~d#0jcr-(4>wV-C7Sq;nqIKGut~LAzZ`1Wy@_D5Lv$;Arw0 zGBNu?3sDWUMOZ7NCdg75CviIyJ~lZCdc4CNyKpfLaWkX|EKZZl(%5-lCOf8PlJ6SS{Ifcf z6MUnDARs#_@*7mwuY4i5iX>x{n#6|y?8aNA(B35QnWk3T@m9*5=#79EY9-BA%;SET z;}$xA15+&lo?3Od?s}PparDyXk&K^X4?!MPIoF5m*D{P(flfq_z#()WDeuw&k&C$g z<4%b+QU9Iht=BwDq>ji*357j3M&!4CCU8G(c|j%f7Viq>NSE+lAap4AHJNB`e_1i@;H@nF!cuPok1|IlCdeRzl$Z!x9+|39dyY zT8l%8osZRj6INi&YmNP5()rh?FSB(AbnSA7vVNB$+g0+!erg}A`*C<7zCBC^4;UHw zOpj!$+oetYY6m+NOy%t!qxC2wwoTWxT-Mwn^aT2+T#3tB42I+F=x%Uaj)FmhF?8ID z%mXJRYMNa>@NP!GG~{QRFTJGh`o4JJqg2YE1t4DcK9xoJ_T&3!Wu9p&-h;nGbPaEo zq)QPhy4OsT?Cu~Iuog+3gNHeh^CdP%i-)+6QsZ4!fATt|SOt#}7a7rn5Y>PBy-XQV znAEP)MyC-ImrTJeQ3B8PH`8b$(1ITO<=HMme%NbvcCb0;*WT~+yPhB;{VX1$#$=2G z|C%-E;Wq&mmSs52msTCaB+m3_?X`9spJ~4FK^@4s_jCU}35B5-7 z^FzZsR?27iIU2Pm9DmY;A5HnJLft|%9V~IE2Sv@1Lhz&wkDquFk9A4YUjMaH@CvGN zzQou`Z3#XA1iyISxJA`}8&{68>vUTXjaBX$?5Wxldc9Baz)Fo3krDhU;{&KE(3Xr3 zBGvnQP}M!=R7tg%DeAm2${E4)`}8%M)R+YPX4!i1CUAJPpG4IvxlE#w+^0_HUr^(7 zxKLc}oH6QVuxFa5Vd}}?7wC=owpM%VW~|^#7MS87KUHYz+nc6X_DNn#IN1w z2e;!+Y}eu5N@c0${+`JHr7Y@Nb5(IOLgV^d_*dIm&QU<4g7A#|U+b zB|YQdEL1p=r+OBB(DDC>cVtz6H`QG2skC*3)Yp1;f(Z`>oVC783yD(k=G5trRBDR( z>EFG9nluO6R6QoM@uYUp2~XR=YxuNQM=Vv&WS&sfEOJMn1F7qh)j>Zp+C<2^nJPgs zkC?R)xsveRzeuk$;JFadK~_g8`XbI`>IedQuWnR+CWh?;*IZKxf7DYa=odYkqFDDt z2xlXK8ftZZb++b?nr2{XweKl;HP*uyRvoLlGzY3vgHOfpi9a}F^%tgU8XUaQ_usHn zrT%=#*|;)D+x)D2CT~0E)bEf5W{Ikaw|P6~YL98^J7@ipz~SPo^$y0YhDILp?5(3O zp&v%S%4<%t`3(J(`eqy(eD(}Ag4_L4x)O`nmK|IbRX z*4;bdO7{BLRmjbcbg-1cBmA|Q~Ri`?PT@Vy2UziO?tyHbKor2G~H<`u~YSWgc zc*Dkzhh(bHnWTI#nc3u>PhKyr#ke_lP8^zr^Yz+K&uUh|%+1EG(NI?RSdZ1%ldKD~I~%Dk+PMS<2oyP^Nz1)#rv zrlR_Q)p2n1Oa)5z!14pwrH-GYeup0UyKzSRZ){IffjWkMK*0Ar#9l>$&u+I0Txwww z*y#cEGk0O|JkXi5VMYwG9*gZiVqPi)y{x<`x%8JrPJL=r%#x^LpxKOAR1u2u^j&8f zXF+?}Dom7k;+q23QdF{_(VbGS@)gm!-SRr;qm`nR*rt35-hQ*4Jla!%f`Emr8yT`q z0M(8F%rcZ*6Sb-Kw`#hYH?P9*R@>)x>JkAaq7VLxy*kHqlx% zd8TH_1V=_ldR&%($ke!t>k7)PghHa#e_~gZFWlYs_|)=b?FIi1PberC6h4SW&}~{C z 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 z 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

AOctYi*hMHLB=i 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

    V_(Z2VT#e)+M$Ie5B0b`f}$S<=$6 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℘mkU!%w-Rqry?@*-#`!B3EJGBLPqWzoyLU3 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�&FXhM=Q^r#UT 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

    `T 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≶f>h&MLMJoJ|dv#Y9RvNh!kP?z2Z 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