Files
osmedeus/internal/core/workflow.go
T
j3ssie f5840272c5 feat: add run cancellation, event enhancements, and performance optimizations
Major features:
- Add run registry for tracking active runs with PID management
- Add API-based run cancellation with process termination
- Add event trigger input vars syntax for multi-variable extraction
- Add filter_functions with utility function support in triggers
- Add event envelope injection for full event context in workflows
- Add write coordinator for batched database operations

API improvements:
- Add logout endpoint and diffs endpoints for assets/vulnerabilities
- Add step-results listing endpoint
- Update schedule model with target, workspace, params fields
- Change run_id to run_uuid across API responses

Performance:
- Add compiled JS program caching for 60-80% faster loop conditions
- Add parallel shard rendering for 20-40% faster workflow startup
- Add memory-mapped I/O for large file line counting
- Add efficient output buffer combining in runners
- Add mtime-based cache invalidation for workflow loader

Other changes:
- Rename trigger field from trigger to triggers in workflow YAML
- Disable pongo2 HTML autoescape for shell command templates
- Update JWT expiration default to 1440 minutes (1 day)
- Change CORS default to reflect-origin for credentials support
- Add source_type field to events (run, eval, api)
- Skip copying core Unix tools to external-binaries
2026-01-24 01:11:33 +08:00

162 lines
4.9 KiB
Go

package core
import "strings"
// TagList is a comma-separated list of tags that parses to []string
type TagList []string
// UnmarshalYAML implements custom YAML unmarshaling for comma-separated tags
func (t *TagList) UnmarshalYAML(unmarshal func(interface{}) error) error {
var s string
if err := unmarshal(&s); err != nil {
return err
}
if s == "" {
*t = []string{}
return nil
}
parts := strings.Split(s, ",")
for i := range parts {
parts[i] = strings.TrimSpace(parts[i])
}
*t = parts
return nil
}
// Workflow represents either a Module or Flow
type Workflow struct {
Kind WorkflowKind `yaml:"kind"`
Name string `yaml:"name"`
Description string `yaml:"description"`
Tags TagList `yaml:"tags,omitempty"`
Hidden bool `yaml:"hidden,omitempty"`
Params []Param `yaml:"params"`
Triggers []Trigger `yaml:"triggers"`
Dependencies *Dependencies `yaml:"dependencies"`
Reports []Report `yaml:"reports"`
// Execution preferences (optional, can be overridden by CLI flags)
Preferences *Preferences `yaml:"preferences,omitempty"`
// Runner configuration (module-kind only)
Runner RunnerType `yaml:"runner,omitempty"`
RunnerConfig *RunnerConfig `yaml:"runner_config,omitempty"`
// Module-specific fields
Steps []Step `yaml:"steps,omitempty"`
// Flow-specific fields
Modules []ModuleRef `yaml:"modules,omitempty"`
// Inheritance support
Extends string `yaml:"extends,omitempty"`
Override *WorkflowOverride `yaml:"override,omitempty"`
// Internal metadata
FilePath string `yaml:"-"`
Checksum string `yaml:"-"`
ResolvedFrom string `yaml:"-"` // Tracks the parent workflow name if extended
}
// RunnerConfig holds configuration for different runner types
type RunnerConfig struct {
// Docker configuration
Image string `yaml:"image,omitempty"` // Docker image e.g., "ubuntu:latest"
Env map[string]string `yaml:"env,omitempty"` // Environment variables
Volumes []string `yaml:"volumes,omitempty"` // Volume mounts e.g., "/host:/container"
Network string `yaml:"network,omitempty"` // Network mode e.g., "host", "bridge"
Persistent bool `yaml:"persistent,omitempty"` // true=reuse container, false=ephemeral
// SSH configuration
Host string `yaml:"host,omitempty"` // SSH hostname or IP
Port int `yaml:"port,omitempty"` // SSH port (default 22)
User string `yaml:"user,omitempty"` // SSH username
KeyFile string `yaml:"key_file,omitempty"` // Path to SSH private key
Password string `yaml:"password,omitempty"` // SSH password (prefer key_file)
// Common configuration
WorkDir string `yaml:"workdir,omitempty"` // Working directory on remote/container
}
// ModuleRef references a module in a flow
type ModuleRef struct {
Name string `yaml:"name"`
Path string `yaml:"path"`
Params map[string]string `yaml:"params"`
DependsOn []string `yaml:"depends_on"`
Condition string `yaml:"condition"`
OnSuccess []Action `yaml:"on_success"`
OnError []Action `yaml:"on_error"`
Decision *DecisionConfig `yaml:"decision"`
}
// IsModule returns true if the workflow is a module
func (w *Workflow) IsModule() bool {
return w.Kind == KindModule
}
// IsFlow returns true if the workflow is a flow
func (w *Workflow) IsFlow() bool {
return w.Kind == KindFlow
}
// GetRequiredParams returns all required parameters
func (w *Workflow) GetRequiredParams() []Param {
var required []Param
for _, p := range w.Params {
if p.Required {
required = append(required, p)
}
}
return required
}
// HasTriggers returns true if the workflow has any triggers defined
func (w *Workflow) HasTriggers() bool {
return len(w.Triggers) > 0
}
// IsManualExecutionAllowed checks if manual (CLI) execution is allowed
// Returns true if:
// - No triggers are defined (default behavior allows manual)
// - A manual trigger exists and is enabled
// - No manual trigger is explicitly defined (default is enabled)
func (w *Workflow) IsManualExecutionAllowed() bool {
// If no triggers defined, manual is allowed (default behavior)
if len(w.Triggers) == 0 {
return true
}
// Look for explicit manual trigger
for _, t := range w.Triggers {
if t.On == TriggerManual {
return t.Enabled // Use explicit setting
}
}
// No manual trigger defined among other triggers, default to true
return true
}
// GetEventTriggers returns all event-type triggers
func (w *Workflow) GetEventTriggers() []Trigger {
var triggers []Trigger
for _, t := range w.Triggers {
if t.On == TriggerEvent && t.Enabled {
triggers = append(triggers, t)
}
}
return triggers
}
// GetCronTriggers returns all enabled cron-type triggers
func (w *Workflow) GetCronTriggers() []Trigger {
var triggers []Trigger
for _, t := range w.Triggers {
if t.On == TriggerCron && t.Enabled {
triggers = append(triggers, t)
}
}
return triggers
}