mirror of
				https://github.com/maxgoedjen/secretive.git
				synced 2025-11-04 09:20:56 +00:00 
			
		
		
		
	Merge branch 'main' into maxgoedjen-patch-1
This commit is contained in:
		
						commit
						b82a52f172
					
				
							
								
								
									
										24
									
								
								.github/workflows/nightly.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/nightly.yml
									
									
									
									
										vendored
									
									
								
							@ -12,6 +12,7 @@ jobs:
 | 
				
			|||||||
      id-token: write
 | 
					      id-token: write
 | 
				
			||||||
      contents: write
 | 
					      contents: write
 | 
				
			||||||
      attestations: write
 | 
					      attestations: write
 | 
				
			||||||
 | 
					      actions: read
 | 
				
			||||||
    timeout-minutes: 10
 | 
					    timeout-minutes: 10
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
    - uses: actions/checkout@v5
 | 
					    - uses: actions/checkout@v5
 | 
				
			||||||
@ -36,20 +37,27 @@ jobs:
 | 
				
			|||||||
            sed -i '' -e "s/GITHUB_BUILD_URL/https:\/\/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Config/Config.xcconfig
 | 
					            sed -i '' -e "s/GITHUB_BUILD_URL/https:\/\/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Config/Config.xcconfig
 | 
				
			||||||
    - name: Build
 | 
					    - name: Build
 | 
				
			||||||
      run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive archive
 | 
					      run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive archive
 | 
				
			||||||
    - name: Create ZIP
 | 
					    - name: Move to Artifact Folder
 | 
				
			||||||
 | 
					      run: mkdir Artifact; cp -r Archive.xcarchive/Products/Applications/Secretive.app Artifact
 | 
				
			||||||
 | 
					    - name: Upload App to Artifacts
 | 
				
			||||||
 | 
					      id: upload
 | 
				
			||||||
 | 
					      uses: actions/upload-artifact@v4
 | 
				
			||||||
 | 
					      with:
 | 
				
			||||||
 | 
					        name: Secretive
 | 
				
			||||||
 | 
					        path: Artifact
 | 
				
			||||||
 | 
					    - name: Download Zipped Artifact
 | 
				
			||||||
 | 
					      id: download
 | 
				
			||||||
 | 
					      env: 
 | 
				
			||||||
 | 
					        ZIP_ID: ${{ steps.upload.outputs.artifact-id }}
 | 
				
			||||||
 | 
					        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
				
			||||||
      run: |
 | 
					      run: |
 | 
				
			||||||
            ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive/Products/Applications/Secretive.app ./Secretive.zip
 | 
					            curl -L -H "Authorization: Bearer $GITHUB_TOKEN" -L \
 | 
				
			||||||
 | 
					            https://api.github.com/repos/maxgoedjen/secretive/actions/artifacts/$ZIP_ID/zip > Secretive.zip
 | 
				
			||||||
    - name: Notarize
 | 
					    - name: Notarize
 | 
				
			||||||
      env: 
 | 
					      env: 
 | 
				
			||||||
        APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
 | 
					        APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
 | 
				
			||||||
        APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
 | 
					        APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
 | 
				
			||||||
      run: xcrun notarytool submit --key ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip
 | 
					      run: xcrun notarytool submit --key ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip
 | 
				
			||||||
    - name: Upload App to Artifacts
 | 
					 | 
				
			||||||
      id: upload
 | 
					 | 
				
			||||||
      uses: actions/upload-artifact@v4
 | 
					 | 
				
			||||||
      with:
 | 
					 | 
				
			||||||
        name: Secretive.zip
 | 
					 | 
				
			||||||
        path: Secretive.zip
 | 
					 | 
				
			||||||
    - name: Attest
 | 
					    - name: Attest
 | 
				
			||||||
      id: attest
 | 
					      id: attest
 | 
				
			||||||
      uses: actions/attest-build-provenance@v2
 | 
					      uses: actions/attest-build-provenance@v2
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										36
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										36
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@ -32,6 +32,7 @@ jobs:
 | 
				
			|||||||
      id-token: write
 | 
					      id-token: write
 | 
				
			||||||
      contents: write
 | 
					      contents: write
 | 
				
			||||||
      attestations: write
 | 
					      attestations: write
 | 
				
			||||||
 | 
					      actions: read
 | 
				
			||||||
    runs-on: macos-26
 | 
					    runs-on: macos-26
 | 
				
			||||||
    timeout-minutes: 10
 | 
					    timeout-minutes: 10
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
@ -58,33 +59,40 @@ jobs:
 | 
				
			|||||||
            sed -i '' -e "s/GITHUB_BUILD_URL/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Config/Config.xcconfig
 | 
					            sed -i '' -e "s/GITHUB_BUILD_URL/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Config/Config.xcconfig
 | 
				
			||||||
    - name: Build
 | 
					    - name: Build
 | 
				
			||||||
      run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive archive
 | 
					      run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive archive
 | 
				
			||||||
    - name: Create ZIP
 | 
					    - name: Move to Artifact Folder
 | 
				
			||||||
      run: |
 | 
					      run: mkdir Artifact; cp -r Archive.xcarchive/Products/Applications/Secretive.app Artifact
 | 
				
			||||||
            ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive/Products/Applications/Secretive.app ./Secretive.zip
 | 
					 | 
				
			||||||
    - name: Notarize
 | 
					 | 
				
			||||||
      env: 
 | 
					 | 
				
			||||||
        APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
 | 
					 | 
				
			||||||
        APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
 | 
					 | 
				
			||||||
      run: xcrun notarytool submit --key ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip
 | 
					 | 
				
			||||||
    - name: Upload App to Artifacts
 | 
					    - name: Upload App to Artifacts
 | 
				
			||||||
      id: upload
 | 
					      id: upload
 | 
				
			||||||
      uses: actions/upload-artifact@v4
 | 
					      uses: actions/upload-artifact@v4
 | 
				
			||||||
      with:
 | 
					      with:
 | 
				
			||||||
        name: Secretive.zip
 | 
					        name: Secretive.zip
 | 
				
			||||||
        path: Secretive.zip
 | 
					        path: Artifact
 | 
				
			||||||
 | 
					    - name: Download Zipped Artifact
 | 
				
			||||||
 | 
					      id: download
 | 
				
			||||||
 | 
					      env: 
 | 
				
			||||||
 | 
					        ZIP_ID: ${{ steps.upload.outputs.artifact-id }}
 | 
				
			||||||
 | 
					        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					            curl -L -H "Authorization: Bearer $GITHUB_TOKEN" -L \
 | 
				
			||||||
 | 
					            https://api.github.com/repos/maxgoedjen/secretive/actions/artifacts/$ZIP_ID/zip > Secretive.zip
 | 
				
			||||||
 | 
					    - name: Notarize
 | 
				
			||||||
 | 
					      env: 
 | 
				
			||||||
 | 
					        APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
 | 
				
			||||||
 | 
					        APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
 | 
				
			||||||
 | 
					      run: xcrun notarytool submit --key ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip
 | 
				
			||||||
    - name: Attest
 | 
					    - name: Attest
 | 
				
			||||||
      id: attest
 | 
					      id: attest
 | 
				
			||||||
      uses: actions/attest-build-provenance@v2
 | 
					      uses: actions/attest-build-provenance@v2
 | 
				
			||||||
      with:
 | 
					      with:
 | 
				
			||||||
        subject-path: "Secretive.zip"
 | 
					        subject-path: "Secretive.zip"
 | 
				
			||||||
    - name: Create Release
 | 
					    - name: Create Release
 | 
				
			||||||
      run: |
 | 
					 | 
				
			||||||
            sed -i.tmp "s/RUN_ID/$RUN_ID/g" .github/templates/release.md
 | 
					 | 
				
			||||||
            sed -i.tmp "s/ATTESTATION_ID/$ATTESTATION_ID/g" .github/templates/release.md
 | 
					 | 
				
			||||||
            gh release create $TAG_NAME -d -F .github/templates/release.md
 | 
					 | 
				
			||||||
            gh release upload $TAG_NAME Secretive.zip
 | 
					 | 
				
			||||||
      env:
 | 
					      env:
 | 
				
			||||||
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
					        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
				
			||||||
        TAG_NAME: ${{ github.ref }}
 | 
					        TAG_NAME: ${{ github.ref }}
 | 
				
			||||||
        RUN_ID: ${{ github.run_id }}
 | 
					        RUN_ID: ${{ github.run_id }}
 | 
				
			||||||
        ATTESTATION_ID: ${{ steps.attest.outputs.attestation-id }}
 | 
					        ATTESTATION_ID: ${{ steps.attest.outputs.attestation-id }}
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					            sed -i.tmp "s/RUN_ID/$RUN_ID/g" .github/templates/release.md
 | 
				
			||||||
 | 
					            sed -i.tmp "s/ATTESTATION_ID/$ATTESTATION_ID/g" .github/templates/release.md
 | 
				
			||||||
 | 
					            gh release create $TAG_NAME -d -F .github/templates/release.md
 | 
				
			||||||
 | 
					            gh release upload $TAG_NAME Secretive.zip
 | 
				
			||||||
 | 
				
			|||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -47,6 +47,7 @@ extension Agent {
 | 
				
			|||||||
                logger.debug("Agent returned \(SSHAgent.Response.agentSignResponse.debugDescription)")
 | 
					                logger.debug("Agent returned \(SSHAgent.Response.agentSignResponse.debugDescription)")
 | 
				
			||||||
            case .unknown(let value):
 | 
					            case .unknown(let value):
 | 
				
			||||||
                logger.error("Agent received unknown request of type \(value).")
 | 
					                logger.error("Agent received unknown request of type \(value).")
 | 
				
			||||||
 | 
					                throw UnhandledRequestError()
 | 
				
			||||||
            default:
 | 
					            default:
 | 
				
			||||||
                logger.debug("Agent received valid request of type \(request.debugDescription), but not currently supported.")
 | 
					                logger.debug("Agent received valid request of type \(request.debugDescription), but not currently supported.")
 | 
				
			||||||
                throw UnhandledRequestError()
 | 
					                throw UnhandledRequestError()
 | 
				
			||||||
 | 
				
			|||||||
@ -32,7 +32,6 @@
 | 
				
			|||||||
		504788F62E68206F00B4556F /* GettingStartedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F52E68206F00B4556F /* GettingStartedView.swift */; };
 | 
							504788F62E68206F00B4556F /* GettingStartedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504788F52E68206F00B4556F /* GettingStartedView.swift */; };
 | 
				
			||||||
		504789232E697DD300B4556F /* BoxBackgroundStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504789222E697DD300B4556F /* BoxBackgroundStyle.swift */; };
 | 
							504789232E697DD300B4556F /* BoxBackgroundStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504789222E697DD300B4556F /* BoxBackgroundStyle.swift */; };
 | 
				
			||||||
		50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */; };
 | 
							50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */; };
 | 
				
			||||||
		50571E0524393D1500F76F6C /* LaunchAgentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0424393D1500F76F6C /* LaunchAgentController.swift */; };
 | 
					 | 
				
			||||||
		50617D8323FCE48E0099B055 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8223FCE48E0099B055 /* App.swift */; };
 | 
							50617D8323FCE48E0099B055 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8223FCE48E0099B055 /* App.swift */; };
 | 
				
			||||||
		50617D8523FCE48E0099B055 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8423FCE48E0099B055 /* ContentView.swift */; };
 | 
							50617D8523FCE48E0099B055 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8423FCE48E0099B055 /* ContentView.swift */; };
 | 
				
			||||||
		50617D8A23FCE48E0099B055 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8923FCE48E0099B055 /* Preview Assets.xcassets */; };
 | 
							50617D8A23FCE48E0099B055 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8923FCE48E0099B055 /* Preview Assets.xcassets */; };
 | 
				
			||||||
@ -194,7 +193,6 @@
 | 
				
			|||||||
		504788F52E68206F00B4556F /* GettingStartedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GettingStartedView.swift; sourceTree = "<group>"; };
 | 
							504788F52E68206F00B4556F /* GettingStartedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GettingStartedView.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		504789222E697DD300B4556F /* BoxBackgroundStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxBackgroundStyle.swift; sourceTree = "<group>"; };
 | 
							504789222E697DD300B4556F /* BoxBackgroundStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxBackgroundStyle.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustUpdatedChecker.swift; sourceTree = "<group>"; };
 | 
							50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustUpdatedChecker.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		50571E0424393D1500F76F6C /* LaunchAgentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAgentController.swift; sourceTree = "<group>"; };
 | 
					 | 
				
			||||||
		5059933F2E7A3B5B0092CFFA /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/Main.storyboard; sourceTree = "<group>"; };
 | 
							5059933F2E7A3B5B0092CFFA /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/Main.storyboard; sourceTree = "<group>"; };
 | 
				
			||||||
		50617D7F23FCE48E0099B055 /* Secretive.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Secretive.app; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
							50617D7F23FCE48E0099B055 /* Secretive.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Secretive.app; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
				
			||||||
		50617D8223FCE48E0099B055 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
 | 
							50617D8223FCE48E0099B055 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
 | 
				
			||||||
@ -448,7 +446,6 @@
 | 
				
			|||||||
				508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */,
 | 
									508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */,
 | 
				
			||||||
				5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */,
 | 
									5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */,
 | 
				
			||||||
				50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */,
 | 
									50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */,
 | 
				
			||||||
				50571E0424393D1500F76F6C /* LaunchAgentController.swift */,
 | 
					 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
			path = Controllers;
 | 
								path = Controllers;
 | 
				
			||||||
			sourceTree = "<group>";
 | 
								sourceTree = "<group>";
 | 
				
			||||||
@ -707,7 +704,6 @@
 | 
				
			|||||||
				5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */,
 | 
									5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */,
 | 
				
			||||||
				50AE97002E5C1A420018C710 /* IntegrationsView.swift in Sources */,
 | 
									50AE97002E5C1A420018C710 /* IntegrationsView.swift in Sources */,
 | 
				
			||||||
				50153E20250AFCB200525160 /* UpdateView.swift in Sources */,
 | 
									50153E20250AFCB200525160 /* UpdateView.swift in Sources */,
 | 
				
			||||||
				50571E0524393D1500F76F6C /* LaunchAgentController.swift in Sources */,
 | 
					 | 
				
			||||||
				5066A6C82516FE6E004B5A36 /* CopyableView.swift in Sources */,
 | 
									5066A6C82516FE6E004B5A36 /* CopyableView.swift in Sources */,
 | 
				
			||||||
				50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */,
 | 
									50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */,
 | 
				
			||||||
				50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */,
 | 
									50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */,
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ import Brief
 | 
				
			|||||||
@main
 | 
					@main
 | 
				
			||||||
struct Secretive: App {
 | 
					struct Secretive: App {
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    @Environment(\.agentStatusChecker) var agentStatusChecker
 | 
					    @Environment(\.agentLaunchController) var agentLaunchController
 | 
				
			||||||
    @Environment(\.justUpdatedChecker) var justUpdatedChecker
 | 
					    @Environment(\.justUpdatedChecker) var justUpdatedChecker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SceneBuilder var body: some Scene {
 | 
					    @SceneBuilder var body: some Scene {
 | 
				
			||||||
@ -15,14 +15,16 @@ struct Secretive: App {
 | 
				
			|||||||
            ContentView()
 | 
					            ContentView()
 | 
				
			||||||
                .environment(EnvironmentValues._secretStoreList)
 | 
					                .environment(EnvironmentValues._secretStoreList)
 | 
				
			||||||
                .onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in
 | 
					                .onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in
 | 
				
			||||||
                    @AppStorage("defaultsHasRunSetup") var hasRunSetup = false
 | 
					                    Task {
 | 
				
			||||||
                    guard hasRunSetup else { return }
 | 
					                        @AppStorage("defaultsHasRunSetup") var hasRunSetup = false
 | 
				
			||||||
                    agentStatusChecker.check()
 | 
					                        @AppStorage("explicitlyDisabled") var explicitlyDisabled = false
 | 
				
			||||||
                    if agentStatusChecker.running && justUpdatedChecker.justUpdatedBuild {
 | 
					                        guard hasRunSetup && !explicitlyDisabled else { return }
 | 
				
			||||||
                        // Relaunch the agent, since it'll be running from earlier update still
 | 
					                        agentLaunchController.check()
 | 
				
			||||||
                        reinstallAgent()
 | 
					                        guard !agentLaunchController.developmentBuild else { return }
 | 
				
			||||||
                    } else if !agentStatusChecker.running && !agentStatusChecker.developmentBuild {
 | 
					                        if justUpdatedChecker.justUpdatedBuild || !agentLaunchController.running {
 | 
				
			||||||
                        forceLaunchAgent()
 | 
					                            // Relaunch the agent, since it'll be running from earlier update still
 | 
				
			||||||
 | 
					                            try await agentLaunchController.forceLaunch()
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -79,30 +81,6 @@ extension Secretive {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extension Secretive {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private func reinstallAgent() {
 | 
					 | 
				
			||||||
        Task {
 | 
					 | 
				
			||||||
            _ = await LaunchAgentController().install()
 | 
					 | 
				
			||||||
            try? await Task.sleep(for: .seconds(1))
 | 
					 | 
				
			||||||
            agentStatusChecker.check()
 | 
					 | 
				
			||||||
            if !agentStatusChecker.running {
 | 
					 | 
				
			||||||
                forceLaunchAgent()
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private func forceLaunchAgent() {
 | 
					 | 
				
			||||||
        // We've run setup, we didn't just update, launchd is just not doing it's thing.
 | 
					 | 
				
			||||||
        // Force a launch directly.
 | 
					 | 
				
			||||||
        Task {
 | 
					 | 
				
			||||||
            _ = await LaunchAgentController().forceLaunch()
 | 
					 | 
				
			||||||
            agentStatusChecker.check()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private enum Constants {
 | 
					private enum Constants {
 | 
				
			||||||
    static let helpURL = URL(string: "https://github.com/maxgoedjen/secretive/blob/main/FAQ.md")!
 | 
					    static let helpURL = URL(string: "https://github.com/maxgoedjen/secretive/blob/main/FAQ.md")!
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -121,8 +99,8 @@ extension EnvironmentValues {
 | 
				
			|||||||
        return list
 | 
					        return list
 | 
				
			||||||
    }()
 | 
					    }()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static let _agentStatusChecker = AgentStatusChecker()
 | 
					    private static let _agentLaunchController = AgentLaunchController()
 | 
				
			||||||
    @Entry var agentStatusChecker: any AgentStatusCheckerProtocol = _agentStatusChecker
 | 
					    @Entry var agentLaunchController: any AgentLaunchControllerProtocol = _agentLaunchController
 | 
				
			||||||
    private static let _updater: any UpdaterProtocol = {
 | 
					    private static let _updater: any UpdaterProtocol = {
 | 
				
			||||||
        @AppStorage("defaultsHasRunSetup") var hasRunSetup = false
 | 
					        @AppStorage("defaultsHasRunSetup") var hasRunSetup = false
 | 
				
			||||||
        return Updater(checkOnLaunch: hasRunSetup)
 | 
					        return Updater(checkOnLaunch: hasRunSetup)
 | 
				
			||||||
 | 
				
			|||||||
@ -2,18 +2,25 @@ import Foundation
 | 
				
			|||||||
import AppKit
 | 
					import AppKit
 | 
				
			||||||
import SecretKit
 | 
					import SecretKit
 | 
				
			||||||
import Observation
 | 
					import Observation
 | 
				
			||||||
 | 
					import OSLog
 | 
				
			||||||
 | 
					import ServiceManagement
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@MainActor protocol AgentStatusCheckerProtocol: Observable, Sendable {
 | 
					@MainActor protocol AgentLaunchControllerProtocol: Observable, Sendable {
 | 
				
			||||||
    var running: Bool { get }
 | 
					    var running: Bool { get }
 | 
				
			||||||
    var developmentBuild: Bool { get }
 | 
					    var developmentBuild: Bool { get }
 | 
				
			||||||
    var process: NSRunningApplication? { get }
 | 
					    var process: NSRunningApplication? { get }
 | 
				
			||||||
    func check()
 | 
					    func check()
 | 
				
			||||||
 | 
					    func install() async throws
 | 
				
			||||||
 | 
					    func uninstall() async throws
 | 
				
			||||||
 | 
					    func forceLaunch() async throws
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Observable @MainActor final class AgentStatusChecker: AgentStatusCheckerProtocol {
 | 
					@Observable @MainActor final class AgentLaunchController: AgentLaunchControllerProtocol {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var running: Bool = false
 | 
					    var running: Bool = false
 | 
				
			||||||
    var process: NSRunningApplication? = nil
 | 
					    var process: NSRunningApplication? = nil
 | 
				
			||||||
 | 
					    private let logger = Logger(subsystem: "com.maxgoedjen.secretive", category: "LaunchAgentController")
 | 
				
			||||||
 | 
					    private let service = SMAppService.loginItem(identifier: Bundle.agentBundleID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    nonisolated init() {
 | 
					    nonisolated init() {
 | 
				
			||||||
        Task { @MainActor in
 | 
					        Task { @MainActor in
 | 
				
			||||||
@ -33,7 +40,7 @@ import Observation
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // The process corresponding to this instance of Secretive
 | 
					    // The process corresponding to this instance of Secretive
 | 
				
			||||||
    var instanceSecretAgentProcess: NSRunningApplication? {
 | 
					    var instanceSecretAgentProcess: NSRunningApplication? {
 | 
				
			||||||
        // FIXME: CHECK VERSION
 | 
					        // TODO: CHECK VERSION
 | 
				
			||||||
        let agents = allSecretAgentProcesses
 | 
					        let agents = allSecretAgentProcesses
 | 
				
			||||||
        for agent in agents {
 | 
					        for agent in agents {
 | 
				
			||||||
            guard let url = agent.bundleURL else { continue }
 | 
					            guard let url = agent.bundleURL else { continue }
 | 
				
			||||||
@ -49,6 +56,47 @@ import Observation
 | 
				
			|||||||
        Bundle.main.bundleURL.isXcodeURL
 | 
					        Bundle.main.bundleURL.isXcodeURL
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func install() async throws {
 | 
				
			||||||
 | 
					        logger.debug("Installing agent")
 | 
				
			||||||
 | 
					        try? await service.unregister()
 | 
				
			||||||
 | 
					        // This is definitely a bit of a "seems to work better" thing but:
 | 
				
			||||||
 | 
					        // Seems to more reliably hit if these are on separate runloops, otherwise it seems like it sometimes doesn't kill old
 | 
				
			||||||
 | 
					        // and start new?
 | 
				
			||||||
 | 
					        try await Task.sleep(for: .seconds(1))
 | 
				
			||||||
 | 
					        try service.register()
 | 
				
			||||||
 | 
					        try await Task.sleep(for: .seconds(1))
 | 
				
			||||||
 | 
					        check()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func uninstall() async throws {
 | 
				
			||||||
 | 
					        logger.debug("Uninstalling agent")
 | 
				
			||||||
 | 
					        try await Task.sleep(for: .seconds(1))
 | 
				
			||||||
 | 
					        try await service.unregister()
 | 
				
			||||||
 | 
					        try await Task.sleep(for: .seconds(1))
 | 
				
			||||||
 | 
					        check()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func forceLaunch() async throws {
 | 
				
			||||||
 | 
					        logger.debug("Agent is not running, attempting to force launch by reinstalling")
 | 
				
			||||||
 | 
					        try await install()
 | 
				
			||||||
 | 
					        if running {
 | 
				
			||||||
 | 
					            logger.debug("Agent successfully force launched by reinstalling")
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        logger.debug("Agent is not running, attempting to force launch by launching directly")
 | 
				
			||||||
 | 
					        let url = Bundle.main.bundleURL.appendingPathComponent("Contents/Library/LoginItems/SecretAgent.app")
 | 
				
			||||||
 | 
					        let config = NSWorkspace.OpenConfiguration()
 | 
				
			||||||
 | 
					        config.activates = false
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            try await NSWorkspace.shared.openApplication(at: url, configuration: config)
 | 
				
			||||||
 | 
					            logger.debug("Agent force launched")
 | 
				
			||||||
 | 
					            try await Task.sleep(for: .seconds(1))
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					            logger.error("Error force launching \(error.localizedDescription)")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        check()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extension URL {
 | 
					extension URL {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,65 +0,0 @@
 | 
				
			|||||||
import Foundation
 | 
					 | 
				
			||||||
import ServiceManagement
 | 
					 | 
				
			||||||
import AppKit
 | 
					 | 
				
			||||||
import OSLog
 | 
					 | 
				
			||||||
import SecretKit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct LaunchAgentController {
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    private let logger = Logger(subsystem: "com.maxgoedjen.secretive", category: "LaunchAgentController")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    func install() async -> Bool {
 | 
					 | 
				
			||||||
        logger.debug("Installing agent")
 | 
					 | 
				
			||||||
        _ = setEnabled(false)
 | 
					 | 
				
			||||||
        // This is definitely a bit of a "seems to work better" thing but:
 | 
					 | 
				
			||||||
        // Seems to more reliably hit if these are on separate runloops, otherwise it seems like it sometimes doesn't kill old
 | 
					 | 
				
			||||||
        // and start new?
 | 
					 | 
				
			||||||
        try? await Task.sleep(for: .seconds(1))
 | 
					 | 
				
			||||||
        let result = await MainActor.run {
 | 
					 | 
				
			||||||
            setEnabled(true)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        try? await Task.sleep(for: .seconds(1))
 | 
					 | 
				
			||||||
        return result
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    func uninstall() async -> Bool {
 | 
					 | 
				
			||||||
        logger.debug("Uninstalling agent")
 | 
					 | 
				
			||||||
        try? await Task.sleep(for: .seconds(1))
 | 
					 | 
				
			||||||
        let result = await MainActor.run {
 | 
					 | 
				
			||||||
            setEnabled(false)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        try? await Task.sleep(for: .seconds(1))
 | 
					 | 
				
			||||||
        return result
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    func forceLaunch() async -> Bool {
 | 
					 | 
				
			||||||
        logger.debug("Agent is not running, attempting to force launch")
 | 
					 | 
				
			||||||
        let url = Bundle.main.bundleURL.appendingPathComponent("Contents/Library/LoginItems/SecretAgent.app")
 | 
					 | 
				
			||||||
        let config = NSWorkspace.OpenConfiguration()
 | 
					 | 
				
			||||||
        config.activates = false
 | 
					 | 
				
			||||||
        do {
 | 
					 | 
				
			||||||
            try await NSWorkspace.shared.openApplication(at: url, configuration: config)
 | 
					 | 
				
			||||||
            logger.debug("Agent force launched")
 | 
					 | 
				
			||||||
            try? await Task.sleep(for: .seconds(1))
 | 
					 | 
				
			||||||
            return true
 | 
					 | 
				
			||||||
        } catch {
 | 
					 | 
				
			||||||
            logger.error("Error force launching \(error.localizedDescription)")
 | 
					 | 
				
			||||||
            return false
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private func setEnabled(_ enabled: Bool) -> Bool {
 | 
					 | 
				
			||||||
        let service = SMAppService.loginItem(identifier: Bundle.agentBundleID)
 | 
					 | 
				
			||||||
        do {
 | 
					 | 
				
			||||||
            if enabled {
 | 
					 | 
				
			||||||
                try service.register()
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                try service.unregister()
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return true
 | 
					 | 
				
			||||||
        } catch {
 | 
					 | 
				
			||||||
            return false
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import Foundation
 | 
					import Foundation
 | 
				
			||||||
import AppKit
 | 
					import AppKit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PreviewAgentStatusChecker: AgentStatusCheckerProtocol {
 | 
					class PreviewAgentLaunchController: AgentLaunchControllerProtocol {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let running: Bool
 | 
					    let running: Bool
 | 
				
			||||||
    let process: NSRunningApplication?
 | 
					    let process: NSRunningApplication?
 | 
				
			||||||
@ -15,4 +15,13 @@ class PreviewAgentStatusChecker: AgentStatusCheckerProtocol {
 | 
				
			|||||||
    func check() {
 | 
					    func check() {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func install() async throws {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func uninstall() async throws {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func forceLaunch() async throws {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@ import SwiftUI
 | 
				
			|||||||
struct SetupView: View {
 | 
					struct SetupView: View {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Environment(\.dismiss) private var dismiss
 | 
					    @Environment(\.dismiss) private var dismiss
 | 
				
			||||||
 | 
					    @Environment(\.agentLaunchController) private var agentLaunchController
 | 
				
			||||||
    @Binding var setupComplete: Bool
 | 
					    @Binding var setupComplete: Bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @State var showingIntegrations = false
 | 
					    @State var showingIntegrations = false
 | 
				
			||||||
@ -31,7 +32,7 @@ struct SetupView: View {
 | 
				
			|||||||
                    ) {
 | 
					                    ) {
 | 
				
			||||||
                        installed = true
 | 
					                        installed = true
 | 
				
			||||||
                        Task {
 | 
					                        Task {
 | 
				
			||||||
                            await LaunchAgentController().install()
 | 
					                            try? await agentLaunchController.install()
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,7 @@ struct ToolConfigurationView: View {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @State var creating = false
 | 
					    @State var creating = false
 | 
				
			||||||
    @State var selectedSecret: AnySecret?
 | 
					    @State var selectedSecret: AnySecret?
 | 
				
			||||||
 | 
					    @State var email = ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    init(selectedInstruction: ConfigurationFileInstructions) {
 | 
					    init(selectedInstruction: ConfigurationFileInstructions) {
 | 
				
			||||||
        self.selectedInstruction = selectedInstruction
 | 
					        self.selectedInstruction = selectedInstruction
 | 
				
			||||||
@ -48,6 +49,12 @@ struct ToolConfigurationView: View {
 | 
				
			|||||||
                                    .tag(secret)
 | 
					                                    .tag(secret)
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					                        TextField(text: $email, prompt: Text(.integrationsConfigureUsingEmailPlaceholder)) {
 | 
				
			||||||
 | 
					                            Text(.integrationsConfigureUsingEmailTitle)
 | 
				
			||||||
 | 
					                            Text(.integrationsConfigureUsingEmailSubtitle)
 | 
				
			||||||
 | 
					                                .font(.subheadline)
 | 
				
			||||||
 | 
					                                .foregroundStyle(.secondary)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    } header: {
 | 
					                    } header: {
 | 
				
			||||||
                        Text(.integrationsConfigureUsingSecretHeader)
 | 
					                        Text(.integrationsConfigureUsingSecretHeader)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -60,7 +67,7 @@ struct ToolConfigurationView: View {
 | 
				
			|||||||
                Section {
 | 
					                Section {
 | 
				
			||||||
                    ConfigurationItemView(title: .integrationsPathTitle, value: stepGroup.path, action: .revealInFinder(stepGroup.path))
 | 
					                    ConfigurationItemView(title: .integrationsPathTitle, value: stepGroup.path, action: .revealInFinder(stepGroup.path))
 | 
				
			||||||
                    ForEach(stepGroup.steps, id: \.self.key) { step in
 | 
					                    ForEach(stepGroup.steps, id: \.self.key) { step in
 | 
				
			||||||
                        ConfigurationItemView(title: .integrationsAddThisTitle, action: .copy(String(localized: step))) {
 | 
					                        ConfigurationItemView(title: .integrationsAddThisTitle, action: .copy(placeholdersReplaced(text: String(localized: step)))) {
 | 
				
			||||||
                            HStack {
 | 
					                            HStack {
 | 
				
			||||||
                                Text(placeholdersReplaced(text: String(localized: step)))
 | 
					                                Text(placeholdersReplaced(text: String(localized: step)))
 | 
				
			||||||
                                    .padding(8)
 | 
					                                    .padding(8)
 | 
				
			||||||
@ -102,9 +109,11 @@ struct ToolConfigurationView: View {
 | 
				
			|||||||
    func placeholdersReplaced(text: String) -> String {
 | 
					    func placeholdersReplaced(text: String) -> String {
 | 
				
			||||||
        guard let selectedSecret else { return text }
 | 
					        guard let selectedSecret else { return text }
 | 
				
			||||||
        let writer = OpenSSHPublicKeyWriter()
 | 
					        let writer = OpenSSHPublicKeyWriter()
 | 
				
			||||||
 | 
					        let gitAllowedSignersString = [email.isEmpty ? String(localized: .integrationsConfigureUsingEmailPlaceholder) : email, writer.openSSHString(secret: selectedSecret)]
 | 
				
			||||||
 | 
					            .joined(separator: " ")
 | 
				
			||||||
        let fileController = PublicKeyFileStoreController(homeDirectory: URL.agentHomeURL)
 | 
					        let fileController = PublicKeyFileStoreController(homeDirectory: URL.agentHomeURL)
 | 
				
			||||||
        return text
 | 
					        return text
 | 
				
			||||||
            .replacingOccurrences(of: Instructions.Constants.publicKeyPlaceholder, with: writer.openSSHString(secret: selectedSecret))
 | 
					            .replacingOccurrences(of: Instructions.Constants.publicKeyPlaceholder, with: gitAllowedSignersString)
 | 
				
			||||||
            .replacingOccurrences(of: Instructions.Constants.publicKeyPathPlaceholder, with: fileController.publicKeyPath(for: selectedSecret))
 | 
					            .replacingOccurrences(of: Instructions.Constants.publicKeyPathPlaceholder, with: fileController.publicKeyPath(for: selectedSecret))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,10 +2,10 @@ import SwiftUI
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct AgentStatusView: View {
 | 
					struct AgentStatusView: View {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Environment(\.agentStatusChecker) private var agentStatusChecker: any AgentStatusCheckerProtocol
 | 
					    @Environment(\.agentLaunchController) private var agentLaunchController: any AgentLaunchControllerProtocol
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var body: some View {
 | 
					    var body: some View {
 | 
				
			||||||
        if agentStatusChecker.running {
 | 
					        if agentLaunchController.running {
 | 
				
			||||||
            AgentRunningView()
 | 
					            AgentRunningView()
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            AgentNotRunningView()
 | 
					            AgentNotRunningView()
 | 
				
			||||||
@ -14,12 +14,13 @@ struct AgentStatusView: View {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
struct AgentRunningView: View {
 | 
					struct AgentRunningView: View {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Environment(\.agentStatusChecker) private var agentStatusChecker: any AgentStatusCheckerProtocol
 | 
					    @Environment(\.agentLaunchController) private var agentLaunchController: any AgentLaunchControllerProtocol
 | 
				
			||||||
 | 
					    @AppStorage("explicitlyDisabled") var explicitlyDisabled = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var body: some View {
 | 
					    var body: some View {
 | 
				
			||||||
        Form {
 | 
					        Form {
 | 
				
			||||||
            Section {
 | 
					            Section {
 | 
				
			||||||
                if let process = agentStatusChecker.process {
 | 
					                if let process = agentLaunchController.process {
 | 
				
			||||||
                    ConfigurationItemView(
 | 
					                    ConfigurationItemView(
 | 
				
			||||||
                        title: .agentDetailsLocationTitle,
 | 
					                        title: .agentDetailsLocationTitle,
 | 
				
			||||||
                        value: process.bundleURL!.path(),
 | 
					                        value: process.bundleURL!.path(),
 | 
				
			||||||
@ -53,19 +54,14 @@ struct AgentRunningView: View {
 | 
				
			|||||||
                        Menu(.agentDetailsRestartAgentButton) {
 | 
					                        Menu(.agentDetailsRestartAgentButton) {
 | 
				
			||||||
                            Button(.agentDetailsDisableAgentButton) {
 | 
					                            Button(.agentDetailsDisableAgentButton) {
 | 
				
			||||||
                                Task {
 | 
					                                Task {
 | 
				
			||||||
                                    _ = await LaunchAgentController()
 | 
					                                    explicitlyDisabled = true
 | 
				
			||||||
 | 
					                                    try? await agentLaunchController
 | 
				
			||||||
                                        .uninstall()
 | 
					                                        .uninstall()
 | 
				
			||||||
                                    agentStatusChecker.check()
 | 
					 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        } primaryAction: {
 | 
					                        } primaryAction: {
 | 
				
			||||||
                            Task {
 | 
					                            Task {
 | 
				
			||||||
                                let controller = LaunchAgentController()
 | 
					                                try? await agentLaunchController.forceLaunch()
 | 
				
			||||||
                                let installed = await controller.install()
 | 
					 | 
				
			||||||
                                if !installed {
 | 
					 | 
				
			||||||
                                    _ = await controller.forceLaunch()
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                                agentStatusChecker.check()
 | 
					 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -82,9 +78,10 @@ struct AgentRunningView: View {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct AgentNotRunningView: View {
 | 
					struct AgentNotRunningView: View {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Environment(\.agentStatusChecker) private var agentStatusChecker: any AgentStatusCheckerProtocol
 | 
					    @Environment(\.agentLaunchController) private var agentLaunchController
 | 
				
			||||||
    @State var triedRestart = false
 | 
					    @State var triedRestart = false
 | 
				
			||||||
    @State var loading = false
 | 
					    @State var loading = false
 | 
				
			||||||
 | 
					    @AppStorage("explicitlyDisabled") var explicitlyDisabled = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var body: some View {
 | 
					    var body: some View {
 | 
				
			||||||
        Form {
 | 
					        Form {
 | 
				
			||||||
@ -100,18 +97,14 @@ struct AgentNotRunningView: View {
 | 
				
			|||||||
                        if !triedRestart {
 | 
					                        if !triedRestart {
 | 
				
			||||||
                            Spacer()
 | 
					                            Spacer()
 | 
				
			||||||
                            Button {
 | 
					                            Button {
 | 
				
			||||||
 | 
					                                explicitlyDisabled = false
 | 
				
			||||||
                                guard !loading else { return }
 | 
					                                guard !loading else { return }
 | 
				
			||||||
                                loading = true
 | 
					                                loading = true
 | 
				
			||||||
                                Task {
 | 
					                                Task {
 | 
				
			||||||
                                    let controller = LaunchAgentController()
 | 
					                                    try await agentLaunchController.forceLaunch()
 | 
				
			||||||
                                    let installed = await controller.install()
 | 
					 | 
				
			||||||
                                    if !installed {
 | 
					 | 
				
			||||||
                                        _ = await controller.forceLaunch()
 | 
					 | 
				
			||||||
                                    }
 | 
					 | 
				
			||||||
                                    agentStatusChecker.check()
 | 
					 | 
				
			||||||
                                    loading = false
 | 
					                                    loading = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                    if !agentStatusChecker.running {
 | 
					                                    if !agentLaunchController.running {
 | 
				
			||||||
                                        triedRestart = true
 | 
					                                        triedRestart = true
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
@ -145,9 +138,9 @@ struct AgentNotRunningView: View {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
//#Preview {
 | 
					//#Preview {
 | 
				
			||||||
//    AgentStatusView()
 | 
					//    AgentStatusView()
 | 
				
			||||||
//        .environment(\.agentStatusChecker, PreviewAgentStatusChecker(running: false))
 | 
					//        .environment(\.agentLaunchController, PreviewAgentLaunchController(running: false))
 | 
				
			||||||
//}
 | 
					//}
 | 
				
			||||||
//#Preview {
 | 
					//#Preview {
 | 
				
			||||||
//    AgentStatusView()
 | 
					//    AgentStatusView()
 | 
				
			||||||
//        .environment(\.agentStatusChecker, PreviewAgentStatusChecker(running: true, process: .current))
 | 
					//        .environment(\.agentLaunchController, PreviewAgentLaunchController(running: true, process: .current))
 | 
				
			||||||
//}
 | 
					//}
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,7 @@ struct ContentView: View {
 | 
				
			|||||||
    @Environment(\.openWindow) private var openWindow
 | 
					    @Environment(\.openWindow) private var openWindow
 | 
				
			||||||
    @Environment(\.secretStoreList) private var storeList
 | 
					    @Environment(\.secretStoreList) private var storeList
 | 
				
			||||||
    @Environment(\.updater) private var updater
 | 
					    @Environment(\.updater) private var updater
 | 
				
			||||||
    @Environment(\.agentStatusChecker) private var agentStatusChecker
 | 
					    @Environment(\.agentLaunchController) private var agentLaunchController
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @AppStorage("defaultsHasRunSetup") private var hasRunSetup = false
 | 
					    @AppStorage("defaultsHasRunSetup") private var hasRunSetup = false
 | 
				
			||||||
    @State private var showingCreation = false
 | 
					    @State private var showingCreation = false
 | 
				
			||||||
@ -127,7 +127,7 @@ extension ContentView {
 | 
				
			|||||||
            showingAgentInfo = true
 | 
					            showingAgentInfo = true
 | 
				
			||||||
        }, label: {
 | 
					        }, label: {
 | 
				
			||||||
            HStack {
 | 
					            HStack {
 | 
				
			||||||
                if agentStatusChecker.running {
 | 
					                if agentLaunchController.running {
 | 
				
			||||||
                    Text(.agentRunningNoticeTitle)
 | 
					                    Text(.agentRunningNoticeTitle)
 | 
				
			||||||
                        .font(.headline)
 | 
					                        .font(.headline)
 | 
				
			||||||
                        .foregroundColor(colorScheme == .light ? Color(white: 0.3) : .white)
 | 
					                        .foregroundColor(colorScheme == .light ? Color(white: 0.3) : .white)
 | 
				
			||||||
@ -145,8 +145,8 @@ extension ContentView {
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
        .buttonStyle(
 | 
					        .buttonStyle(
 | 
				
			||||||
            ToolbarStatusButtonStyle(
 | 
					            ToolbarStatusButtonStyle(
 | 
				
			||||||
                lightColor: agentStatusChecker.running ? .black.opacity(0.05) : .red.opacity(0.75),
 | 
					                lightColor: agentLaunchController.running ? .black.opacity(0.05) : .red.opacity(0.75),
 | 
				
			||||||
                darkColor: agentStatusChecker.running ? .white.opacity(0.05) : .red.opacity(0.5),
 | 
					                darkColor: agentLaunchController.running ? .white.opacity(0.05) : .red.opacity(0.5),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        .popover(isPresented: $showingAgentInfo, attachmentAnchor: attachmentAnchor, arrowEdge: .bottom) {
 | 
					        .popover(isPresented: $showingAgentInfo, attachmentAnchor: attachmentAnchor, arrowEdge: .bottom) {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user