diff --git a/cli/cliui/agent_test.go b/cli/cliui/agent_test.go index 9a43eeb701dce..4f549f39f055e 100644 --- a/cli/cliui/agent_test.go +++ b/cli/cliui/agent_test.go @@ -12,6 +12,7 @@ import ( "github.com/coder/coder/cli/cliui" "github.com/coder/coder/codersdk" "github.com/coder/coder/pty/ptytest" + "github.com/coder/coder/testutil" ) func TestAgent(t *testing.T) { @@ -49,3 +50,50 @@ func TestAgent(t *testing.T) { disconnected.Store(true) <-done } + +func TestAgentTimeoutWithTroubleshootingURL(t *testing.T) { + t.Parallel() + + ctx, _ := testutil.Context(t) + + wantURL := "https://coder.com/troubleshoot" + + var connected, timeout atomic.Bool + cmd := &cobra.Command{ + RunE: func(cmd *cobra.Command, args []string) error { + err := cliui.Agent(cmd.Context(), cmd.OutOrStdout(), cliui.AgentOptions{ + WorkspaceName: "example", + Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) { + agent := codersdk.WorkspaceAgent{ + Status: codersdk.WorkspaceAgentConnecting, + TroubleshootingURL: "https://coder.com/troubleshoot", + } + switch { + case connected.Load(): + agent.Status = codersdk.WorkspaceAgentConnected + case timeout.Load(): + agent.Status = codersdk.WorkspaceAgentTimeout + } + return agent, nil + }, + FetchInterval: time.Millisecond, + WarnInterval: 5 * time.Millisecond, + }) + return err + }, + } + ptty := ptytest.New(t) + cmd.SetOutput(ptty.Output()) + cmd.SetIn(ptty.Input()) + done := make(chan struct{}) + go func() { + defer close(done) + err := cmd.ExecuteContext(ctx) + assert.NoError(t, err) + }() + ptty.ExpectMatch("Don't panic") + timeout.Store(true) + ptty.ExpectMatch(wantURL) + connected.Store(true) + <-done +} diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 1f309c7314f06..0eb9c926d3cbc 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -342,6 +342,13 @@ func newConfig() *codersdk.DeploymentConfig { Hidden: true, Default: 10 * time.Minute, }, + AgentFallbackTroubleshootingURL: &codersdk.DeploymentConfigField[string]{ + Name: "Agent Fallback Troubleshooting URL", + Usage: "URL to use for agent troubleshooting when not set in the template", + Flag: "agent-fallback-troubleshooting-url", + Hidden: true, + Default: "https://coder.com/docs/coder-oss/latest/templates#troubleshooting-templates", + }, AuditLogging: &codersdk.DeploymentConfigField[bool]{ Name: "Audit Logging", Usage: "Specifies whether audit logging is enabled.", diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index 9111f9ca287a2..c9eedf8dd68cc 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -262,7 +262,7 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request, } } - apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), agent, convertApps(dbApps), api.AgentInactiveDisconnectTimeout) + apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), agent, convertApps(dbApps), api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error reading job agent.", diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index f8d10d6ff33b8..665211fe0cc97 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -52,7 +52,7 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) { }) return } - apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, convertApps(dbApps), api.AgentInactiveDisconnectTimeout) + apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, convertApps(dbApps), api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error reading workspace agent.", @@ -67,7 +67,7 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) { func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspaceAgent := httpmw.WorkspaceAgent(r) - apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout) + apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error reading workspace agent.", @@ -138,7 +138,7 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) func (api *API) postWorkspaceAgentVersion(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspaceAgent := httpmw.WorkspaceAgent(r) - apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout) + apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error reading workspace agent.", @@ -192,7 +192,7 @@ func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) { httpapi.ResourceNotFound(rw) return } - apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout) + apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error reading workspace agent.", @@ -269,7 +269,7 @@ func (api *API) workspaceAgentListeningPorts(rw http.ResponseWriter, r *http.Req return } - apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout) + apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error reading workspace agent.", @@ -660,7 +660,7 @@ func convertApps(dbApps []database.WorkspaceApp) []codersdk.WorkspaceApp { return apps } -func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordinator, dbAgent database.WorkspaceAgent, apps []codersdk.WorkspaceApp, agentInactiveDisconnectTimeout time.Duration) (codersdk.WorkspaceAgent, error) { +func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordinator, dbAgent database.WorkspaceAgent, apps []codersdk.WorkspaceApp, agentInactiveDisconnectTimeout time.Duration, agentFallbackTroubleshootingURL string) (codersdk.WorkspaceAgent, error) { var envs map[string]string if dbAgent.EnvironmentVariables.Valid { err := json.Unmarshal(dbAgent.EnvironmentVariables.RawMessage, &envs) @@ -668,6 +668,10 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin return codersdk.WorkspaceAgent{}, xerrors.Errorf("unmarshal env vars: %w", err) } } + troubleshootingURL := agentFallbackTroubleshootingURL + if dbAgent.TroubleshootingURL != "" { + troubleshootingURL = dbAgent.TroubleshootingURL + } workspaceAgent := codersdk.WorkspaceAgent{ ID: dbAgent.ID, CreatedAt: dbAgent.CreatedAt, @@ -683,7 +687,7 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin Directory: dbAgent.Directory, Apps: apps, ConnectionTimeoutSeconds: dbAgent.ConnectionTimeoutSeconds, - TroubleshootingURL: dbAgent.TroubleshootingURL, + TroubleshootingURL: troubleshootingURL, } node := coordinator.Node(dbAgent.ID) if node != nil { diff --git a/coderd/workspaceagents_test.go b/coderd/workspaceagents_test.go index e89b913f1bf17..25e0010cab057 100644 --- a/coderd/workspaceagents_test.go +++ b/coderd/workspaceagents_test.go @@ -74,6 +74,48 @@ func TestWorkspaceAgent(t *testing.T) { _, err = client.WorkspaceAgent(ctx, workspace.LatestBuild.Resources[0].Agents[0].ID) require.NoError(t, err) }) + t.Run("HasFallbackTroubleshootingURL", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, &coderdtest.Options{ + IncludeProvisionerDaemon: true, + }) + user := coderdtest.CreateFirstUser(t, client) + authToken := uuid.NewString() + tmpDir := t.TempDir() + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ + Parse: echo.ParseComplete, + ProvisionPlan: echo.ProvisionComplete, + ProvisionApply: []*proto.Provision_Response{{ + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{ + Resources: []*proto.Resource{{ + Name: "example", + Type: "aws_instance", + Agents: []*proto.Agent{{ + Id: uuid.NewString(), + Directory: tmpDir, + Auth: &proto.Agent_Token{ + Token: authToken, + }, + }}, + }}, + }, + }, + }}, + }) + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + coderdtest.AwaitTemplateVersionJob(t, client, version.ID) + workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) + coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium) + defer cancel() + + workspace, err := client.Workspace(ctx, workspace.ID) + require.NoError(t, err) + require.NotEmpty(t, workspace.LatestBuild.Resources[0].Agents[0].TroubleshootingURL) + t.Log(workspace.LatestBuild.Resources[0].Agents[0].TroubleshootingURL) + }) t.Run("Timeout", func(t *testing.T) { t.Parallel() client := coderdtest.New(t, &coderdtest.Options{ @@ -82,6 +124,9 @@ func TestWorkspaceAgent(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) authToken := uuid.NewString() tmpDir := t.TempDir() + + wantTroubleshootingURL := "https://example.com/troubleshoot" + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.ProvisionComplete, @@ -98,7 +143,7 @@ func TestWorkspaceAgent(t *testing.T) { Token: authToken, }, ConnectionTimeoutSeconds: 1, - TroubleshootingUrl: "https://example.com/troubleshoot", + TroubleshootingUrl: wantTroubleshootingURL, }}, }}, }, @@ -113,13 +158,16 @@ func TestWorkspaceAgent(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium) defer cancel() + var err error testutil.Eventually(ctx, t, func(ctx context.Context) (done bool) { - workspace, err := client.Workspace(ctx, workspace.ID) + workspace, err = client.Workspace(ctx, workspace.ID) if !assert.NoError(t, err) { return false } return workspace.LatestBuild.Resources[0].Agents[0].Status == codersdk.WorkspaceAgentTimeout }, testutil.IntervalMedium, "agent status timeout") + + require.Equal(t, wantTroubleshootingURL, workspace.LatestBuild.Resources[0].Agents[0].TroubleshootingURL) }) } diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 89dd05dec1a0e..a797c50031d28 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -902,7 +902,7 @@ func (api *API) convertWorkspaceBuild( apiAgents := make([]codersdk.WorkspaceAgent, 0) for _, agent := range agents { apps := appsByAgentID[agent.ID] - apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), agent, convertApps(apps), api.AgentInactiveDisconnectTimeout) + apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), agent, convertApps(apps), api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value) if err != nil { return codersdk.WorkspaceBuild{}, xerrors.Errorf("converting workspace agent: %w", err) } diff --git a/codersdk/deploymentconfig.go b/codersdk/deploymentconfig.go index cb56ba5aa88e0..f15ba49df13b9 100644 --- a/codersdk/deploymentconfig.go +++ b/codersdk/deploymentconfig.go @@ -11,35 +11,36 @@ import ( // DeploymentConfig is the central configuration for the coder server. type DeploymentConfig struct { - AccessURL *DeploymentConfigField[string] `json:"access_url" typescript:",notnull"` - WildcardAccessURL *DeploymentConfigField[string] `json:"wildcard_access_url" typescript:",notnull"` - Address *DeploymentConfigField[string] `json:"address" typescript:",notnull"` - AutobuildPollInterval *DeploymentConfigField[time.Duration] `json:"autobuild_poll_interval" typescript:",notnull"` - DERP *DERP `json:"derp" typescript:",notnull"` - GitAuth *DeploymentConfigField[[]GitAuthConfig] `json:"gitauth" typescript:",notnull"` - Prometheus *PrometheusConfig `json:"prometheus" typescript:",notnull"` - Pprof *PprofConfig `json:"pprof" typescript:",notnull"` - ProxyTrustedHeaders *DeploymentConfigField[[]string] `json:"proxy_trusted_headers" typescript:",notnull"` - ProxyTrustedOrigins *DeploymentConfigField[[]string] `json:"proxy_trusted_origins" typescript:",notnull"` - CacheDirectory *DeploymentConfigField[string] `json:"cache_directory" typescript:",notnull"` - InMemoryDatabase *DeploymentConfigField[bool] `json:"in_memory_database" typescript:",notnull"` - PostgresURL *DeploymentConfigField[string] `json:"pg_connection_url" typescript:",notnull"` - OAuth2 *OAuth2Config `json:"oauth2" typescript:",notnull"` - OIDC *OIDCConfig `json:"oidc" typescript:",notnull"` - Telemetry *TelemetryConfig `json:"telemetry" typescript:",notnull"` - TLS *TLSConfig `json:"tls" typescript:",notnull"` - Trace *TraceConfig `json:"trace" typescript:",notnull"` - SecureAuthCookie *DeploymentConfigField[bool] `json:"secure_auth_cookie" typescript:",notnull"` - SSHKeygenAlgorithm *DeploymentConfigField[string] `json:"ssh_keygen_algorithm" typescript:",notnull"` - AutoImportTemplates *DeploymentConfigField[[]string] `json:"auto_import_templates" typescript:",notnull"` - MetricsCacheRefreshInterval *DeploymentConfigField[time.Duration] `json:"metrics_cache_refresh_interval" typescript:",notnull"` - AgentStatRefreshInterval *DeploymentConfigField[time.Duration] `json:"agent_stat_refresh_interval" typescript:",notnull"` - AuditLogging *DeploymentConfigField[bool] `json:"audit_logging" typescript:",notnull"` - BrowserOnly *DeploymentConfigField[bool] `json:"browser_only" typescript:",notnull"` - SCIMAPIKey *DeploymentConfigField[string] `json:"scim_api_key" typescript:",notnull"` - Provisioner *ProvisionerConfig `json:"provisioner" typescript:",notnull"` - APIRateLimit *DeploymentConfigField[int] `json:"api_rate_limit" typescript:",notnull"` - Experimental *DeploymentConfigField[bool] `json:"experimental" typescript:",notnull"` + AccessURL *DeploymentConfigField[string] `json:"access_url" typescript:",notnull"` + WildcardAccessURL *DeploymentConfigField[string] `json:"wildcard_access_url" typescript:",notnull"` + Address *DeploymentConfigField[string] `json:"address" typescript:",notnull"` + AutobuildPollInterval *DeploymentConfigField[time.Duration] `json:"autobuild_poll_interval" typescript:",notnull"` + DERP *DERP `json:"derp" typescript:",notnull"` + GitAuth *DeploymentConfigField[[]GitAuthConfig] `json:"gitauth" typescript:",notnull"` + Prometheus *PrometheusConfig `json:"prometheus" typescript:",notnull"` + Pprof *PprofConfig `json:"pprof" typescript:",notnull"` + ProxyTrustedHeaders *DeploymentConfigField[[]string] `json:"proxy_trusted_headers" typescript:",notnull"` + ProxyTrustedOrigins *DeploymentConfigField[[]string] `json:"proxy_trusted_origins" typescript:",notnull"` + CacheDirectory *DeploymentConfigField[string] `json:"cache_directory" typescript:",notnull"` + InMemoryDatabase *DeploymentConfigField[bool] `json:"in_memory_database" typescript:",notnull"` + PostgresURL *DeploymentConfigField[string] `json:"pg_connection_url" typescript:",notnull"` + OAuth2 *OAuth2Config `json:"oauth2" typescript:",notnull"` + OIDC *OIDCConfig `json:"oidc" typescript:",notnull"` + Telemetry *TelemetryConfig `json:"telemetry" typescript:",notnull"` + TLS *TLSConfig `json:"tls" typescript:",notnull"` + Trace *TraceConfig `json:"trace" typescript:",notnull"` + SecureAuthCookie *DeploymentConfigField[bool] `json:"secure_auth_cookie" typescript:",notnull"` + SSHKeygenAlgorithm *DeploymentConfigField[string] `json:"ssh_keygen_algorithm" typescript:",notnull"` + AutoImportTemplates *DeploymentConfigField[[]string] `json:"auto_import_templates" typescript:",notnull"` + MetricsCacheRefreshInterval *DeploymentConfigField[time.Duration] `json:"metrics_cache_refresh_interval" typescript:",notnull"` + AgentStatRefreshInterval *DeploymentConfigField[time.Duration] `json:"agent_stat_refresh_interval" typescript:",notnull"` + AgentFallbackTroubleshootingURL *DeploymentConfigField[string] `json:"agent_fallback_troubleshooting_url" typescript:",notnull"` + AuditLogging *DeploymentConfigField[bool] `json:"audit_logging" typescript:",notnull"` + BrowserOnly *DeploymentConfigField[bool] `json:"browser_only" typescript:",notnull"` + SCIMAPIKey *DeploymentConfigField[string] `json:"scim_api_key" typescript:",notnull"` + Provisioner *ProvisionerConfig `json:"provisioner" typescript:",notnull"` + APIRateLimit *DeploymentConfigField[int] `json:"api_rate_limit" typescript:",notnull"` + Experimental *DeploymentConfigField[bool] `json:"experimental" typescript:",notnull"` } type DERP struct { diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index a5e1b0ce2e3a0..3721aaa316f92 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -56,7 +56,7 @@ type WorkspaceAgent struct { // DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). DERPLatency map[string]DERPRegion `json:"latency,omitempty"` ConnectionTimeoutSeconds int32 `json:"connection_timeout_seconds"` - TroubleshootingURL string `json:"troubleshooting_url,omitempty"` + TroubleshootingURL string `json:"troubleshooting_url"` } type WorkspaceAgentResourceMetadata struct { diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index f6b71a0daf608..3a1be8c00cc82 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -299,6 +299,7 @@ export interface DeploymentConfig { readonly auto_import_templates: DeploymentConfigField readonly metrics_cache_refresh_interval: DeploymentConfigField readonly agent_stat_refresh_interval: DeploymentConfigField + readonly agent_fallback_troubleshooting_url: DeploymentConfigField readonly audit_logging: DeploymentConfigField readonly browser_only: DeploymentConfigField readonly scim_api_key: DeploymentConfigField @@ -819,7 +820,7 @@ export interface WorkspaceAgent { readonly apps: WorkspaceApp[] readonly latency?: Record readonly connection_timeout_seconds: number - readonly troubleshooting_url?: string + readonly troubleshooting_url: string } // From codersdk/workspaceagents.go diff --git a/site/src/components/Resources/AgentRow.tsx b/site/src/components/Resources/AgentRow.tsx index e53c2678a55b7..d0fbc112fbb44 100644 --- a/site/src/components/Resources/AgentRow.tsx +++ b/site/src/components/Resources/AgentRow.tsx @@ -45,7 +45,7 @@ export const AgentRow: FC = ({ >
- +
{agent.name}
diff --git a/site/src/components/Resources/AgentStatus.tsx b/site/src/components/Resources/AgentStatus.tsx index 203f721845f30..48de1597c8ca3 100644 --- a/site/src/components/Resources/AgentStatus.tsx +++ b/site/src/components/Resources/AgentStatus.tsx @@ -1,7 +1,7 @@ import Tooltip from "@material-ui/core/Tooltip" import { makeStyles } from "@material-ui/core/styles" import { combineClasses } from "util/combineClasses" -import { Workspace, WorkspaceAgent } from "api/typesGenerated" +import { WorkspaceAgent } from "api/typesGenerated" import { ChooseOne, Cond } from "components/Conditionals/ChooseOne" import { useTranslation } from "react-i18next" import WarningRounded from "@material-ui/icons/WarningRounded" @@ -58,15 +58,12 @@ const ConnectingStatus: React.FC = () => { const TimeoutStatus: React.FC<{ agent: WorkspaceAgent - workspace: Workspace -}> = ({ agent, workspace }) => { +}> = ({ agent }) => { const { t } = useTranslation("agent") const styles = useStyles() const anchorRef = useRef(null) const [isOpen, setIsOpen] = useState(false) const id = isOpen ? "timeout-popover" : undefined - const troubleshootLink = - agent.troubleshooting_url ?? `/templates/${workspace.template_name}#readme` return ( <> @@ -88,7 +85,11 @@ const TimeoutStatus: React.FC<{ {t("timeoutTooltip.title")} {t("timeoutTooltip.message")}{" "} - + {t("timeoutTooltip.link")} . @@ -100,8 +101,7 @@ const TimeoutStatus: React.FC<{ export const AgentStatus: React.FC<{ agent: WorkspaceAgent - workspace: Workspace -}> = ({ agent, workspace }) => { +}> = ({ agent }) => { return ( @@ -111,7 +111,7 @@ export const AgentStatus: React.FC<{ - +