167 lines
5.1 KiB
Go
167 lines
5.1 KiB
Go
package sqlite
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"time"
|
|
)
|
|
|
|
// RuntimeState holds the DB-persisted runtime state for an agent instance.
|
|
type RuntimeState struct {
|
|
AgentID string `db:"agent_id"`
|
|
Status string `db:"status"`
|
|
PauseReason string `db:"pause_reason"`
|
|
LastRunFinishedAt *time.Time `db:"last_run_finished_at"`
|
|
UpdatedAt time.Time `db:"updated_at"`
|
|
}
|
|
|
|
// GetAgentRuntime returns the runtime state for a single agent.
|
|
// Returns nil, nil if the agent has no runtime row.
|
|
func (r *Repository) GetAgentRuntime(ctx context.Context, agentID string) (*RuntimeState, error) {
|
|
var state RuntimeState
|
|
err := r.ro.QueryRowxContext(ctx, r.ro.Rebind(
|
|
`SELECT * FROM office_agent_runtime WHERE agent_id = ?`), agentID).StructScan(&state)
|
|
if err == sql.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &state, nil
|
|
}
|
|
|
|
// UpsertAgentRuntime creates or updates the runtime state for an agent.
|
|
func (r *Repository) UpsertAgentRuntime(ctx context.Context, agentID, status, pauseReason string) error {
|
|
now := time.Now().UTC()
|
|
_, err := r.db.ExecContext(ctx, r.db.Rebind(`
|
|
INSERT INTO office_agent_runtime (agent_id, status, pause_reason, updated_at)
|
|
VALUES (?, ?, ?, ?)
|
|
ON CONFLICT(agent_id) DO UPDATE SET
|
|
status = excluded.status,
|
|
pause_reason = excluded.pause_reason,
|
|
updated_at = excluded.updated_at
|
|
`), agentID, status, pauseReason, now)
|
|
return err
|
|
}
|
|
|
|
// DeleteAgentRuntime removes the runtime row for an agent.
|
|
func (r *Repository) DeleteAgentRuntime(ctx context.Context, agentID string) error {
|
|
_, err := r.db.ExecContext(ctx, r.db.Rebind(
|
|
`DELETE FROM office_agent_runtime WHERE agent_id = ?`), agentID)
|
|
return err
|
|
}
|
|
|
|
// ListAgentRuntimes returns all runtime rows keyed by agent ID.
|
|
func (r *Repository) ListAgentRuntimes(ctx context.Context) (map[string]*RuntimeState, error) {
|
|
var rows []*RuntimeState
|
|
err := r.ro.SelectContext(ctx, &rows,
|
|
`SELECT * FROM office_agent_runtime`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result := make(map[string]*RuntimeState, len(rows))
|
|
for _, row := range rows {
|
|
result[row.AgentID] = row
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// UpdateRuntimeLastRunFinished records the time an agent's run finished.
|
|
func (r *Repository) UpdateRuntimeLastRunFinished(ctx context.Context, agentID string, finishedAt time.Time) error {
|
|
now := time.Now().UTC()
|
|
_, err := r.db.ExecContext(ctx, r.db.Rebind(`
|
|
UPDATE office_agent_runtime
|
|
SET last_run_finished_at = ?, updated_at = ?
|
|
WHERE agent_id = ?
|
|
`), finishedAt, now, agentID)
|
|
return err
|
|
}
|
|
|
|
// ListDistinctTriggerRoutineIDs returns the set of routine IDs that have triggers in the DB.
|
|
func (r *Repository) ListDistinctTriggerRoutineIDs(ctx context.Context) ([]string, error) {
|
|
var ids []string
|
|
err := r.ro.SelectContext(ctx, &ids,
|
|
`SELECT DISTINCT routine_id FROM office_routine_triggers`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ids, nil
|
|
}
|
|
|
|
// DeleteTriggersByRoutineID removes all triggers for a routine.
|
|
func (r *Repository) DeleteTriggersByRoutineID(ctx context.Context, routineID string) error {
|
|
_, err := r.db.ExecContext(ctx, r.db.Rebind(
|
|
`DELETE FROM office_routine_triggers WHERE routine_id = ?`), routineID)
|
|
return err
|
|
}
|
|
|
|
// DeleteRunsByRoutineID removes all runs for a routine.
|
|
func (r *Repository) DeleteRunsByRoutineID(ctx context.Context, routineID string) error {
|
|
_, err := r.db.ExecContext(ctx, r.db.Rebind(
|
|
`DELETE FROM office_routine_runs WHERE routine_id = ?`), routineID)
|
|
return err
|
|
}
|
|
|
|
// DeleteBudgetPoliciesForRemovedScopes removes budget policies whose scope_id
|
|
// is no longer in the provided set of valid IDs.
|
|
func (r *Repository) DeleteBudgetPoliciesForRemovedScopes(
|
|
ctx context.Context, scopeType string, validIDs []string,
|
|
) (int64, error) {
|
|
if len(validIDs) == 0 {
|
|
res, err := r.db.ExecContext(ctx, r.db.Rebind(
|
|
`DELETE FROM office_budget_policies WHERE scope_type = ?`), scopeType)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return res.RowsAffected()
|
|
}
|
|
|
|
// Build placeholders for the IN clause.
|
|
query := `DELETE FROM office_budget_policies WHERE scope_type = ? AND scope_id NOT IN (`
|
|
args := []interface{}{scopeType}
|
|
for i, id := range validIDs {
|
|
if i > 0 {
|
|
query += ","
|
|
}
|
|
query += "?"
|
|
args = append(args, id)
|
|
}
|
|
query += ")"
|
|
res, err := r.db.ExecContext(ctx, r.db.Rebind(query), args...)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return res.RowsAffected()
|
|
}
|
|
|
|
// DeleteChannelsForRemovedAgents removes channels whose agent_profile_id
|
|
// is no longer in the provided set of valid IDs.
|
|
func (r *Repository) DeleteChannelsForRemovedAgents(
|
|
ctx context.Context, validAgentIDs []string,
|
|
) (int64, error) {
|
|
if len(validAgentIDs) == 0 {
|
|
res, err := r.db.ExecContext(ctx,
|
|
`DELETE FROM office_channels`)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return res.RowsAffected()
|
|
}
|
|
|
|
query := `DELETE FROM office_channels WHERE agent_profile_id NOT IN (`
|
|
args := make([]interface{}, 0, len(validAgentIDs))
|
|
for i, id := range validAgentIDs {
|
|
if i > 0 {
|
|
query += ","
|
|
}
|
|
query += "?"
|
|
args = append(args, id)
|
|
}
|
|
query += ")"
|
|
res, err := r.db.ExecContext(ctx, r.db.Rebind(query), args...)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return res.RowsAffected()
|
|
}
|