diff --git a/coder-sdk/agent.go b/coder-sdk/agent.go new file mode 100644 index 00000000..29052f11 --- /dev/null +++ b/coder-sdk/agent.go @@ -0,0 +1,22 @@ +package coder + +import ( + "context" + "fmt" + "net/http" +) + +// UpdateLastConnectionAt updates the last connection at attribute of a workspace. +func (c *DefaultClient) UpdateLastConnectionAt(ctx context.Context, workspaceID string) error { + reqURL := fmt.Sprintf("/api/private/envagent/%s/update-last-connection-at", workspaceID) + resp, err := c.request(ctx, http.MethodPost, reqURL, nil) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + return NewHTTPError(resp) + } + + return nil +} diff --git a/coder-sdk/interface.go b/coder-sdk/interface.go index dafb2114..b28bfe84 100644 --- a/coder-sdk/interface.go +++ b/coder-sdk/interface.go @@ -250,4 +250,7 @@ type Client interface { // DeleteSatelliteByID deletes a satellite entity from the Coder control plane. DeleteSatelliteByID(ctx context.Context, id string) error + + // UpdateLastConnectionAt updates the last connection at attribute of a workspace. + UpdateLastConnectionAt(ctx context.Context, workspaceID string) error } diff --git a/internal/cmd/tunnel.go b/internal/cmd/tunnel.go index da29e585..bc9a867f 100644 --- a/internal/cmd/tunnel.go +++ b/internal/cmd/tunnel.go @@ -8,6 +8,7 @@ import ( "net/url" "os" "strconv" + "time" "cdr.dev/slog" "cdr.dev/slog/sloggers/sloghuman" @@ -130,6 +131,27 @@ func (c *tunnneler) start(ctx context.Context) error { } c.log.Debug(ctx, "Connected to workspace!") + sdk, err := newClient(ctx, false) + if err != nil { + return xerrors.Errorf("getting coder client: %w", err) + } + + // regularly update the last connection at + go func() { + ticker := time.NewTicker(time.Minute) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + // silently ignore failures so we don't spam the console + _ = sdk.UpdateLastConnectionAt(ctx, c.workspaceID) + } + } + }() + // proxy via stdio if c.stdio { go func() {