@@ -6,7 +6,7 @@ import type { IProviderRegistry } from '../services/ui';
66import type { TelemetryManager } from '../services/telemetry' ;
77import type { ArtemisWebviewProvider , ChatWebviewProvider } from '../provider' ;
88import { logger , LogCategory } from '../services/loggingService' ;
9- import { processPlantUml , normalizeRelativePath , extractErrorMessage } from '../utils' ;
9+ import { processPlantUml , normalizeRelativePath , extractErrorMessage , VSCODE_CONFIG } from '../utils' ;
1010import { executeReplayCommand } from '../services/telemetry/replay' ;
1111
1212// ── Individual command registrations ─────────────────────────────────
@@ -19,11 +19,15 @@ function registerLoginCommand(): vscode.Disposable {
1919
2020function registerLogoutCommand (
2121 authManager : AuthManager ,
22+ artemisApiService : ArtemisApiService ,
2223 updateAuthContext : ( isAuthenticated : boolean ) => Promise < void > ,
2324 artemisWebviewProvider : ArtemisWebviewProvider ,
2425) : vscode . Disposable {
2526 return vscode . commands . registerCommand ( 'artemis.logout' , async ( ) => {
2627 try {
28+ // Best-effort server-side logout before clearing local state.
29+ // Never throws — local cleanup proceeds regardless.
30+ await artemisApiService . logoutFromServer ( ) ;
2731 await authManager . clear ( ) ;
2832 await updateAuthContext ( false ) ;
2933 vscode . window . showInformationMessage ( 'Successfully logged out of Artemis' ) ;
@@ -418,6 +422,50 @@ function registerReplaySessionCommand(globalStorageUri: vscode.Uri): vscode.Disp
418422 } ) ;
419423}
420424
425+ /**
426+ * Developer-only command: copy the current raw JWT to the clipboard for use
427+ * in curl/Postman based server testing. Gated on the `artemis.developerMode`
428+ * setting both at the menu level (commandPalette `when` clause) and at runtime
429+ * (defense-in-depth against direct invocation via `vscode.commands.executeCommand`).
430+ *
431+ * The full token is NEVER shown in the UI or written to logs — only a masked
432+ * preview appears in the notification, and the full value lands in the clipboard.
433+ */
434+ function registerShowJwtTokenCommand ( authManager : AuthManager ) : vscode . Disposable {
435+ return vscode . commands . registerCommand ( 'artemis.showJwtToken' , async ( ) => {
436+ const config = vscode . workspace . getConfiguration ( VSCODE_CONFIG . ARTEMIS_SECTION ) ;
437+ const developerMode = config . get < boolean > ( VSCODE_CONFIG . DEVELOPER_MODE_KEY , false ) ;
438+ if ( ! developerMode ) {
439+ vscode . window . showErrorMessage (
440+ `Enable '${ VSCODE_CONFIG . ARTEMIS_SECTION } .${ VSCODE_CONFIG . DEVELOPER_MODE_KEY } ' in settings to use this command.`
441+ ) ;
442+ return ;
443+ }
444+
445+ const hasToken = await authManager . hasAuthToken ( ) ;
446+ if ( ! hasToken ) {
447+ vscode . window . showInformationMessage ( 'Not logged in to Artemis — no JWT to show.' ) ;
448+ return ;
449+ }
450+
451+ const rawJwt = await authManager . getRawJwt ( ) ;
452+ if ( ! rawJwt ) {
453+ vscode . window . showErrorMessage ( 'Failed to retrieve JWT token from secret storage.' ) ;
454+ logger . error ( 'getRawJwt returned undefined despite hasAuthToken=true' , LogCategory . AUTH ) ;
455+ return ;
456+ }
457+
458+ await vscode . env . clipboard . writeText ( rawJwt ) ;
459+
460+ const preview = `${ rawJwt . substring ( 0 , 20 ) } ...` ;
461+ logger . info ( `JWT copied to clipboard via developer command (preview: ${ preview } )` , LogCategory . AUTH ) ;
462+
463+ vscode . window . showWarningMessage (
464+ `JWT copied to clipboard (${ preview } ). Do not share, do not commit.`
465+ ) ;
466+ } ) ;
467+ }
468+
421469// ── Aggregate registration ───────────────────────────────────────────
422470
423471export interface CommandDeps {
@@ -435,7 +483,7 @@ export interface CommandDeps {
435483export function registerAllCommands ( deps : CommandDeps ) : vscode . Disposable {
436484 return vscode . Disposable . from (
437485 registerLoginCommand ( ) ,
438- registerLogoutCommand ( deps . authManager , deps . updateAuthContext , deps . artemisWebviewProvider ) ,
486+ registerLogoutCommand ( deps . authManager , deps . artemisApiService , deps . updateAuthContext , deps . artemisWebviewProvider ) ,
439487 registerResetIrisChatCommand ( deps . chatWebviewProvider ) ,
440488 registerIrisHealthCheckCommand ( deps . authManager , deps . artemisApiService , deps . providerRegistry ) ,
441489 registerWebSocketStatusCommand ( deps . artemisWebsocketService ) ,
@@ -445,5 +493,6 @@ export function registerAllCommands(deps: CommandDeps): vscode.Disposable {
445493 registerClearTrustedDomainsCommand ( deps . context ) ,
446494 registerStruggleScoreCommand ( deps . telemetryManager ) ,
447495 registerReplaySessionCommand ( deps . context . globalStorageUri ) ,
496+ registerShowJwtTokenCommand ( deps . authManager ) ,
448497 ) ;
449498}
0 commit comments