diff --git a/src/commands.ts b/src/commands.ts index 19daaa57..b0024316 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -86,7 +86,7 @@ export class Commands { if (this.deploymentManager.isAuthenticated()) { return; } - this.logger.info("Logging in"); + this.logger.debug("Logging in"); const currentDeployment = await this.secretsManager.getCurrentDeployment(); const url = await maybeAskUrl( @@ -205,7 +205,7 @@ export class Commands { return; } - this.logger.info("Logging out"); + this.logger.debug("Logging out"); await this.deploymentManager.clearDeployment(); diff --git a/src/deployment/deploymentManager.ts b/src/deployment/deploymentManager.ts index 8bb34629..4922d5a3 100644 --- a/src/deployment/deploymentManager.ts +++ b/src/deployment/deploymentManager.ts @@ -7,7 +7,11 @@ import { type Logger } from "../logging/logger"; import { type OAuthSessionManager } from "../oauth/sessionManager"; import { type WorkspaceProvider } from "../workspace/workspacesProvider"; -import { type Deployment, type DeploymentWithAuth } from "./types"; +import { + DeploymentSchema, + type Deployment, + type DeploymentWithAuth, +} from "./types"; import type { User } from "coder/site/src/api/typesGenerated"; import type * as vscode from "vscode"; @@ -115,6 +119,10 @@ export class DeploymentManager implements vscode.Disposable { public async setDeployment( deployment: DeploymentWithAuth & { user: User }, ): Promise { + this.logger.debug("Setting deployment", { + hostname: deployment.safeHostname, + user: deployment.user.username, + }); this.#deployment = { ...deployment }; // Updates client credentials @@ -131,14 +139,17 @@ export class DeploymentManager implements vscode.Disposable { this.updateAuthContexts(deployment.user); this.refreshWorkspaces(); - await this.oauthSessionManager.setDeployment(deployment); - await this.persistDeployment(deployment); + const deploymentWithoutAuth: Deployment = + DeploymentSchema.parse(deployment); + await this.oauthSessionManager.setDeployment(deploymentWithoutAuth); + await this.persistDeployment(deploymentWithoutAuth); } /** * Clears the current deployment. */ public async clearDeployment(): Promise { + this.logger.debug("Clearing deployment", this.#deployment?.safeHostname); this.suspendSession(); this.#authListenerDisposable?.dispose(); this.#authListenerDisposable = undefined; diff --git a/src/login/loginCoordinator.ts b/src/login/loginCoordinator.ts index 1abb342c..a065d534 100644 --- a/src/login/loginCoordinator.ts +++ b/src/login/loginCoordinator.ts @@ -367,7 +367,7 @@ export class LoginCoordinator implements vscode.Disposable { */ private async loginWithOAuth(deployment: Deployment): Promise { try { - this.logger.info("Starting OAuth authentication"); + this.logger.debug("Starting OAuth authentication"); const { tokenResponse, user } = await vscode.window.withProgress( { diff --git a/src/oauth/authorizer.ts b/src/oauth/authorizer.ts index 001c06c7..98e2a119 100644 --- a/src/oauth/authorizer.ts +++ b/src/oauth/authorizer.ts @@ -96,7 +96,7 @@ export class OAuthAuthorizer implements vscode.Disposable { reportProgress("fetching user...", 20); const user = await client.getAuthenticatedUser(); - this.logger.info("OAuth login flow completed successfully"); + this.logger.debug("OAuth login flow completed successfully"); return { tokenResponse, @@ -157,7 +157,7 @@ export class OAuthAuthorizer implements vscode.Disposable { deployment.safeHostname, response.data, ); - this.logger.info( + this.logger.debug( "Saved OAuth client registration:", response.data.client_id, ); diff --git a/src/oauth/sessionManager.ts b/src/oauth/sessionManager.ts index a073173f..32ab7e78 100644 --- a/src/oauth/sessionManager.ts +++ b/src/oauth/sessionManager.ts @@ -311,12 +311,15 @@ export class OAuthSessionManager implements vscode.Disposable { ) { return; } - this.logger.debug("Switching OAuth deployment", deployment); this.deployment = deployment; this.clearRefreshState(); - // Block on refresh if token is expired to ensure valid state for callers const storedTokens = await this.getStoredTokens(); + if (storedTokens) { + this.logger.debug("Switching OAuth deployment", deployment); + } + + // Block on refresh if token is expired to ensure valid state for callers if (storedTokens && Date.now() >= storedTokens.expiry_timestamp) { try { await this.refreshToken(); @@ -331,7 +334,6 @@ export class OAuthSessionManager implements vscode.Disposable { } public clearDeployment(): void { - this.logger.debug("Clearing OAuth deployment state"); this.deployment = null; this.clearRefreshState(); } @@ -422,7 +424,7 @@ export class OAuthSessionManager implements vscode.Disposable { public async revokeRefreshToken(): Promise { const storedTokens = await this.getStoredTokens(); if (!storedTokens?.refresh_token) { - this.logger.info("No refresh token to revoke"); + this.logger.debug("No refresh token to revoke"); return; } @@ -449,11 +451,13 @@ export class OAuthSessionManager implements vscode.Disposable { await this.prepareOAuthOperation(authToken); if (!metadata.revocation_endpoint) { - this.logger.info("No revocation endpoint available, skipping revocation"); + this.logger.debug( + "No revocation endpoint available, skipping revocation", + ); return; } - this.logger.info("Revoking refresh token"); + this.logger.debug("Revoking refresh token"); const params: OAuth2TokenRevocationRequest = { token: tokenToRevoke, @@ -475,7 +479,7 @@ export class OAuthSessionManager implements vscode.Disposable { }, ); - this.logger.info("Token revocation successful"); + this.logger.debug("Token revocation successful"); } catch (error) { this.logger.error("Token revocation failed:", error); throw error; @@ -503,6 +507,5 @@ export class OAuthSessionManager implements vscode.Disposable { public dispose(): void { this.disposed = true; this.clearDeployment(); - this.logger.debug("OAuth session manager disposed"); } } diff --git a/src/remote/remote.ts b/src/remote/remote.ts index 8dab3c3c..7dcdce41 100644 --- a/src/remote/remote.ts +++ b/src/remote/remote.ts @@ -101,6 +101,12 @@ export class Remote { return; } + this.logger.debug("Setting up remote connection", { + hostname: parts.safeHostname, + workspace: `${parts.username}/${parts.workspace}`, + agent: parts.agent || "(default)", + }); + const workspaceName = `${parts.username}/${parts.workspace}`; // Migrate existing legacy file-based auth to secrets storage. @@ -110,6 +116,11 @@ export class Remote { const auth = await this.secretsManager.getSessionAuth(parts.safeHostname); const baseUrlRaw = auth?.url ?? ""; const token = auth?.token; + this.logger.debug("Retrieved auth for hostname", { + hostname: parts.safeHostname, + hasUrl: Boolean(baseUrlRaw), + hasToken: token !== undefined, + }); // Empty token is valid for mTLS if (baseUrlRaw && token !== undefined) { await this.cliManager.configure(parts.safeHostname, baseUrlRaw, token); @@ -343,6 +354,10 @@ export class Remote { ); // Wait for workspace to be running and agent to be ready + this.logger.debug("Starting workspace state machine", { + workspace: workspaceName, + initialStatus: workspace.latest_build.status, + }); const stateMachine = new WorkspaceStateMachine( parts, workspaceClient, @@ -423,6 +438,12 @@ export class Remote { throw new Error("Failed to get workspace or agent from state machine"); } + this.logger.info("Workspace ready", { + workspace: workspaceName, + agent: agent.name, + status: workspace.latest_build.status, + }); + this.commands.workspace = workspace; // Watch coder inbox for messages diff --git a/src/workspace/workspacesProvider.ts b/src/workspace/workspacesProvider.ts index a8d19ffc..ff462e75 100644 --- a/src/workspace/workspacesProvider.ts +++ b/src/workspace/workspacesProvider.ts @@ -101,11 +101,9 @@ export class WorkspaceProvider * logged in or the query fails. */ private async fetch(): Promise { - if (vscode.env.logLevel <= vscode.LogLevel.Debug) { - this.logger.info( - `Fetching workspaces: ${this.getWorkspacesQuery || "no filter"}...`, - ); - } + this.logger.debug( + `Fetching workspaces: ${this.getWorkspacesQuery || "no filter"}...`, + ); // If there is no URL configured, assume we are logged out. const url = this.client.getAxiosInstance().defaults.baseURL;