usage

package
v0.0.32 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 8, 2026 License: AGPL-3.0 Imports: 29 Imported by: 0

Documentation

Index

Constants

View Source
const (
	TerminationModeNone   TerminationMode = "none"
	TerminationModeBlock  TerminationMode = "block"
	TerminationModePaused TerminationMode = "paused"
	TerminationModeAllow  TerminationMode = "allow"

	TerminationModeSourceApplication TerminationModeSource = "application"
	TerminationModeSourceCustomRules TerminationModeSource = "custom_rules"
	TerminationModeSourceWhitelist   TerminationModeSource = "whitelist"
	TerminationModeSourcePaused      TerminationModeSource = "paused"

	ClassificationSourceUserSet     ClassificationSource = "user_set"
	ClassificationSourceObviously   ClassificationSource = "obviously"
	ClassificationSourceCloudLLM    ClassificationSource = "llm"
	ClassificationSourceCustomRules ClassificationSource = "custom_rules"

	IdleApplicationName = "Idle"

	ClassificationNone        Classification = "none"
	ClassificationProductive  Classification = "productive"
	ClassificationDistracting Classification = "distracting"
	ClassificationNeutral     Classification = "neutral"
	ClassificationSystem      Classification = "system"
)

Variables

This section is empty.

Functions

func FetchMainContent

func FetchMainContent(ctx context.Context, rawURL string) (string, error)

Types

type Application

type Application struct {
	// mandatory fields
	ID   int64  `json:"id" gorm:"primaryKey;autoIncrement;not null"`
	Name string `json:"name" gorm:"uniqueIndex:idx_name_hostname_id;not null"`

	// optional fields
	Icon     *string `json:"icon"` // either app icon or favicon if host is present
	Hostname *string `json:"hostname" gorm:"uniqueIndex:idx_name_hostname_id"`
	Domain   *string `json:"domain"`

	// darwin only
	BundleID *string `json:"bundle_id"`
}

Application represents a unique application that has been used by the user Application is unique by name and hostname that is

  • if the application is not a browser it is unique by name
  • if the application is a browser it is unique by name and domain eg. Chrome + google.com != Chrome + youtube.com, each of them will have its own application

func (Application) TableName added in v0.0.25

func (a Application) TableName() string

type ApplicationTagsSlice added in v0.0.25

type ApplicationTagsSlice []ApplicationUsageTags

func (ApplicationTagsSlice) Tags added in v0.0.25

func (a ApplicationTagsSlice) Tags() []string

type ApplicationUsage

type ApplicationUsage struct {
	// mandatory fields
	ID              int64           `json:"id" gorm:"primaryKey;autoIncrement;not null"`
	WindowTitle     string          `json:"window_title" gorm:"not null"`
	StartedAt       int64           `json:"started_at" gorm:"not null"`
	Classification  Classification  `json:"classification" gorm:"index:idx_classification"`
	TerminationMode TerminationMode `json:"termination_mode" gorm:"not null"`
	ExecutablePath  string          `json:"executable_path" gorm:"not null"`

	// optional fields
	BrowserURL      *string `json:"browser_url" gorm:"type:text"`
	EndedAt         *int64  `json:"ended_at"`
	DurationSeconds *int    `json:"duration_seconds"`

	ClassificationError      *string               `gorm:"index:idx_classification_error" json:"classification_error"`
	ClassificationConfidence *float32              `json:"classification_confidence"`
	ClassificationReasoning  *string               `json:"classification_reasoning"`
	ClassificationSource     *ClassificationSource `json:"classification_source"`

	DetectedProject              *string `gorm:"index:idx_detected_project" json:"detected_project"`
	DetectedCommunicationChannel *string `gorm:"index:idx_detected_communication_channel" json:"detected_communication_channel"`

	TerminationReasoning *string                `json:"termination_reasoning"`
	TerminationSource    *TerminationModeSource `json:"termination_mode_source"`
	TerminationError     *string                `gorm:"index:idx_termination_mode_error" json:"termination_mode_error"`

	SandboxContext  *string `json:"sandbox_context" gorm:"type:text;nullable"`
	SandboxResponse *string `json:"sandbox_response" gorm:"type:text;nullable"`
	SandboxLogs     *string `json:"sandbox_logs" gorm:"type:text;nullable"`

	// relations
	Tags          []ApplicationUsageTags `gorm:"foreignKey:UsageID" json:"tags"`
	ApplicationID int64                  `json:"application_id"`
	Application   Application            `gorm:"foreignKey:ApplicationID" json:"application"`
}

func (*ApplicationUsage) Same

func (a *ApplicationUsage) Same(windowTitle, appName string, bundleID, url *string) bool

Same returns true if the application usage is the same as the given application usage

Application usage is considered the same if:

  • The url is not nil and the url is the same
  • The application name and title are the same, eg. Slack + general discussion != Slack + funny memes

func (*ApplicationUsage) TableName

func (a *ApplicationUsage) TableName() string

type ApplicationUsageTags

type ApplicationUsageTags struct {
	ID      int64  `gorm:"primaryKey;autoIncrement" json:"id"`
	Tag     string `json:"tag"`
	UsageID int64  `json:"usage_id" gorm:"index:idx_usage_id"`
}

type BlockedBreakdown added in v0.0.31

type BlockedBreakdown struct {
	Name  string `json:"name"`
	Count int    `json:"count"`
}

type Classification

type Classification string

type ClassificationResponse

type ClassificationResponse struct {
	Classification               Classification       `json:"classification"`
	ClassificationSource         ClassificationSource `json:"classification_source"`
	Reasoning                    string               `json:"reasoning"`
	ConfidenceScore              float32              `json:"confidence_score"`
	DetectedProject              string               `json:"detected_project"`
	DetectedCommunicationChannel string               `json:"detected_communication_channel"`
	Tags                         []string             `json:"tags"`

	SandboxContext  string  `json:"sandbox_context"`
	SandboxResponse *string `json:"sandbox_response"`
	SandboxLogs     string  `json:"sandbox_logs"`
}

type ClassificationSource

type ClassificationSource string

type ClassifyRequest

type ClassifyRequest struct {
	AppName        string         `json:"app_name"`
	ExecutablePath string         `json:"executable_path"`
	Hostname       string         `json:"hostname"`
	URL            string         `json:"url"`
	Classification Classification `json:"classification"`
}

type CommunicationBreakdown added in v0.0.31

type CommunicationBreakdown struct {
	Name    string `json:"name"`
	Minutes int    `json:"minutes"`
}

type DayInsights

type DayInsights struct {
	ProductivityScore            ProductivityScore
	ProductivityPerHourBreakdown ProductivityPerHourBreakdown
	LLMDailySummary              *LLMDailySummary
	TopDistractions              []DistractionBreakdown
	TopBlocked                   []BlockedBreakdown
	ProjectBreakdown             []ProjectBreakdown
	CommunicationBreakdown       []CommunicationBreakdown
}

type DistractionBreakdown added in v0.0.31

type DistractionBreakdown struct {
	Name    string `json:"name"`
	Minutes int    `json:"minutes"`
}

type ExecutionLogType added in v0.0.21

type ExecutionLogType string
const (
	ExecutionLogTypeClassification  ExecutionLogType = "classification"
	ExecutionLogTypeTerminationMode ExecutionLogType = "termination_mode"
)

type GetUsageListOptions

type GetUsageListOptions struct {
	Date            *time.Time
	Page            *int
	PageSize        *int
	StartedAt       *time.Time
	EndedAt         *time.Time
	TerminationMode *TerminationMode
	Classification  *Classification
	ApplicationID   *int64
	ApplicationName *string
	Hostname        *string
	BundleID        *string
}

type LLMAppTimeSummary added in v0.0.31

type LLMAppTimeSummary struct {
	App     string `json:"app"`
	Minutes int    `json:"minutes"`
	Visits  int    `json:"visits"`
}

type LLMDailySummary added in v0.0.31

type LLMDailySummary struct {
	ID         int64  `json:"id" gorm:"primaryKey;autoIncrement"`
	Date       string `json:"date" gorm:"uniqueIndex;not null"`
	Headline   string `json:"headline"`
	Narrative  string `json:"narrative"`
	KeyPattern string `json:"key_pattern"`
	Wins       string `json:"wins"` // JSON array of strings
	Suggestion string `json:"suggestion"`
	DayVibe    string `json:"day_vibe"`

	ContextSwitchCount  int `json:"context_switch_count"`
	LongestFocusMinutes int `json:"longest_focus_minutes"`
	DeepWorkMinutes     int `json:"deep_work_minutes"`
	BlockedAttemptCount int `json:"blocked_attempt_count"`

	CreatedAt int64 `json:"created_at"`
}

LLMDailySummary is the persisted daily summary generated by the LLM.

func (LLMDailySummary) TableName added in v0.0.31

func (LLMDailySummary) TableName() string

type LLMDaySummaryInput added in v0.0.31

type LLMDaySummaryInput struct {
	Date                    string                  `json:"date"`
	TotalProductiveMinutes  int                     `json:"total_productive_minutes"`
	TotalDistractiveMinutes int                     `json:"total_distractive_minutes"`
	FocusScore              int                     `json:"focus_score"`
	ContextSwitchCount      int                     `json:"context_switch_count"`
	LongestFocusStretchMin  int                     `json:"longest_focus_stretch_min"`
	DeepWorkSessions        []LLMDeepWorkSession    `json:"deep_work_sessions"`
	DeepWorkTotalMinutes    int                     `json:"deep_work_total_minutes"`
	DistractionCascades     []LLMDistractionCascade `json:"distraction_cascades"`
	TopDistractions         []LLMAppTimeSummary     `json:"top_distractions"`
	TopProductiveApps       []LLMAppTimeSummary     `json:"top_productive_apps"`
	MostProductiveHours     string                  `json:"most_productive_hours"`
	MostDistractiveHours    string                  `json:"most_distractive_hours"`
	BlockedAttemptCount     int                     `json:"blocked_attempt_count"`
	ProtectionPauseCount    int                     `json:"protection_pause_count"`
	AvgFocusScoreLast7Days  int                     `json:"avg_focus_score_last_7_days"`
	FocusScoreTrend         string                  `json:"focus_score_trend"`
}

LLMDaySummaryInput is the pre-computed data that gets serialized and sent to the LLM. It is never stored -- only used as an intermediate representation.

type LLMDeepWorkSession added in v0.0.31

type LLMDeepWorkSession struct {
	Start   string `json:"start"`
	End     string `json:"end"`
	App     string `json:"app"`
	Minutes int    `json:"minutes"`
}

type LLMDistractionCascade added in v0.0.31

type LLMDistractionCascade struct {
	TriggerTime    string   `json:"trigger_time"`
	TriggerApp     string   `json:"trigger_app"`
	CascadeApps    []string `json:"cascade_apps"`
	TotalMinutes   int      `json:"total_minutes"`
	ReturnedToWork string   `json:"returned_to_work_at"`
}

type MetaData

type MetaData struct {
	Property string
	Content  string
}

type Option

type Option func(*Service)

Option is a function that configures a Service.

func WithAppBlocker

func WithAppBlocker(appBlocker func(appName, title, reason string, tags []string, browserURL *string)) Option

WithAppBlocker configures the Service with a function to call when an application is blocked.

func WithGenaiClient

func WithGenaiClient(genaiClient *genai.Client) Option

WithGenaiClient configures the Service with a GenaiClient interface to allow using the Genai client.

func WithLLMDailySummaryReady added in v0.0.31

func WithLLMDailySummaryReady(fn func(summary LLMDailySummary)) Option

WithLLMDailySummaryReady configures the Service with a function to call when a daily LLM summary is generated.

func WithProtectionPaused

func WithProtectionPaused(onProtectionPaused func(pause ProtectionPause)) Option

WithProtectionPaused configures the Service with a function to call when protection is paused.

func WithProtectionResumed

func WithProtectionResumed(onProtectionResumed func(pause ProtectionPause)) Option

WithProtectionResumed configures the Service with a function to call when protection is resumed.

func WithSettingsService

func WithSettingsService(settingsService *settings.Service) Option

WithSettingsService configures the Service with a SettingsService interface to allow accessing settings from the database.

type PauseRequets

type PauseRequets struct {
	DurationSeconds int    `json:"duration_seconds"`
	Reason          string `json:"reason"`
}

type ProductivityPerHourBreakdown

type ProductivityPerHourBreakdown map[int]ProductivityScore

type ProductivityScore

type ProductivityScore struct {
	ProductiveSeconds  int
	DistractiveSeconds int
	OtherSeconds       int
	ProductivityScore  int
}

type ProjectBreakdown added in v0.0.31

type ProjectBreakdown struct {
	Name    string `json:"name"`
	Minutes int    `json:"minutes"`
}

type ProtectionPause

type ProtectionPause struct {
	ID                       int64  `gorm:"primaryKey;autoIncrement" json:"id"`
	RequestedDurationSeconds int    `json:"requested_duration_seconds"`
	ActualDurationSeconds    int    `json:"actual_duration_seconds"`
	ResumedAt                int64  `json:"resumed_at"`
	ResumedReason            string `json:"resumed_reason"`
	CreatedAt                int64  `json:"created_at"`
	Reason                   string `json:"reason"`
}

func (*ProtectionPause) TableName

func (p *ProtectionPause) TableName() string

type ProtectionWhitelist

type ProtectionWhitelist struct {
	ID int64 `gorm:"primaryKey;autoIncrement" json:"id"`
	// ExpiresAt should be pre-calculated and set to the time when the whitelist expires
	ExpiresAt int64 `json:"expires_at"`

	AppName  string  `gorm:"uniqueIndex:idx_allow_usage_identity" json:"appname"`
	Hostname *string `gorm:"uniqueIndex:idx_allow_usage_identity" json:"hostname"`
}

func (*ProtectionWhitelist) TableName

func (p *ProtectionWhitelist) TableName() string

type SandboxExecutionLog

type SandboxExecutionLog struct {
	ID         int64   `gorm:"primaryKey;autoIncrement" json:"id"`
	Context    string  `json:"context" gorm:"type:text;nullable"`
	Response   *string `json:"response" gorm:"type:text;nullable"`
	Logs       string  `json:"logs" gorm:"type:text;nullable"`
	CreatedAt  int64   `json:"created_at" gorm:"index:idx_created_at"`
	FinishedAt *int64  `json:"finished_at" gorm:"index:idx_finished_at;nullable"`
	Error      *string `json:"error" gorm:"type:text;nullable"`
	Type       string  `json:"type" gorm:"index:idx_type"`
}

type Service

type Service struct {

	// channel to receive usage updates
	UsageUpdates chan *ApplicationUsage
	// contains filtered or unexported fields
}

func NewService

func NewService(ctx context.Context, db *gorm.DB, options ...Option) (*Service, error)

func (*Service) CalculateTerminationMode

func (s *Service) CalculateTerminationMode(ctx context.Context, appUsage *ApplicationUsage) (TerminationDecision, error)

CalculateTerminationMode determines whether an application or website should be blocked, allowed, or paused based on classification, custom rules, protection status, and whitelist entries.

This function evaluates multiple factors in order of priority: 1. Custom rules (if configured) - highest priority 2. Classification - non-distracting usage is always allowed 3. Protection pause status - if protection is paused, usage is allowed 4. Whitelist entries - temporarily whitelisted bundle ID/hostname combinations are allowed 5. Default blocking - distracting usage is blocked when protection is active

Parameters:

  • bundleID: The application bundle identifier (e.g., "com.example.app")
  • hostname: The website hostname (e.g., "example.com") - empty string for non-browser apps
  • domain: The domain name extracted from the URL
  • url: The full URL being accessed
  • classification: The classification result indicating whether the usage is distracting
  • terminationMode: The requested termination mode (may be overridden by custom rules)

Returns:

  • TerminationDecision: A decision containing the mode (Allow/Block/Paused), reasoning, and source
  • error: Database error if protection status or whitelist lookup fails

func (*Service) ClassifyCustomRules

func (s *Service) ClassifyCustomRules(ctx context.Context, appName string, url *string, nowTime *time.Time) (*ClassificationResponse, error)

func (*Service) ClassifyCustomRulesWithSandbox

func (s *Service) ClassifyCustomRulesWithSandbox(ctx context.Context, sandboxCtx sandboxContext) (*ClassificationResponse, error)

func (*Service) ClassifyWithLLM

func (s *Service) ClassifyWithLLM(ctx context.Context, appName, title string, url *string) (*ClassificationResponse, error)

func (*Service) GenerateLLMDailySummaryIfNeeded added in v0.0.31

func (s *Service) GenerateLLMDailySummaryIfNeeded(ctx context.Context) error

GenerateLLMDailySummaryIfNeeded checks if it's time to generate yesterday's summary and produces one if it doesn't already exist.

func (*Service) GetDayInsights

func (s *Service) GetDayInsights(date time.Time) (DayInsights, error)

func (*Service) GetPauseHistory

func (s *Service) GetPauseHistory(days int) ([]ProtectionPause, error)

GetPauseHistory retrieves the history of protection pauses within the specified number of days.

Parameters:

  • days: The number of days to look back (e.g., 7 for one week)

Returns:

  • []ProtectionPause: A slice of pause records ordered by creation time (newest first)
  • error: Database error if the query fails

func (*Service) GetProtectionStatus

func (s *Service) GetProtectionStatus() (ProtectionPause, error)

GetProtectionStatus retrieves the current protection pause status.

It queries for an active ProtectionPause record where ResumedAt is greater than the current time (indicating the pause is still active and hasn't been resumed yet). When a pause is created, ResumedAt is set to a future timestamp representing when the pause should automatically expire. When manually resumed, ResumedAt is updated to the current time, making it no longer match this query.

Returns:

  • ProtectionPause: The active pause record if protection is currently paused, or an empty ProtectionPause (ID == 0) if protection is active (not paused)
  • error: Database error if the query fails

func (*Service) GetSandboxExecutionLogs added in v0.0.21

func (s *Service) GetSandboxExecutionLogs(logType string, search string, page, pageSize int) ([]SandboxExecutionLog, error)

GetSandboxExecutionLogs retrieves paginated sandbox execution logs from the database. It supports fuzzy search on context and response fields, and filtering by log type. Only returns logs from the last 7 days.

func (*Service) GetUsageAggregation added in v0.0.24

func (s *Service) GetUsageAggregation(options GetUsageListOptions) ([]UsageAggregation, error)

func (*Service) GetUsageList

func (s *Service) GetUsageList(options GetUsageListOptions) ([]ApplicationUsage, error)

GetUsageList returns a list of application usages for the given date and pagination options

Parameters:

  • options: GetUsageListOptions
  • Date: The date to get usages for - useful for detailed analysis of a specific day usage
  • Page: The page number to get
  • PageSize: The number of usages per page

Returns:

  • []ApplicationUsage: The list of application usages

func (*Service) GetWhitelist

func (s *Service) GetWhitelist() ([]ProtectionWhitelist, error)

GetWhitelist returns all active whitelist entries that haven't expired.

Returns:

  • []ProtectionWhitelist: A slice of active whitelist entries
  • error: Database error if the query fails

func (*Service) IdleChanged

func (s *Service) IdleChanged(ctx context.Context, isIdle bool) error

IdleChanged is called when the idle state of the user changes (e.g. user starts or stops using the computer)

func (*Service) PauseProtection

func (s *Service) PauseProtection(durationSeconds int, reason string) (ProtectionPause, error)

PauseProtection temporarily disables focus protection for the specified duration.

The function starts a background goroutine that automatically resumes protection after the duration expires. The pause state is persisted to the database, allowing the application to recover pause state across restarts.

Parameters:

  • duration: The duration to pause protection (must be > 0)

Returns:

  • error: Returns an error if protection is already paused

Side effects:

  • Creates a ProtectionPause record in the database
  • Emits a state update via the state channel
  • Spawns a goroutine that calls ResumeProtection after the duration

func (*Service) RegisterHTTPHandlers

func (s *Service) RegisterHTTPHandlers(r *chi.Mux)

func (*Service) RemoveWhitelist

func (s *Service) RemoveWhitelist(id int64) error

RemoveWhitelist removes a whitelist entry by ID.

Parameters:

  • id: The ID of the whitelist entry to remove

Returns:

  • error: Database error if the deletion fails

func (*Service) ResumeProtection

func (s *Service) ResumeProtection(reason string) (ProtectionPause, error)

ResumeProtection re-enables focus protection and records the reason for resumption.

This function is called either automatically when a pause duration expires, or manually by the user to end a pause early. The reason is persisted to the database for auditing and analytics purposes.

Parameters:

  • reason: A human-readable explanation for why protection was resumed (e.g., "protection paused for 5m0s expired" or "user manually resumed")

Returns:

  • error: Returns an error if protection is not currently paused

Side effects:

  • Updates the ProtectionPause record in the database with ResumedAt timestamp
  • Clears the pause state and emits a state update via the state channel

func (*Service) TitleChanged

func (s *Service) TitleChanged(ctx context.Context, executablePath, windowTitle, appName, icon string, bundleID, url *string) error

TitleChanged is called when the title of the current application changes, whether it's a new application or the same application title has changed

func (*Service) Whitelist

func (s *Service) Whitelist(appname string, hostname string, duration time.Duration) error

Whitelist temporarily allows a specific blocked usage (by bundle ID and hostname) for the specified duration.

This function creates a ProtectionWhitelist entry that allows the specified application or website to bypass focus protection for a limited time. This enables users to temporarily access blocked content without pausing all protection. The whitelist entry is persisted to the database and can be checked during termination decision evaluation.

Parameters:

  • bundleID: The application bundle identifier (e.g., "com.example.app")
  • hostname: The website hostname (e.g., "example.com") - empty string for non-browser apps
  • duration: The duration to allow the usage (defaults to 1 hour if 0)

Returns:

  • error: Database error if the whitelist entry creation fails

Side effects:

  • Creates a ProtectionWhitelist record in the database with expiration timestamp

type TerminationDecision

type TerminationDecision struct {
	Mode      TerminationMode
	Reasoning string
	Source    TerminationModeSource
}

type TerminationMode

type TerminationMode string

type TerminationModeSource

type TerminationModeSource string

type UnpauseRequest

type UnpauseRequest struct {
	Reason string `json:"reason"`
}

type UnwhitelistRequest added in v0.0.24

type UnwhitelistRequest struct {
	ID int64 `json:"id"`
}

type UsageAggregation added in v0.0.24

type UsageAggregation struct {
	Application   Application `json:"application"`
	TotalDuration int         `json:"total_duration"` // in seconds
	UsageCount    int         `json:"usage_count"`
}

type WhitelistRequest added in v0.0.24

type WhitelistRequest struct {
	AppName         string `json:"app_name"`
	Hostname        string `json:"hostname"`
	DurationSeconds int    `json:"duration_seconds"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL