Files
coder/codersdk/appname.go
T
Ehab Younes c767cc5198 refactor: normalize workspace agent session metadata (#28124)
Phase 1 of the Scalable Approach for Adding New IDE Session Types RFC,
tracked in #27410.

Gives agent session accounting one canonical vocabulary for app names, so
later work can report a count per app without the agent and the server
disagreeing about what an app is called.

- Adds codersdk/appname.go: NormalizeAppName for storage and lookup, and
  AppFamilyName for the places that need a bounded value, such as a
  Prometheus label or the Connection_Type enum.
- agentssh.Server keeps session counters in a map keyed by normalized name
  instead of three fixed atomics, dropping entries at zero.
- Drops MagicSessionType. Sessions carry plain app names, and unrecognized
  ones flow through as themselves rather than collapsing to unknown.

Wire behavior is unchanged: the agent still reports the four session_count_*
fields over Agent API 2.10.
2026-08-21 02:22:18 +03:00

76 lines
2.6 KiB
Go

package codersdk
import (
"strings"
utilstrings "github.com/coder/coder/v2/coderd/util/strings"
)
// maxAppNameLength caps an app name in runes.
const maxAppNameLength = 64
// AppFamilyName is the bounded set of names that arbitrary app names group
// into, for callers that need a fixed value such as a metric label or the
// Connection_Type enum. Server-side only, not part of the HTTP API.
//
// @typescript-ignore AppFamilyName
type AppFamilyName string
const (
AppFamilyVSCode AppFamilyName = "vscode"
AppFamilyJetBrains AppFamilyName = "jetbrains"
AppFamilySSH AppFamilyName = "ssh"
AppFamilyReconnectingPTY AppFamilyName = "reconnecting_pty"
AppFamilyUnknown AppFamilyName = "unknown"
)
// appNameFamilies never gates storage, so a missing alias only costs an
// AppFamilyUnknown label. Keys are the IDs Coder's registry modules use.
var appNameFamilies = map[string]AppFamilyName{
"vscode": AppFamilyVSCode,
"vscode_insiders": AppFamilyVSCode,
"vscode_web": AppFamilyVSCode,
"code_server": AppFamilyVSCode,
"cursor": AppFamilyVSCode,
"windsurf": AppFamilyVSCode,
"positron": AppFamilyVSCode,
"vscodium": AppFamilyVSCode,
"codium": AppFamilyVSCode,
"antigravity": AppFamilyVSCode,
"trae": AppFamilyVSCode,
"kiro": AppFamilyVSCode,
"devin": AppFamilyVSCode,
"jetbrains": AppFamilyJetBrains,
// Zed has no Connection_Type or session count field of its own, so it
// rolls up under SSH. The raw name still reaches storage.
"zed": AppFamilySSH,
"ssh": AppFamilySSH,
"reconnecting_pty": AppFamilyReconnectingPTY,
}
// AppNameFamily normalizes an app name and returns its family, or
// AppFamilyUnknown.
func AppNameFamily(appName string) AppFamilyName {
if family, ok := appNameFamilies[NormalizeAppName(appName)]; ok {
return family
}
return AppFamilyUnknown
}
// NormalizeAppName prepares a client-supplied app name for storage and
// lookup: it strips the null bytes Postgres TEXT rejects, trims, truncates,
// lowercases, and folds hyphens to underscores. Empty becomes
// AppFamilyUnknown.
func NormalizeAppName(appName string) string {
appName = strings.ReplaceAll(appName, "\x00", "")
// Trim before truncating so padding does not spend the budget, and after
// in case the cut lands in whitespace.
appName = strings.TrimSpace(appName)
appName = strings.TrimSpace(utilstrings.Truncate(appName, maxAppNameLength))
if appName == "" {
return string(AppFamilyUnknown)
}
return strings.ReplaceAll(strings.ToLower(appName), "-", "_")
}