cli_identity.go 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. package xai
  2. import (
  3. "net/http"
  4. "os"
  5. "strings"
  6. "golang.org/x/mod/semver"
  7. )
  8. // Fixed Grok Build / CLI-chat-proxy client identity.
  9. // These values are intentionally pinned in-binary (not scraped from live CLI).
  10. // Operators may bump the version via XAI_GROK_CLI_VERSION without a release.
  11. const (
  12. // CLIProxyHost is the hostname that requires the official CLI identity headers.
  13. CLIProxyHost = "cli-chat-proxy.grok.com"
  14. // CLIStableVersion is the known-good minimum client version accepted by cli-chat-proxy.
  15. CLIStableVersion = "0.2.93"
  16. // CLIVersionEnv is the optional operator override for CLIStableVersion.
  17. CLIVersionEnv = "XAI_GROK_CLI_VERSION"
  18. // CLITokenAuth is required by cli-chat-proxy for Grok Build OAuth tokens.
  19. CLITokenAuth = "xai-grok-cli"
  20. // CLIClientIdentifier is the x-grok-client-identifier value used by Grok shell/CLI.
  21. CLIClientIdentifier = "grok-shell"
  22. // CLIClientMode is used by billing / quota probes on the CLI surface.
  23. CLIClientMode = "cli"
  24. )
  25. // ResolveCLIVersion returns a supported CLI client version.
  26. // Empty or invalid overrides fall back to CLIClientVersion (the pinned
  27. // preferred client pin in billing.go). CLIStableVersion is only the minimum
  28. // accepted by IsSupportedCLIVersion, not the default identity we advertise.
  29. func ResolveCLIVersion() string {
  30. version := strings.TrimSpace(os.Getenv(CLIVersionEnv))
  31. if !IsSupportedCLIVersion(version) {
  32. return CLIClientVersion
  33. }
  34. return version
  35. }
  36. // IsSupportedCLIVersion reports whether version is a valid semver string at or
  37. // above CLIStableVersion (prereleases below a higher release are rejected when
  38. // they compare less than the stable pin).
  39. func IsSupportedCLIVersion(version string) bool {
  40. canonical := "v" + version
  41. minimum := "v" + CLIStableVersion
  42. return semver.IsValid(canonical) &&
  43. semver.Canonical(canonical) == canonical &&
  44. semver.Compare(canonical, minimum) >= 0
  45. }
  46. // CLIUserAgent builds the workspace-style User-Agent for a CLI client version.
  47. func CLIUserAgent(version string) string {
  48. if strings.TrimSpace(version) == "" {
  49. version = CLIClientVersion
  50. }
  51. return "xai-grok-workspace/" + version
  52. }
  53. // ApplyCLIProxyHeaders stamps the fixed Grok CLI identity when the request
  54. // targets cli-chat-proxy. Direct api.x.ai traffic is left unchanged.
  55. func ApplyCLIProxyHeaders(req *http.Request) {
  56. if req == nil || req.URL == nil || !strings.EqualFold(strings.TrimSpace(req.URL.Hostname()), CLIProxyHost) {
  57. return
  58. }
  59. if req.Header == nil {
  60. req.Header = make(http.Header)
  61. }
  62. version := ResolveCLIVersion()
  63. req.Header.Set("X-XAI-Token-Auth", CLITokenAuth)
  64. req.Header.Set("x-grok-client-version", version)
  65. req.Header.Set("x-grok-client-identifier", CLIClientIdentifier)
  66. req.Header.Set("User-Agent", CLIUserAgent(version))
  67. }