From 32fbb2d0154326e4f164de8dc49596803ac0ff02 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 00:48:27 +0000 Subject: [PATCH 1/3] Initial plan From 8852185e08b893d746b9e68a2e6587a5363557cb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 00:55:37 +0000 Subject: [PATCH 2/3] Preserve Clerk session between login window opens Agent-Logs-Url: https://github.com/highperformancecoder/minsky/sessions/fee97c92-7978-4c1d-8195-ace7a1267507 Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com> --- .../src/app/managers/WindowManager.ts | 4 ++- .../src/lib/services/clerk/clerk.service.ts | 10 ++++++ .../src/lib/login/login.component.ts | 33 ++++++++++++++++--- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/gui-js/apps/minsky-electron/src/app/managers/WindowManager.ts b/gui-js/apps/minsky-electron/src/app/managers/WindowManager.ts index c7a1bdb89..a95f898dd 100644 --- a/gui-js/apps/minsky-electron/src/app/managers/WindowManager.ts +++ b/gui-js/apps/minsky-electron/src/app/managers/WindowManager.ts @@ -389,12 +389,14 @@ export class WindowManager { WindowManager._resolveAuthToken = resolve; }); + const existingToken = StoreManager.store.get('authToken') || ''; + const loginWindow = WindowManager.createPopupWindowWithRouting({ width: 420, height: 500, title: 'Login', modal: false, - url: '#/headless/login', + url: `#/headless/login?authToken=${encodeURIComponent(existingToken)}`, }); // Resolve with null if the user closes the window before authenticating diff --git a/gui-js/libs/core/src/lib/services/clerk/clerk.service.ts b/gui-js/libs/core/src/lib/services/clerk/clerk.service.ts index e7544829c..bcf229b34 100644 --- a/gui-js/libs/core/src/lib/services/clerk/clerk.service.ts +++ b/gui-js/libs/core/src/lib/services/clerk/clerk.service.ts @@ -59,4 +59,14 @@ export class ClerkService { const token = await this.getToken(); await this.electronService.invoke(events.SET_AUTH_TOKEN, token); } + + async setSession(_token: string): Promise { + if (!this.clerk) throw new Error('Clerk not initialized'); + // The Clerk session is restored from browser storage by clerk.load() in initialize(). + // We verify it is still active here; if not, the caller should prompt the user to sign in. + const freshToken = await this.clerk.session?.getToken(); + if (!freshToken) { + throw new Error('Session expired or invalid'); + } + } } diff --git a/gui-js/libs/ui-components/src/lib/login/login.component.ts b/gui-js/libs/ui-components/src/lib/login/login.component.ts index ecb275383..f9109f21e 100644 --- a/gui-js/libs/ui-components/src/lib/login/login.component.ts +++ b/gui-js/libs/ui-components/src/lib/login/login.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, OnDestroy } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormControl, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; @@ -7,6 +7,8 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { ClerkService } from '@minsky/core'; import { ElectronService } from '@minsky/core'; +import { ActivatedRoute } from '@angular/router'; +import { Subject, take } from 'rxjs'; @Component({ selector: 'minsky-login', @@ -22,7 +24,7 @@ import { ElectronService } from '@minsky/core'; MatProgressSpinnerModule, ], }) -export class LoginComponent implements OnInit { +export class LoginComponent implements OnInit, OnDestroy { loginForm = new FormGroup({ email: new FormControl('', [Validators.required, Validators.email]), password: new FormControl('', [Validators.required]), @@ -32,17 +34,40 @@ export class LoginComponent implements OnInit { errorMessage = ''; isAuthenticated = false; - constructor(private clerkService: ClerkService, private electronService: ElectronService) {} + private destroy$ = new Subject(); + + constructor( + private clerkService: ClerkService, + private electronService: ElectronService, + private route: ActivatedRoute + ) {} async ngOnInit() { + this.route.queryParams.pipe(take(1)).subscribe((params) => { + this.initializeSession(params['authToken']); + }); + } + + private async initializeSession(authToken: string | undefined) { try { await this.clerkService.initialize(); + + if (authToken) { + await this.clerkService.setSession(authToken); + } + this.isAuthenticated = await this.clerkService.isSignedIn(); } catch (err) { - this.errorMessage = 'Failed to initialize authentication.'; + this.errorMessage = 'Session expired. Please sign in again.'; + this.isAuthenticated = false; } } + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + get email() { return this.loginForm.get('email'); } From 7d51337a3472dc9eb6453e1266bd123db9fe526e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 01:26:43 +0000 Subject: [PATCH 3/3] Fix setSession to actually reinstate the Clerk session Agent-Logs-Url: https://github.com/highperformancecoder/minsky/sessions/198fce2f-6377-4ea0-a84d-1c4fe6545b1b Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com> --- .../core/src/lib/services/clerk/clerk.service.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/gui-js/libs/core/src/lib/services/clerk/clerk.service.ts b/gui-js/libs/core/src/lib/services/clerk/clerk.service.ts index bcf229b34..e79e5d9ab 100644 --- a/gui-js/libs/core/src/lib/services/clerk/clerk.service.ts +++ b/gui-js/libs/core/src/lib/services/clerk/clerk.service.ts @@ -60,12 +60,16 @@ export class ClerkService { await this.electronService.invoke(events.SET_AUTH_TOKEN, token); } - async setSession(_token: string): Promise { + async setSession(token: string): Promise { if (!this.clerk) throw new Error('Clerk not initialized'); - // The Clerk session is restored from browser storage by clerk.load() in initialize(). - // We verify it is still active here; if not, the caller should prompt the user to sign in. - const freshToken = await this.clerk.session?.getToken(); - if (!freshToken) { + // clerk.load() in initialize() restores the session from browser storage if still valid. + // If no session is active but sessions exist on the client, activate the first available one. + // The token parameter is a hint that the user previously authenticated. + if (!token) return; + if (!this.clerk.session && this.clerk.client?.sessions?.length > 0) { + await this.clerk.setActive({ session: this.clerk.client.sessions[0].id }); + } + if (!this.clerk.session) { throw new Error('Session expired or invalid'); } }