env.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. package hostbridge
  2. import (
  3. "context"
  4. "log"
  5. "os"
  6. "github.com/atotto/clipboard"
  7. "github.com/cline/cli/pkg/cli/global"
  8. "github.com/cline/grpc-go/cline"
  9. "github.com/cline/grpc-go/host"
  10. "google.golang.org/protobuf/proto"
  11. )
  12. // Global shutdown channel - simple approach
  13. var globalShutdownCh chan struct{}
  14. func init() {
  15. globalShutdownCh = make(chan struct{})
  16. }
  17. // EnvService implements the host.EnvServiceServer interface
  18. type EnvService struct {
  19. host.UnimplementedEnvServiceServer
  20. verbose bool
  21. }
  22. // NewEnvService creates a new EnvService
  23. func NewEnvService(verbose bool) *EnvService {
  24. return &EnvService{
  25. verbose: verbose,
  26. }
  27. }
  28. // ClipboardWriteText writes text to the system clipboard
  29. func (s *EnvService) ClipboardWriteText(ctx context.Context, req *cline.StringRequest) (*cline.Empty, error) {
  30. if s.verbose {
  31. log.Printf("ClipboardWriteText called with text length: %d", len(req.GetValue()))
  32. }
  33. err := clipboard.WriteAll(req.GetValue())
  34. if err != nil {
  35. if s.verbose {
  36. log.Printf("Failed to write to clipboard: %v", err)
  37. }
  38. // Don't fail if clipboard is not available (e.g., headless environment)
  39. }
  40. return &cline.Empty{}, nil
  41. }
  42. // ClipboardReadText reads text from the system clipboard
  43. func (s *EnvService) ClipboardReadText(ctx context.Context, req *cline.EmptyRequest) (*cline.String, error) {
  44. if s.verbose {
  45. log.Printf("ClipboardReadText called")
  46. }
  47. text, err := clipboard.ReadAll()
  48. if err != nil {
  49. if s.verbose {
  50. log.Printf("Failed to read from clipboard: %v", err)
  51. }
  52. // Return empty string if clipboard is not available
  53. text = ""
  54. }
  55. return &cline.String{
  56. Value: text,
  57. }, nil
  58. }
  59. // GetHostVersion returns the host platform name and version
  60. func (s *EnvService) GetHostVersion(ctx context.Context, req *cline.EmptyRequest) (*host.GetHostVersionResponse, error) {
  61. if s.verbose {
  62. log.Printf("GetHostVersion called")
  63. }
  64. return &host.GetHostVersionResponse{
  65. Platform: proto.String("Cline CLI"),
  66. Version: proto.String(global.CliVersion),
  67. ClineType: proto.String("CLI"),
  68. ClineVersion: proto.String(global.CliVersion),
  69. }, nil
  70. }
  71. // Shutdown initiates a graceful shutdown of the host bridge service
  72. func (s *EnvService) Shutdown(ctx context.Context, req *cline.EmptyRequest) (*cline.Empty, error) {
  73. if s.verbose {
  74. log.Printf("Shutdown requested via RPC")
  75. }
  76. // Trigger global shutdown signal
  77. select {
  78. case globalShutdownCh <- struct{}{}:
  79. if s.verbose {
  80. log.Printf("Shutdown signal sent successfully")
  81. }
  82. default:
  83. if s.verbose {
  84. log.Printf("Shutdown signal already pending")
  85. }
  86. }
  87. return &cline.Empty{}, nil
  88. }
  89. // GetTelemetrySettings returns the telemetry settings for CLI mode
  90. func (s *EnvService) GetTelemetrySettings(ctx context.Context, req *cline.EmptyRequest) (*host.GetTelemetrySettingsResponse, error) {
  91. if s.verbose {
  92. log.Printf("GetTelemetrySettings called")
  93. }
  94. // In CLI mode, check the POSTHOG_TELEMETRY_ENABLED environment variable
  95. telemetryEnabled := os.Getenv("POSTHOG_TELEMETRY_ENABLED") == "true"
  96. var setting host.Setting
  97. if telemetryEnabled {
  98. setting = host.Setting_ENABLED
  99. } else {
  100. setting = host.Setting_DISABLED
  101. }
  102. return &host.GetTelemetrySettingsResponse{
  103. IsEnabled: setting,
  104. }, nil
  105. }
  106. // SubscribeToTelemetrySettings returns a stream of telemetry setting changes
  107. // In CLI mode, telemetry settings don't change at runtime, so we just send
  108. // the current state and keep the stream open
  109. func (s *EnvService) SubscribeToTelemetrySettings(req *cline.EmptyRequest, stream host.EnvService_SubscribeToTelemetrySettingsServer) error {
  110. if s.verbose {
  111. log.Printf("SubscribeToTelemetrySettings called")
  112. }
  113. // Send initial telemetry state
  114. telemetryEnabled := os.Getenv("POSTHOG_TELEMETRY_ENABLED") == "true"
  115. var setting host.Setting
  116. if telemetryEnabled {
  117. setting = host.Setting_ENABLED
  118. } else {
  119. setting = host.Setting_DISABLED
  120. }
  121. event := &host.TelemetrySettingsEvent{
  122. IsEnabled: setting,
  123. }
  124. if err := stream.Send(event); err != nil {
  125. if s.verbose {
  126. log.Printf("Failed to send telemetry settings event: %v", err)
  127. }
  128. return err
  129. }
  130. // Keep stream open until context is cancelled
  131. // (In CLI mode, settings don't change dynamically)
  132. <-stream.Context().Done()
  133. if s.verbose {
  134. log.Printf("SubscribeToTelemetrySettings stream closed")
  135. }
  136. return nil
  137. }