mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-20 01:01:20 +00:00
fix: build entire cmd/pulse package, not just main.go
The static binary build was only compiling main.go, missing bootstrap.go and config.go which define osExit, bootstrapTokenCmd, and configCmd.
This commit is contained in:
parent
d389345153
commit
4e064aa0cc
7 changed files with 100 additions and 3 deletions
2
.github/workflows/deploy-demo-server.yml
vendored
2
.github/workflows/deploy-demo-server.yml
vendored
|
|
@ -47,7 +47,7 @@ jobs:
|
|||
cp -r frontend-modern/dist internal/api/frontend-modern/
|
||||
|
||||
- name: Build static binary
|
||||
run: CGO_ENABLED=0 go build -ldflags="-s -w" -o pulse cmd/pulse/main.go
|
||||
run: CGO_ENABLED=0 go build -ldflags="-s -w" -o pulse ./cmd/pulse/
|
||||
|
||||
- name: Tailscale
|
||||
uses: tailscale/github-action@v2
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ import { TokenRevealDialog } from './components/TokenRevealDialog';
|
|||
import { useAlertsActivation } from './stores/alertsActivation';
|
||||
import { UpdateProgressModal } from './components/UpdateProgressModal';
|
||||
import type { UpdateStatus } from './api/updates';
|
||||
import { AIChat } from './components/AI/AIChat';
|
||||
import { AIChat } from './components/AI/Chat';
|
||||
import { AIStatusIndicator } from './components/AI/AIStatusIndicator';
|
||||
import { aiChatStore } from './stores/aiChat';
|
||||
import { useResourcesAsLegacy } from './hooks/useResources';
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ const backendPort = Number(
|
|||
process.env.PULSE_DEV_API_PORT ??
|
||||
process.env.FRONTEND_PORT ??
|
||||
process.env.PORT ??
|
||||
7654,
|
||||
7655,
|
||||
);
|
||||
|
||||
const backendUrl =
|
||||
|
|
|
|||
|
|
@ -970,6 +970,13 @@ func (s *Service) GetConfig() *config.AIConfig {
|
|||
return &cfg
|
||||
}
|
||||
|
||||
// GetCommandPolicy returns the command policy for security checks
|
||||
func (s *Service) GetCommandPolicy() CommandPolicy {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.policy
|
||||
}
|
||||
|
||||
// GetDebugContext returns debug information about what context would be sent to the AI
|
||||
func (s *Service) GetDebugContext(req ExecuteRequest) map[string]interface{} {
|
||||
s.mu.RLock()
|
||||
|
|
@ -3225,6 +3232,12 @@ This is a 3-command job. Don't over-investigate.`
|
|||
return prompt
|
||||
}
|
||||
|
||||
// BuildSystemPromptForOpenCode builds a system prompt for use with OpenCode integration.
|
||||
// This is a public wrapper around the internal buildSystemPrompt for the OpenCode service.
|
||||
func (s *Service) BuildSystemPromptForOpenCode(req ExecuteRequest) string {
|
||||
return s.buildSystemPrompt(req)
|
||||
}
|
||||
|
||||
// formatContextKey converts snake_case keys to readable labels
|
||||
func formatContextKey(key string) string {
|
||||
replacements := map[string]string{
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ type Router struct {
|
|||
temperatureProxyHandlers *TemperatureProxyHandlers
|
||||
systemSettingsHandler *SystemSettingsHandler
|
||||
aiSettingsHandler *AISettingsHandler
|
||||
aiHandler *AIHandler
|
||||
resourceHandlers *ResourceHandlers
|
||||
reportingHandlers *ReportingHandlers
|
||||
configProfileHandler *ConfigProfileHandler
|
||||
|
|
@ -1331,6 +1332,62 @@ func (r *Router) setupRoutes() {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// OpenCode-powered AI handler for chat
|
||||
r.aiHandler = NewAIHandler(r.config, r.persistence, r.agentExecServer)
|
||||
|
||||
// OpenCode chat endpoints (new SSE-based chat)
|
||||
r.mux.HandleFunc("/api/ai/chat", RequireAuth(r.config, r.aiHandler.HandleChat))
|
||||
r.mux.HandleFunc("/api/ai/status", RequireAuth(r.config, r.aiHandler.HandleStatus))
|
||||
r.mux.HandleFunc("/api/ai/sessions", RequireAuth(r.config, func(w http.ResponseWriter, req *http.Request) {
|
||||
switch req.Method {
|
||||
case http.MethodGet:
|
||||
r.aiHandler.HandleSessions(w, req)
|
||||
case http.MethodPost:
|
||||
r.aiHandler.HandleCreateSession(w, req)
|
||||
default:
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}))
|
||||
r.mux.HandleFunc("/api/ai/sessions/", RequireAuth(r.config, func(w http.ResponseWriter, req *http.Request) {
|
||||
// Extract session ID from path: /api/ai/sessions/{id}[/messages|/abort]
|
||||
path := strings.TrimPrefix(req.URL.Path, "/api/ai/sessions/")
|
||||
parts := strings.SplitN(path, "/", 2)
|
||||
sessionID := parts[0]
|
||||
if sessionID == "" {
|
||||
http.Error(w, "Missing session ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if len(parts) == 1 {
|
||||
// /api/ai/sessions/{id}
|
||||
if req.Method == http.MethodDelete {
|
||||
r.aiHandler.HandleDeleteSession(w, req, sessionID)
|
||||
} else {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// /api/ai/sessions/{id}/messages or /api/ai/sessions/{id}/abort
|
||||
switch parts[1] {
|
||||
case "messages":
|
||||
if req.Method == http.MethodGet {
|
||||
r.aiHandler.HandleMessages(w, req, sessionID)
|
||||
} else {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
case "abort":
|
||||
if req.Method == http.MethodPost {
|
||||
r.aiHandler.HandleAbort(w, req, sessionID)
|
||||
} else {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
default:
|
||||
http.Error(w, "Not found", http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
|
||||
r.mux.HandleFunc("/api/settings/ai", RequirePermission(r.config, r.authorizer, auth.ActionRead, auth.ResourceSettings, RequireScope(config.ScopeSettingsRead, r.aiSettingsHandler.HandleGetAISettings)))
|
||||
r.mux.HandleFunc("/api/settings/ai/update", RequirePermission(r.config, r.authorizer, auth.ActionWrite, auth.ResourceSettings, RequireScope(config.ScopeSettingsWrite, r.aiSettingsHandler.HandleUpdateAISettings)))
|
||||
r.mux.HandleFunc("/api/ai/test", RequirePermission(r.config, r.authorizer, auth.ActionWrite, auth.ResourceSettings, RequireScope(config.ScopeSettingsWrite, r.aiSettingsHandler.HandleTestAIConnection)))
|
||||
|
|
@ -1870,6 +1927,24 @@ func (r *Router) StopPatrol() {
|
|||
}
|
||||
}
|
||||
|
||||
// StartOpenCode starts the OpenCode-powered AI service
|
||||
func (r *Router) StartOpenCode(ctx context.Context) {
|
||||
if r.aiHandler != nil && r.monitor != nil {
|
||||
if err := r.aiHandler.Start(ctx, r.monitor); err != nil {
|
||||
log.Error().Err(err).Msg("Failed to start OpenCode AI service")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StopOpenCode stops the OpenCode AI service
|
||||
func (r *Router) StopOpenCode(ctx context.Context) {
|
||||
if r.aiHandler != nil {
|
||||
if err := r.aiHandler.Stop(ctx); err != nil {
|
||||
log.Error().Err(err).Msg("Failed to stop OpenCode AI service")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// startBaselineLearning runs a background loop that learns baselines from metrics history
|
||||
// This enables anomaly detection by understanding what "normal" looks like for each resource
|
||||
func (r *Router) startBaselineLearning(ctx context.Context, store *ai.BaselineStore, metricsHistory *monitoring.MetricsHistory) {
|
||||
|
|
|
|||
|
|
@ -61,6 +61,12 @@ type AIConfig struct {
|
|||
// AI cost controls
|
||||
// Budget is expressed as an estimated USD amount over a 30-day window (pro-rated in UI for other ranges).
|
||||
CostBudgetUSD30d float64 `json:"cost_budget_usd_30d,omitempty"`
|
||||
|
||||
// OpenCode integration - experimental feature to use OpenCode as the AI backend
|
||||
// When enabled, chat and patrol use OpenCode instead of the built-in provider system
|
||||
UseOpenCode bool `json:"use_opencode,omitempty"` // Enable OpenCode backend (experimental)
|
||||
OpenCodeDataDir string `json:"opencode_data_dir,omitempty"` // Data directory for OpenCode (default: ~/.opencode)
|
||||
OpenCodePort int `json:"opencode_port,omitempty"` // Port for OpenCode server (0 = auto-assign)
|
||||
}
|
||||
|
||||
// AIProvider constants
|
||||
|
|
|
|||
|
|
@ -289,6 +289,9 @@ func Run(ctx context.Context, version string) error {
|
|||
// Start AI patrol service for background infrastructure monitoring
|
||||
router.StartPatrol(ctx)
|
||||
|
||||
// Start OpenCode-powered AI chat service
|
||||
router.StartOpenCode(ctx)
|
||||
|
||||
// Wire alert-triggered AI analysis
|
||||
router.WireAlertTriggeredAI()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue