Compare commits

...

2 Commits

Author SHA1 Message Date
Thomas Kosiewski
8517ca1720 refactor(toolsdk): reuse agent resolution for WorkspaceBash 2026-02-04 15:45:00 +00:00
Thomas Kosiewski
2e789b06e7 fix(toolsdk): block WorkspaceBash on Windows agents 2026-02-04 15:06:05 +00:00
2 changed files with 35 additions and 7 deletions

View File

@@ -39,6 +39,8 @@ This tool provides the same functionality as the 'coder ssh <workspace> <command
It automatically starts the workspace if it's stopped and waits for the agent to be ready.
The output is trimmed of leading and trailing whitespace.
This tool is not supported for Windows workspace agents.
The workspace parameter supports various formats:
- workspace (uses current user)
- owner/workspace
@@ -100,7 +102,17 @@ Examples:
ctx, cancel := context.WithTimeoutCause(ctx, 5*time.Minute, xerrors.New("MCP handler timeout after 5 min"))
defer cancel()
conn, err := newAgentConn(ctx, deps.coderClient, args.Workspace)
// coder_workspace_bash relies on POSIX shell behavior (e.g. nohup and
// redirections) and is not supported for Windows workspace agents.
workspaceAgent, err := resolveWorkspaceAgent(ctx, deps.coderClient, args.Workspace)
if err != nil {
return WorkspaceBashResult{}, err
}
if strings.EqualFold(workspaceAgent.OperatingSystem, "windows") {
return WorkspaceBashResult{}, xerrors.New("coder_workspace_bash is not supported on Windows workspaces")
}
conn, err := dialAgentConn(ctx, deps.coderClient, workspaceAgent.ID)
if err != nil {
return WorkspaceBashResult{}, err
}

View File

@@ -2154,17 +2154,22 @@ func NormalizeWorkspaceInput(input string) string {
return normalized
}
// newAgentConn returns a connection to the agent specified by the workspace,
// which must be in the format [owner/]workspace[.agent].
func newAgentConn(ctx context.Context, client *codersdk.Client, workspace string) (workspacesdk.AgentConn, error) {
// resolveWorkspaceAgent finds workspace and agent by name with auto-start
// support. The workspace identifier must be in the format
// [owner/]workspace[.agent].
func resolveWorkspaceAgent(ctx context.Context, client *codersdk.Client, workspace string) (codersdk.WorkspaceAgent, error) {
workspaceName := NormalizeWorkspaceInput(workspace)
_, workspaceAgent, err := findWorkspaceAndAgent(ctx, client, workspaceName)
if err != nil {
return nil, xerrors.Errorf("failed to find workspace: %w", err)
return codersdk.WorkspaceAgent{}, xerrors.Errorf("failed to find workspace: %w", err)
}
return workspaceAgent, nil
}
func dialAgentConn(ctx context.Context, client *codersdk.Client, agentID uuid.UUID) (workspacesdk.AgentConn, error) {
// Wait for agent to be ready.
if err := cliui.Agent(ctx, io.Discard, workspaceAgent.ID, cliui.AgentOptions{
if err := cliui.Agent(ctx, io.Discard, agentID, cliui.AgentOptions{
FetchInterval: 0,
Fetch: client.WorkspaceAgent,
FetchLogs: client.WorkspaceAgentLogsAfter,
@@ -2175,7 +2180,7 @@ func newAgentConn(ctx context.Context, client *codersdk.Client, workspace string
wsClient := workspacesdk.New(client)
conn, err := wsClient.DialAgent(ctx, workspaceAgent.ID, &workspacesdk.DialAgentOptions{
conn, err := wsClient.DialAgent(ctx, agentID, &workspacesdk.DialAgentOptions{
BlockEndpoints: false,
})
if err != nil {
@@ -2189,6 +2194,17 @@ func newAgentConn(ctx context.Context, client *codersdk.Client, workspace string
return conn, nil
}
// newAgentConn returns a connection to the agent specified by the workspace,
// which must be in the format [owner/]workspace[.agent].
func newAgentConn(ctx context.Context, client *codersdk.Client, workspace string) (workspacesdk.AgentConn, error) {
workspaceAgent, err := resolveWorkspaceAgent(ctx, client, workspace)
if err != nil {
return nil, err
}
return dialAgentConn(ctx, client, workspaceAgent.ID)
}
const workspaceDescription = "The workspace ID or name in the format [owner/]workspace. If an owner is not specified, the authenticated user is used."
const workspaceAgentDescription = "The workspace name in the format [owner/]workspace[.agent]. If an owner is not specified, the authenticated user is used."