chore(ax): gofmt exported declaration comments
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
305aa0da6f
commit
c42cc4a6ce
65 changed files with 0 additions and 248 deletions
|
|
@ -10,7 +10,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// RunMode determines the execution strategy for a dispatched task.
|
// RunMode determines the execution strategy for a dispatched task.
|
||||||
//
|
|
||||||
type RunMode string
|
type RunMode string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -21,14 +20,12 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Spinner is the Clotho orchestrator that determines the fate of each task.
|
// Spinner is the Clotho orchestrator that determines the fate of each task.
|
||||||
//
|
|
||||||
type Spinner struct {
|
type Spinner struct {
|
||||||
Config ClothoConfig
|
Config ClothoConfig
|
||||||
Agents map[string]AgentConfig
|
Agents map[string]AgentConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSpinner creates a new Clotho orchestrator.
|
// NewSpinner creates a new Clotho orchestrator.
|
||||||
//
|
|
||||||
func NewSpinner(cfg ClothoConfig, agents map[string]AgentConfig) *Spinner {
|
func NewSpinner(cfg ClothoConfig, agents map[string]AgentConfig) *Spinner {
|
||||||
return &Spinner{
|
return &Spinner{
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// AgentConfig represents a single agent machine in the config file.
|
// AgentConfig represents a single agent machine in the config file.
|
||||||
//
|
|
||||||
type AgentConfig struct {
|
type AgentConfig struct {
|
||||||
Host string `yaml:"host" mapstructure:"host"`
|
Host string `yaml:"host" mapstructure:"host"`
|
||||||
QueueDir string `yaml:"queue_dir" mapstructure:"queue_dir"`
|
QueueDir string `yaml:"queue_dir" mapstructure:"queue_dir"`
|
||||||
|
|
@ -26,7 +25,6 @@ type AgentConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClothoConfig controls the orchestration strategy.
|
// ClothoConfig controls the orchestration strategy.
|
||||||
//
|
|
||||||
type ClothoConfig struct {
|
type ClothoConfig struct {
|
||||||
Strategy string `yaml:"strategy" mapstructure:"strategy"` // direct, clotho-verified
|
Strategy string `yaml:"strategy" mapstructure:"strategy"` // direct, clotho-verified
|
||||||
ValidationThreshold float64 `yaml:"validation_threshold" mapstructure:"validation_threshold"` // divergence limit (0.0-1.0)
|
ValidationThreshold float64 `yaml:"validation_threshold" mapstructure:"validation_threshold"` // divergence limit (0.0-1.0)
|
||||||
|
|
@ -35,7 +33,6 @@ type ClothoConfig struct {
|
||||||
|
|
||||||
// LoadAgents reads agent targets from config and returns a map of AgentConfig.
|
// LoadAgents reads agent targets from config and returns a map of AgentConfig.
|
||||||
// Returns an empty map (not an error) if no agents are configured.
|
// Returns an empty map (not an error) if no agents are configured.
|
||||||
//
|
|
||||||
func LoadAgents(cfg *config.Config) (map[string]AgentConfig, error) {
|
func LoadAgents(cfg *config.Config) (map[string]AgentConfig, error) {
|
||||||
var agents map[string]AgentConfig
|
var agents map[string]AgentConfig
|
||||||
if err := cfg.Get("agentci.agents", &agents); err != nil {
|
if err := cfg.Get("agentci.agents", &agents); err != nil {
|
||||||
|
|
@ -66,7 +63,6 @@ func LoadAgents(cfg *config.Config) (map[string]AgentConfig, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadActiveAgents returns only active agents.
|
// LoadActiveAgents returns only active agents.
|
||||||
//
|
|
||||||
func LoadActiveAgents(cfg *config.Config) (map[string]AgentConfig, error) {
|
func LoadActiveAgents(cfg *config.Config) (map[string]AgentConfig, error) {
|
||||||
all, err := LoadAgents(cfg)
|
all, err := LoadAgents(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -83,7 +79,6 @@ func LoadActiveAgents(cfg *config.Config) (map[string]AgentConfig, error) {
|
||||||
|
|
||||||
// LoadClothoConfig loads the Clotho orchestrator settings.
|
// LoadClothoConfig loads the Clotho orchestrator settings.
|
||||||
// Returns sensible defaults if no config is present.
|
// Returns sensible defaults if no config is present.
|
||||||
//
|
|
||||||
func LoadClothoConfig(cfg *config.Config) (ClothoConfig, error) {
|
func LoadClothoConfig(cfg *config.Config) (ClothoConfig, error) {
|
||||||
var cc ClothoConfig
|
var cc ClothoConfig
|
||||||
if err := cfg.Get("agentci.clotho", &cc); err != nil {
|
if err := cfg.Get("agentci.clotho", &cc); err != nil {
|
||||||
|
|
@ -102,7 +97,6 @@ func LoadClothoConfig(cfg *config.Config) (ClothoConfig, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveAgent writes an agent config entry to the config file.
|
// SaveAgent writes an agent config entry to the config file.
|
||||||
//
|
|
||||||
func SaveAgent(cfg *config.Config, name string, ac AgentConfig) error {
|
func SaveAgent(cfg *config.Config, name string, ac AgentConfig) error {
|
||||||
key := fmt.Sprintf("agentci.agents.%s", name)
|
key := fmt.Sprintf("agentci.agents.%s", name)
|
||||||
data := map[string]any{
|
data := map[string]any{
|
||||||
|
|
@ -131,7 +125,6 @@ func SaveAgent(cfg *config.Config, name string, ac AgentConfig) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveAgent removes an agent from the config file.
|
// RemoveAgent removes an agent from the config file.
|
||||||
//
|
|
||||||
func RemoveAgent(cfg *config.Config, name string) error {
|
func RemoveAgent(cfg *config.Config, name string) error {
|
||||||
var agents map[string]AgentConfig
|
var agents map[string]AgentConfig
|
||||||
if err := cfg.Get("agentci.agents", &agents); err != nil {
|
if err := cfg.Get("agentci.agents", &agents); err != nil {
|
||||||
|
|
@ -145,7 +138,6 @@ func RemoveAgent(cfg *config.Config, name string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListAgents returns all configured agents (active and inactive).
|
// ListAgents returns all configured agents (active and inactive).
|
||||||
//
|
|
||||||
func ListAgents(cfg *config.Config) (map[string]AgentConfig, error) {
|
func ListAgents(cfg *config.Config) (map[string]AgentConfig, error) {
|
||||||
var agents map[string]AgentConfig
|
var agents map[string]AgentConfig
|
||||||
if err := cfg.Get("agentci.agents", &agents); err != nil {
|
if err := cfg.Get("agentci.agents", &agents); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ var safeNameRegex = regexp.MustCompile(`^[a-zA-Z0-9\-\_\.]+$`)
|
||||||
|
|
||||||
// SanitizePath ensures a filename or directory name is safe and prevents path traversal.
|
// SanitizePath ensures a filename or directory name is safe and prevents path traversal.
|
||||||
// Returns the validated input unchanged.
|
// Returns the validated input unchanged.
|
||||||
//
|
|
||||||
func SanitizePath(input string) (string, error) {
|
func SanitizePath(input string) (string, error) {
|
||||||
if input == "" {
|
if input == "" {
|
||||||
return "", coreerr.E("agentci.SanitizePath", "path element is required", nil)
|
return "", coreerr.E("agentci.SanitizePath", "path element is required", nil)
|
||||||
|
|
@ -34,13 +33,11 @@ func SanitizePath(input string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidatePathElement validates a single local path element and returns its safe form.
|
// ValidatePathElement validates a single local path element and returns its safe form.
|
||||||
//
|
|
||||||
func ValidatePathElement(input string) (string, error) {
|
func ValidatePathElement(input string) (string, error) {
|
||||||
return SanitizePath(input)
|
return SanitizePath(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolvePathWithinRoot resolves a validated path element beneath a root directory.
|
// ResolvePathWithinRoot resolves a validated path element beneath a root directory.
|
||||||
//
|
|
||||||
func ResolvePathWithinRoot(root string, input string) (string, string, error) {
|
func ResolvePathWithinRoot(root string, input string) (string, string, error) {
|
||||||
safeName, err := ValidatePathElement(input)
|
safeName, err := ValidatePathElement(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -63,7 +60,6 @@ func ResolvePathWithinRoot(root string, input string) (string, string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateRemoteDir validates a remote directory path used over SSH.
|
// ValidateRemoteDir validates a remote directory path used over SSH.
|
||||||
//
|
|
||||||
func ValidateRemoteDir(dir string) (string, error) {
|
func ValidateRemoteDir(dir string) (string, error) {
|
||||||
if strings.TrimSpace(dir) == "" {
|
if strings.TrimSpace(dir) == "" {
|
||||||
return "", coreerr.E("agentci.ValidateRemoteDir", "directory is required", nil)
|
return "", coreerr.E("agentci.ValidateRemoteDir", "directory is required", nil)
|
||||||
|
|
@ -111,7 +107,6 @@ func ValidateRemoteDir(dir string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// JoinRemotePath joins validated remote path elements using forward slashes.
|
// JoinRemotePath joins validated remote path elements using forward slashes.
|
||||||
//
|
|
||||||
func JoinRemotePath(base string, parts ...string) (string, error) {
|
func JoinRemotePath(base string, parts ...string) (string, error) {
|
||||||
safeBase, err := ValidateRemoteDir(base)
|
safeBase, err := ValidateRemoteDir(base)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -138,13 +133,11 @@ func JoinRemotePath(base string, parts ...string) (string, error) {
|
||||||
|
|
||||||
// EscapeShellArg wraps a string in single quotes for safe remote shell insertion.
|
// EscapeShellArg wraps a string in single quotes for safe remote shell insertion.
|
||||||
// Prefer exec.Command arguments over constructing shell strings where possible.
|
// Prefer exec.Command arguments over constructing shell strings where possible.
|
||||||
//
|
|
||||||
func EscapeShellArg(arg string) string {
|
func EscapeShellArg(arg string) string {
|
||||||
return "'" + strings.ReplaceAll(arg, "'", "'\\''") + "'"
|
return "'" + strings.ReplaceAll(arg, "'", "'\\''") + "'"
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecureSSHCommand creates an SSH exec.Cmd with strict host key checking and batch mode.
|
// SecureSSHCommand creates an SSH exec.Cmd with strict host key checking and batch mode.
|
||||||
//
|
|
||||||
func SecureSSHCommand(host string, remoteCmd string) *exec.Cmd {
|
func SecureSSHCommand(host string, remoteCmd string) *exec.Cmd {
|
||||||
return exec.Command("ssh",
|
return exec.Command("ssh",
|
||||||
"-o", "StrictHostKeyChecking=yes",
|
"-o", "StrictHostKeyChecking=yes",
|
||||||
|
|
@ -156,7 +149,6 @@ func SecureSSHCommand(host string, remoteCmd string) *exec.Cmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MaskToken returns a masked version of a token for safe logging.
|
// MaskToken returns a masked version of a token for safe logging.
|
||||||
//
|
|
||||||
func MaskToken(token string) string {
|
func MaskToken(token string) string {
|
||||||
if len(token) < 8 {
|
if len(token) < 8 {
|
||||||
return "*****"
|
return "*****"
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddCollectCommands registers the 'collect' command and all subcommands.
|
// AddCollectCommands registers the 'collect' command and all subcommands.
|
||||||
//
|
|
||||||
func AddCollectCommands(root *cli.Command) {
|
func AddCollectCommands(root *cli.Command) {
|
||||||
collectCmd := &cli.Command{
|
collectCmd := &cli.Command{
|
||||||
Use: "collect",
|
Use: "collect",
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddForgeCommands registers the 'forge' command and all subcommands.
|
// AddForgeCommands registers the 'forge' command and all subcommands.
|
||||||
//
|
|
||||||
func AddForgeCommands(root *cli.Command) {
|
func AddForgeCommands(root *cli.Command) {
|
||||||
forgeCmd := &cli.Command{
|
forgeCmd := &cli.Command{
|
||||||
Use: "forge",
|
Use: "forge",
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddGiteaCommands registers the 'gitea' command and all subcommands.
|
// AddGiteaCommands registers the 'gitea' command and all subcommands.
|
||||||
//
|
|
||||||
func AddGiteaCommands(root *cli.Command) {
|
func AddGiteaCommands(root *cli.Command) {
|
||||||
giteaCmd := &cli.Command{
|
giteaCmd := &cli.Command{
|
||||||
Use: "gitea",
|
Use: "gitea",
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddScmCommands registers the 'scm' command and all subcommands.
|
// AddScmCommands registers the 'scm' command and all subcommands.
|
||||||
//
|
|
||||||
func AddScmCommands(root *cli.Command) {
|
func AddScmCommands(root *cli.Command) {
|
||||||
scmCmd := &cli.Command{
|
scmCmd := &cli.Command{
|
||||||
Use: "scm",
|
Use: "scm",
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ var httpClient = &http.Client{
|
||||||
}
|
}
|
||||||
|
|
||||||
// BitcoinTalkCollector collects forum posts from BitcoinTalk.
|
// BitcoinTalkCollector collects forum posts from BitcoinTalk.
|
||||||
//
|
|
||||||
type BitcoinTalkCollector struct {
|
type BitcoinTalkCollector struct {
|
||||||
// TopicID is the numeric topic identifier.
|
// TopicID is the numeric topic identifier.
|
||||||
TopicID string
|
TopicID string
|
||||||
|
|
@ -284,7 +283,6 @@ func formatPostMarkdown(num int, post btPost) string {
|
||||||
|
|
||||||
// ParsePostsFromHTML parses BitcoinTalk posts from raw HTML content.
|
// ParsePostsFromHTML parses BitcoinTalk posts from raw HTML content.
|
||||||
// This is exported for testing purposes.
|
// This is exported for testing purposes.
|
||||||
//
|
|
||||||
func ParsePostsFromHTML(htmlContent string) ([]btPost, error) {
|
func ParsePostsFromHTML(htmlContent string) ([]btPost, error) {
|
||||||
doc, err := html.Parse(strings.NewReader(htmlContent))
|
doc, err := html.Parse(strings.NewReader(htmlContent))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -294,17 +292,14 @@ func ParsePostsFromHTML(htmlContent string) ([]btPost, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatPostMarkdown is exported for testing purposes.
|
// FormatPostMarkdown is exported for testing purposes.
|
||||||
//
|
|
||||||
func FormatPostMarkdown(num int, author, date, content string) string {
|
func FormatPostMarkdown(num int, author, date, content string) string {
|
||||||
return formatPostMarkdown(num, btPost{Author: author, Date: date, Content: content})
|
return formatPostMarkdown(num, btPost{Author: author, Date: date, Content: content})
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchPageFunc is an injectable function type for fetching pages, used in testing.
|
// FetchPageFunc is an injectable function type for fetching pages, used in testing.
|
||||||
//
|
|
||||||
type FetchPageFunc func(ctx context.Context, url string) ([]btPost, error)
|
type FetchPageFunc func(ctx context.Context, url string) ([]btPost, error)
|
||||||
|
|
||||||
// BitcoinTalkCollectorWithFetcher wraps BitcoinTalkCollector with a custom fetcher for testing.
|
// BitcoinTalkCollectorWithFetcher wraps BitcoinTalkCollector with a custom fetcher for testing.
|
||||||
//
|
|
||||||
type BitcoinTalkCollectorWithFetcher struct {
|
type BitcoinTalkCollectorWithFetcher struct {
|
||||||
BitcoinTalkCollector
|
BitcoinTalkCollector
|
||||||
Fetcher FetchPageFunc
|
Fetcher FetchPageFunc
|
||||||
|
|
@ -312,7 +307,6 @@ type BitcoinTalkCollectorWithFetcher struct {
|
||||||
|
|
||||||
// SetHTTPClient replaces the package-level HTTP client.
|
// SetHTTPClient replaces the package-level HTTP client.
|
||||||
// Use this in tests to inject a custom transport or timeout.
|
// Use this in tests to inject a custom transport or timeout.
|
||||||
//
|
|
||||||
func SetHTTPClient(c *http.Client) {
|
func SetHTTPClient(c *http.Client) {
|
||||||
httpClient = c
|
httpClient = c
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Collector is the interface all collection sources implement.
|
// Collector is the interface all collection sources implement.
|
||||||
//
|
|
||||||
type Collector interface {
|
type Collector interface {
|
||||||
// Name returns a human-readable name for this collector.
|
// Name returns a human-readable name for this collector.
|
||||||
Name() string
|
Name() string
|
||||||
|
|
@ -24,7 +23,6 @@ type Collector interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config holds shared configuration for all collectors.
|
// Config holds shared configuration for all collectors.
|
||||||
//
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// Output is the storage medium for writing collected data.
|
// Output is the storage medium for writing collected data.
|
||||||
Output io.Medium
|
Output io.Medium
|
||||||
|
|
@ -49,7 +47,6 @@ type Config struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result holds the output of a collection run.
|
// Result holds the output of a collection run.
|
||||||
//
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
// Source identifies which collector produced this result.
|
// Source identifies which collector produced this result.
|
||||||
Source string
|
Source string
|
||||||
|
|
@ -70,7 +67,6 @@ type Result struct {
|
||||||
// NewConfig creates a Config with sensible defaults.
|
// NewConfig creates a Config with sensible defaults.
|
||||||
// It initialises a MockMedium for output if none is provided,
|
// It initialises a MockMedium for output if none is provided,
|
||||||
// sets up a rate limiter, state tracker, and event dispatcher.
|
// sets up a rate limiter, state tracker, and event dispatcher.
|
||||||
//
|
|
||||||
func NewConfig(outputDir string) *Config {
|
func NewConfig(outputDir string) *Config {
|
||||||
m := io.NewMockMedium()
|
m := io.NewMockMedium()
|
||||||
return &Config{
|
return &Config{
|
||||||
|
|
@ -83,7 +79,6 @@ func NewConfig(outputDir string) *Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConfigWithMedium creates a Config using the specified storage medium.
|
// NewConfigWithMedium creates a Config using the specified storage medium.
|
||||||
//
|
|
||||||
func NewConfigWithMedium(m io.Medium, outputDir string) *Config {
|
func NewConfigWithMedium(m io.Medium, outputDir string) *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
Output: m,
|
Output: m,
|
||||||
|
|
@ -95,7 +90,6 @@ func NewConfigWithMedium(m io.Medium, outputDir string) *Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MergeResults combines multiple results into a single aggregated result.
|
// MergeResults combines multiple results into a single aggregated result.
|
||||||
//
|
|
||||||
func MergeResults(source string, results ...*Result) *Result {
|
func MergeResults(source string, results ...*Result) *Result {
|
||||||
merged := &Result{Source: source}
|
merged := &Result{Source: source}
|
||||||
for _, r := range results {
|
for _, r := range results {
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Event represents a collection event.
|
// Event represents a collection event.
|
||||||
//
|
|
||||||
type Event struct {
|
type Event struct {
|
||||||
// Type is one of the Event* constants.
|
// Type is one of the Event* constants.
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
|
@ -50,19 +49,16 @@ type Event struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventHandler handles collection events.
|
// EventHandler handles collection events.
|
||||||
//
|
|
||||||
type EventHandler func(Event)
|
type EventHandler func(Event)
|
||||||
|
|
||||||
// Dispatcher manages event dispatch. Handlers are registered per event type
|
// Dispatcher manages event dispatch. Handlers are registered per event type
|
||||||
// and are called synchronously when an event is emitted.
|
// and are called synchronously when an event is emitted.
|
||||||
//
|
|
||||||
type Dispatcher struct {
|
type Dispatcher struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
handlers map[string][]EventHandler
|
handlers map[string][]EventHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDispatcher creates a new event dispatcher.
|
// NewDispatcher creates a new event dispatcher.
|
||||||
//
|
|
||||||
func NewDispatcher() *Dispatcher {
|
func NewDispatcher() *Dispatcher {
|
||||||
return &Dispatcher{
|
return &Dispatcher{
|
||||||
handlers: make(map[string][]EventHandler),
|
handlers: make(map[string][]EventHandler),
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import (
|
||||||
// Excavator runs multiple collectors as a coordinated operation.
|
// Excavator runs multiple collectors as a coordinated operation.
|
||||||
// It provides sequential execution with rate limit respect, state tracking
|
// It provides sequential execution with rate limit respect, state tracking
|
||||||
// for resume support, and aggregated results.
|
// for resume support, and aggregated results.
|
||||||
//
|
|
||||||
type Excavator struct {
|
type Excavator struct {
|
||||||
// Collectors is the list of collectors to run.
|
// Collectors is the list of collectors to run.
|
||||||
Collectors []Collector
|
Collectors []Collector
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@ type ghRepo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GitHubCollector collects issues and PRs from GitHub repositories.
|
// GitHubCollector collects issues and PRs from GitHub repositories.
|
||||||
//
|
|
||||||
type GitHubCollector struct {
|
type GitHubCollector struct {
|
||||||
// Org is the GitHub organisation.
|
// Org is the GitHub organisation.
|
||||||
Org string
|
Org string
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import (
|
||||||
var coinGeckoBaseURL = "https://api.coingecko.com/api/v3"
|
var coinGeckoBaseURL = "https://api.coingecko.com/api/v3"
|
||||||
|
|
||||||
// MarketCollector collects market data from CoinGecko.
|
// MarketCollector collects market data from CoinGecko.
|
||||||
//
|
|
||||||
type MarketCollector struct {
|
type MarketCollector struct {
|
||||||
// CoinID is the CoinGecko coin identifier (e.g. "bitcoin", "ethereum").
|
// CoinID is the CoinGecko coin identifier (e.g. "bitcoin", "ethereum").
|
||||||
CoinID string
|
CoinID string
|
||||||
|
|
@ -275,7 +274,6 @@ func formatMarketSummary(data *coinData) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatMarketSummary is exported for testing.
|
// FormatMarketSummary is exported for testing.
|
||||||
//
|
|
||||||
func FormatMarketSummary(data *coinData) string {
|
func FormatMarketSummary(data *coinData) string {
|
||||||
return formatMarketSummary(data)
|
return formatMarketSummary(data)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// PapersCollector collects papers from IACR and arXiv.
|
// PapersCollector collects papers from IACR and arXiv.
|
||||||
//
|
|
||||||
type PapersCollector struct {
|
type PapersCollector struct {
|
||||||
// Source is one of PaperSourceIACR, PaperSourceArXiv, or PaperSourceAll.
|
// Source is one of PaperSourceIACR, PaperSourceArXiv, or PaperSourceAll.
|
||||||
Source string
|
Source string
|
||||||
|
|
@ -409,7 +408,6 @@ func formatPaperMarkdown(ppr paper) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatPaperMarkdown is exported for testing.
|
// FormatPaperMarkdown is exported for testing.
|
||||||
//
|
|
||||||
func FormatPaperMarkdown(title string, authors []string, date, paperURL, source, abstract string) string {
|
func FormatPaperMarkdown(title string, authors []string, date, paperURL, source, abstract string) string {
|
||||||
return formatPaperMarkdown(paper{
|
return formatPaperMarkdown(paper{
|
||||||
Title: title,
|
Title: title,
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Processor converts collected data to clean markdown.
|
// Processor converts collected data to clean markdown.
|
||||||
//
|
|
||||||
type Processor struct {
|
type Processor struct {
|
||||||
// Source identifies the data source directory to process.
|
// Source identifies the data source directory to process.
|
||||||
Source string
|
Source string
|
||||||
|
|
@ -334,13 +333,11 @@ func jsonValueToMarkdown(b *strings.Builder, data any, depth int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTMLToMarkdown is exported for testing.
|
// HTMLToMarkdown is exported for testing.
|
||||||
//
|
|
||||||
func HTMLToMarkdown(content string) (string, error) {
|
func HTMLToMarkdown(content string) (string, error) {
|
||||||
return htmlToMarkdown(content)
|
return htmlToMarkdown(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSONToMarkdown is exported for testing.
|
// JSONToMarkdown is exported for testing.
|
||||||
//
|
|
||||||
func JSONToMarkdown(content string) (string, error) {
|
func JSONToMarkdown(content string) (string, error) {
|
||||||
return jsonToMarkdown(content)
|
return jsonToMarkdown(content)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// RateLimiter tracks per-source rate limiting to avoid overwhelming APIs.
|
// RateLimiter tracks per-source rate limiting to avoid overwhelming APIs.
|
||||||
//
|
|
||||||
type RateLimiter struct {
|
type RateLimiter struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
delays map[string]time.Duration
|
delays map[string]time.Duration
|
||||||
|
|
@ -33,7 +32,6 @@ var defaultDelays = map[string]time.Duration{
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRateLimiter creates a limiter with default delays.
|
// NewRateLimiter creates a limiter with default delays.
|
||||||
//
|
|
||||||
func NewRateLimiter() *RateLimiter {
|
func NewRateLimiter() *RateLimiter {
|
||||||
delays := make(map[string]time.Duration, len(defaultDelays))
|
delays := make(map[string]time.Duration, len(defaultDelays))
|
||||||
maps.Copy(delays, defaultDelays)
|
maps.Copy(delays, defaultDelays)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import (
|
||||||
// State tracks collection progress for incremental runs.
|
// State tracks collection progress for incremental runs.
|
||||||
// It persists entries to disk so that subsequent runs can resume
|
// It persists entries to disk so that subsequent runs can resume
|
||||||
// where they left off.
|
// where they left off.
|
||||||
//
|
|
||||||
type State struct {
|
type State struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
medium io.Medium
|
medium io.Medium
|
||||||
|
|
@ -23,7 +22,6 @@ type State struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateEntry tracks state for one source.
|
// StateEntry tracks state for one source.
|
||||||
//
|
|
||||||
type StateEntry struct {
|
type StateEntry struct {
|
||||||
// Source identifies the collector.
|
// Source identifies the collector.
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
|
|
@ -43,7 +41,6 @@ type StateEntry struct {
|
||||||
|
|
||||||
// NewState creates a state tracker that persists to the given path
|
// NewState creates a state tracker that persists to the given path
|
||||||
// using the provided storage medium.
|
// using the provided storage medium.
|
||||||
//
|
|
||||||
func NewState(m io.Medium, path string) *State {
|
func NewState(m io.Medium, path string) *State {
|
||||||
return &State{
|
return &State{
|
||||||
medium: m,
|
medium: m,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client wraps the Forgejo SDK client with config-based auth.
|
// Client wraps the Forgejo SDK client with config-based auth.
|
||||||
//
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
api *forgejo.Client
|
api *forgejo.Client
|
||||||
url string
|
url string
|
||||||
|
|
@ -25,7 +24,6 @@ type Client struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Forgejo API client for the given URL and token.
|
// New creates a new Forgejo API client for the given URL and token.
|
||||||
//
|
|
||||||
func New(url, token string) (*Client, error) {
|
func New(url, token string) (*Client, error) {
|
||||||
api, err := forgejo.NewClient(url, forgejo.SetToken(token))
|
api, err := forgejo.NewClient(url, forgejo.SetToken(token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,6 @@ const (
|
||||||
// 1. ~/.core/config.yaml keys: forge.token, forge.url
|
// 1. ~/.core/config.yaml keys: forge.token, forge.url
|
||||||
// 2. FORGE_TOKEN + FORGE_URL environment variables (override config file)
|
// 2. FORGE_TOKEN + FORGE_URL environment variables (override config file)
|
||||||
// 3. Provided flag overrides (highest priority; pass empty to skip)
|
// 3. Provided flag overrides (highest priority; pass empty to skip)
|
||||||
//
|
|
||||||
//
|
|
||||||
func NewFromConfig(flagURL, flagToken string) (*Client, error) {
|
func NewFromConfig(flagURL, flagToken string) (*Client, error) {
|
||||||
url, token, err := ResolveConfig(flagURL, flagToken)
|
url, token, err := ResolveConfig(flagURL, flagToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -44,7 +42,6 @@ func NewFromConfig(flagURL, flagToken string) (*Client, error) {
|
||||||
|
|
||||||
// ResolveConfig resolves the Forgejo URL and token from all config sources.
|
// ResolveConfig resolves the Forgejo URL and token from all config sources.
|
||||||
// Flag values take highest priority, then env vars, then config file.
|
// Flag values take highest priority, then env vars, then config file.
|
||||||
//
|
|
||||||
func ResolveConfig(flagURL, flagToken string) (url, token string, err error) {
|
func ResolveConfig(flagURL, flagToken string) (url, token string, err error) {
|
||||||
// Start with config file values
|
// Start with config file values
|
||||||
cfg, cfgErr := config.New()
|
cfg, cfgErr := config.New()
|
||||||
|
|
@ -78,7 +75,6 @@ func ResolveConfig(flagURL, flagToken string) (url, token string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveConfig persists the Forgejo URL and/or token to the config file.
|
// SaveConfig persists the Forgejo URL and/or token to the config file.
|
||||||
//
|
|
||||||
func SaveConfig(url, token string) error {
|
func SaveConfig(url, token string) error {
|
||||||
cfg, err := config.New()
|
cfg, err := config.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListIssuesOpts configures issue listing.
|
// ListIssuesOpts configures issue listing.
|
||||||
//
|
|
||||||
type ListIssuesOpts struct {
|
type ListIssuesOpts struct {
|
||||||
State string // "open", "closed", "all"
|
State string // "open", "closed", "all"
|
||||||
Labels []string // filter by label names
|
Labels []string // filter by label names
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import (
|
||||||
|
|
||||||
// PRMeta holds structural signals from a pull request,
|
// PRMeta holds structural signals from a pull request,
|
||||||
// used by the pipeline MetaReader for AI-driven workflows.
|
// used by the pipeline MetaReader for AI-driven workflows.
|
||||||
//
|
|
||||||
type PRMeta struct {
|
type PRMeta struct {
|
||||||
Number int64
|
Number int64
|
||||||
Title string
|
Title string
|
||||||
|
|
@ -29,7 +28,6 @@ type PRMeta struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comment represents a comment with metadata.
|
// Comment represents a comment with metadata.
|
||||||
//
|
|
||||||
type Comment struct {
|
type Comment struct {
|
||||||
ID int64
|
ID int64
|
||||||
Author string
|
Author string
|
||||||
|
|
|
||||||
11
git/git.go
11
git/git.go
|
|
@ -17,7 +17,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// RepoStatus represents the git status of a single repository.
|
// RepoStatus represents the git status of a single repository.
|
||||||
//
|
|
||||||
type RepoStatus struct {
|
type RepoStatus struct {
|
||||||
Name string
|
Name string
|
||||||
Path string
|
Path string
|
||||||
|
|
@ -46,7 +45,6 @@ func (s *RepoStatus) HasUnpulled() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatusOptions configures the status check.
|
// StatusOptions configures the status check.
|
||||||
//
|
|
||||||
type StatusOptions struct {
|
type StatusOptions struct {
|
||||||
// Paths is a list of repo paths to check
|
// Paths is a list of repo paths to check
|
||||||
Paths []string
|
Paths []string
|
||||||
|
|
@ -55,7 +53,6 @@ type StatusOptions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status checks git status for multiple repositories in parallel.
|
// Status checks git status for multiple repositories in parallel.
|
||||||
//
|
|
||||||
func Status(ctx context.Context, opts StatusOptions) []RepoStatus {
|
func Status(ctx context.Context, opts StatusOptions) []RepoStatus {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
results := make([]RepoStatus, len(opts.Paths))
|
results := make([]RepoStatus, len(opts.Paths))
|
||||||
|
|
@ -77,7 +74,6 @@ func Status(ctx context.Context, opts StatusOptions) []RepoStatus {
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatusIter returns an iterator over git status for multiple repositories.
|
// StatusIter returns an iterator over git status for multiple repositories.
|
||||||
//
|
|
||||||
func StatusIter(ctx context.Context, opts StatusOptions) iter.Seq[RepoStatus] {
|
func StatusIter(ctx context.Context, opts StatusOptions) iter.Seq[RepoStatus] {
|
||||||
return func(yield func(RepoStatus) bool) {
|
return func(yield func(RepoStatus) bool) {
|
||||||
results := Status(ctx, opts)
|
results := Status(ctx, opts)
|
||||||
|
|
@ -162,20 +158,17 @@ func getAheadBehind(ctx context.Context, path string) (ahead, behind int) {
|
||||||
|
|
||||||
// Push pushes commits for a single repository.
|
// Push pushes commits for a single repository.
|
||||||
// Uses interactive mode to support SSH passphrase prompts.
|
// Uses interactive mode to support SSH passphrase prompts.
|
||||||
//
|
|
||||||
func Push(ctx context.Context, path string) error {
|
func Push(ctx context.Context, path string) error {
|
||||||
return gitInteractive(ctx, path, "push")
|
return gitInteractive(ctx, path, "push")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pull pulls changes for a single repository.
|
// Pull pulls changes for a single repository.
|
||||||
// Uses interactive mode to support SSH passphrase prompts.
|
// Uses interactive mode to support SSH passphrase prompts.
|
||||||
//
|
|
||||||
func Pull(ctx context.Context, path string) error {
|
func Pull(ctx context.Context, path string) error {
|
||||||
return gitInteractive(ctx, path, "pull", "--rebase")
|
return gitInteractive(ctx, path, "pull", "--rebase")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNonFastForward checks if an error is a non-fast-forward rejection.
|
// IsNonFastForward checks if an error is a non-fast-forward rejection.
|
||||||
//
|
|
||||||
func IsNonFastForward(err error) bool {
|
func IsNonFastForward(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
|
|
@ -210,7 +203,6 @@ func gitInteractive(ctx context.Context, dir string, args ...string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushResult represents the result of a push operation.
|
// PushResult represents the result of a push operation.
|
||||||
//
|
|
||||||
type PushResult struct {
|
type PushResult struct {
|
||||||
Name string
|
Name string
|
||||||
Path string
|
Path string
|
||||||
|
|
@ -220,13 +212,11 @@ type PushResult struct {
|
||||||
|
|
||||||
// PushMultiple pushes multiple repositories sequentially.
|
// PushMultiple pushes multiple repositories sequentially.
|
||||||
// Sequential because SSH passphrase prompts need user interaction.
|
// Sequential because SSH passphrase prompts need user interaction.
|
||||||
//
|
|
||||||
func PushMultiple(ctx context.Context, paths []string, names map[string]string) []PushResult {
|
func PushMultiple(ctx context.Context, paths []string, names map[string]string) []PushResult {
|
||||||
return slices.Collect(PushMultipleIter(ctx, paths, names))
|
return slices.Collect(PushMultipleIter(ctx, paths, names))
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushMultipleIter returns an iterator that pushes repositories sequentially and yields results.
|
// PushMultipleIter returns an iterator that pushes repositories sequentially and yields results.
|
||||||
//
|
|
||||||
func PushMultipleIter(ctx context.Context, paths []string, names map[string]string) iter.Seq[PushResult] {
|
func PushMultipleIter(ctx context.Context, paths []string, names map[string]string) iter.Seq[PushResult] {
|
||||||
return func(yield func(PushResult) bool) {
|
return func(yield func(PushResult) bool) {
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
|
|
@ -275,7 +265,6 @@ func gitCommand(ctx context.Context, dir string, args ...string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GitError wraps a git command error with stderr output.
|
// GitError wraps a git command error with stderr output.
|
||||||
//
|
|
||||||
type GitError struct {
|
type GitError struct {
|
||||||
Err error
|
Err error
|
||||||
Stderr string
|
Stderr string
|
||||||
|
|
|
||||||
|
|
@ -13,58 +13,49 @@ import (
|
||||||
// Queries for git service
|
// Queries for git service
|
||||||
|
|
||||||
// QueryStatus requests git status for paths.
|
// QueryStatus requests git status for paths.
|
||||||
//
|
|
||||||
type QueryStatus struct {
|
type QueryStatus struct {
|
||||||
Paths []string
|
Paths []string
|
||||||
Names map[string]string
|
Names map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryDirtyRepos requests repos with uncommitted changes.
|
// QueryDirtyRepos requests repos with uncommitted changes.
|
||||||
//
|
|
||||||
type QueryDirtyRepos struct{}
|
type QueryDirtyRepos struct{}
|
||||||
|
|
||||||
// QueryAheadRepos requests repos with unpushed commits.
|
// QueryAheadRepos requests repos with unpushed commits.
|
||||||
//
|
|
||||||
type QueryAheadRepos struct{}
|
type QueryAheadRepos struct{}
|
||||||
|
|
||||||
// Tasks for git service
|
// Tasks for git service
|
||||||
|
|
||||||
// TaskPush requests git push for a path.
|
// TaskPush requests git push for a path.
|
||||||
//
|
|
||||||
type TaskPush struct {
|
type TaskPush struct {
|
||||||
Path string
|
Path string
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TaskPull requests git pull for a path.
|
// TaskPull requests git pull for a path.
|
||||||
//
|
|
||||||
type TaskPull struct {
|
type TaskPull struct {
|
||||||
Path string
|
Path string
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TaskPushMultiple requests git push for multiple paths.
|
// TaskPushMultiple requests git push for multiple paths.
|
||||||
//
|
|
||||||
type TaskPushMultiple struct {
|
type TaskPushMultiple struct {
|
||||||
Paths []string
|
Paths []string
|
||||||
Names map[string]string
|
Names map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceOptions for configuring the git service.
|
// ServiceOptions for configuring the git service.
|
||||||
//
|
|
||||||
type ServiceOptions struct {
|
type ServiceOptions struct {
|
||||||
WorkDir string
|
WorkDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Service provides git operations as a Core service.
|
// Service provides git operations as a Core service.
|
||||||
//
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
*core.ServiceRuntime[ServiceOptions]
|
*core.ServiceRuntime[ServiceOptions]
|
||||||
lastStatus []RepoStatus
|
lastStatus []RepoStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewService creates a git service factory.
|
// NewService creates a git service factory.
|
||||||
//
|
|
||||||
func NewService(opts ServiceOptions) func(*core.Core) (any, error) {
|
func NewService(opts ServiceOptions) func(*core.Core) (any, error) {
|
||||||
return func(c *core.Core) (any, error) {
|
return func(c *core.Core) (any, error) {
|
||||||
return &Service{
|
return &Service{
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client wraps the Gitea SDK client with config-based auth.
|
// Client wraps the Gitea SDK client with config-based auth.
|
||||||
//
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
api *gitea.Client
|
api *gitea.Client
|
||||||
url string
|
url string
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Gitea API client for the given URL and token.
|
// New creates a new Gitea API client for the given URL and token.
|
||||||
//
|
|
||||||
func New(url, token string) (*Client, error) {
|
func New(url, token string) (*Client, error) {
|
||||||
api, err := gitea.NewClient(url, gitea.SetToken(token))
|
api, err := gitea.NewClient(url, gitea.SetToken(token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,6 @@ const (
|
||||||
// 1. ~/.core/config.yaml keys: gitea.token, gitea.url
|
// 1. ~/.core/config.yaml keys: gitea.token, gitea.url
|
||||||
// 2. GITEA_TOKEN + GITEA_URL environment variables (override config file)
|
// 2. GITEA_TOKEN + GITEA_URL environment variables (override config file)
|
||||||
// 3. Provided flag overrides (highest priority; pass empty to skip)
|
// 3. Provided flag overrides (highest priority; pass empty to skip)
|
||||||
//
|
|
||||||
//
|
|
||||||
func NewFromConfig(flagURL, flagToken string) (*Client, error) {
|
func NewFromConfig(flagURL, flagToken string) (*Client, error) {
|
||||||
url, token, err := ResolveConfig(flagURL, flagToken)
|
url, token, err := ResolveConfig(flagURL, flagToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -44,7 +42,6 @@ func NewFromConfig(flagURL, flagToken string) (*Client, error) {
|
||||||
|
|
||||||
// ResolveConfig resolves the Gitea URL and token from all config sources.
|
// ResolveConfig resolves the Gitea URL and token from all config sources.
|
||||||
// Flag values take highest priority, then env vars, then config file.
|
// Flag values take highest priority, then env vars, then config file.
|
||||||
//
|
|
||||||
func ResolveConfig(flagURL, flagToken string) (url, token string, err error) {
|
func ResolveConfig(flagURL, flagToken string) (url, token string, err error) {
|
||||||
// Start with config file values
|
// Start with config file values
|
||||||
cfg, cfgErr := config.New()
|
cfg, cfgErr := config.New()
|
||||||
|
|
@ -78,7 +75,6 @@ func ResolveConfig(flagURL, flagToken string) (url, token string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveConfig persists the Gitea URL and/or token to the config file.
|
// SaveConfig persists the Gitea URL and/or token to the config file.
|
||||||
//
|
|
||||||
func SaveConfig(url, token string) error {
|
func SaveConfig(url, token string) error {
|
||||||
cfg, err := config.New()
|
cfg, err := config.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListIssuesOpts configures issue listing.
|
// ListIssuesOpts configures issue listing.
|
||||||
//
|
|
||||||
type ListIssuesOpts struct {
|
type ListIssuesOpts struct {
|
||||||
State string // "open", "closed", "all"
|
State string // "open", "closed", "all"
|
||||||
Page int
|
Page int
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import (
|
||||||
|
|
||||||
// PRMeta holds structural signals from a pull request,
|
// PRMeta holds structural signals from a pull request,
|
||||||
// used by the pipeline MetaReader for AI-driven workflows.
|
// used by the pipeline MetaReader for AI-driven workflows.
|
||||||
//
|
|
||||||
type PRMeta struct {
|
type PRMeta struct {
|
||||||
Number int64
|
Number int64
|
||||||
Title string
|
Title string
|
||||||
|
|
@ -29,7 +28,6 @@ type PRMeta struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comment represents a comment with metadata.
|
// Comment represents a comment with metadata.
|
||||||
//
|
|
||||||
type Comment struct {
|
type Comment struct {
|
||||||
ID int64
|
ID int64
|
||||||
Author string
|
Author string
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Separator mirrors filepath.Separator for Unix-style Core paths.
|
// Separator mirrors filepath.Separator for Unix-style Core paths.
|
||||||
//
|
|
||||||
const Separator = '/'
|
const Separator = '/'
|
||||||
|
|
||||||
// Abs mirrors filepath.Abs for the paths used in this repo.
|
// Abs mirrors filepath.Abs for the paths used in this repo.
|
||||||
//
|
|
||||||
func Abs(p string) (string, error) {
|
func Abs(p string) (string, error) {
|
||||||
if path.IsAbs(p) {
|
if path.IsAbs(p) {
|
||||||
return path.Clean(p), nil
|
return path.Clean(p), nil
|
||||||
|
|
@ -25,31 +23,26 @@ func Abs(p string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base mirrors filepath.Base.
|
// Base mirrors filepath.Base.
|
||||||
//
|
|
||||||
func Base(p string) string {
|
func Base(p string) string {
|
||||||
return path.Base(p)
|
return path.Base(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean mirrors filepath.Clean.
|
// Clean mirrors filepath.Clean.
|
||||||
//
|
|
||||||
func Clean(p string) string {
|
func Clean(p string) string {
|
||||||
return path.Clean(p)
|
return path.Clean(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dir mirrors filepath.Dir.
|
// Dir mirrors filepath.Dir.
|
||||||
//
|
|
||||||
func Dir(p string) string {
|
func Dir(p string) string {
|
||||||
return path.Dir(p)
|
return path.Dir(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ext mirrors filepath.Ext.
|
// Ext mirrors filepath.Ext.
|
||||||
//
|
|
||||||
func Ext(p string) string {
|
func Ext(p string) string {
|
||||||
return path.Ext(p)
|
return path.Ext(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join mirrors filepath.Join.
|
// Join mirrors filepath.Join.
|
||||||
//
|
|
||||||
func Join(elem ...string) string {
|
func Join(elem ...string) string {
|
||||||
return path.Join(elem...)
|
return path.Join(elem...)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,31 +10,26 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sprint mirrors fmt.Sprint using Core primitives.
|
// Sprint mirrors fmt.Sprint using Core primitives.
|
||||||
//
|
|
||||||
func Sprint(args ...any) string {
|
func Sprint(args ...any) string {
|
||||||
return core.Sprint(args...)
|
return core.Sprint(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sprintf mirrors fmt.Sprintf using Core primitives.
|
// Sprintf mirrors fmt.Sprintf using Core primitives.
|
||||||
//
|
|
||||||
func Sprintf(format string, args ...any) string {
|
func Sprintf(format string, args ...any) string {
|
||||||
return core.Sprintf(format, args...)
|
return core.Sprintf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fprintf mirrors fmt.Fprintf using Core primitives.
|
// Fprintf mirrors fmt.Fprintf using Core primitives.
|
||||||
//
|
|
||||||
func Fprintf(w io.Writer, format string, args ...any) (int, error) {
|
func Fprintf(w io.Writer, format string, args ...any) (int, error) {
|
||||||
return io.WriteString(w, Sprintf(format, args...))
|
return io.WriteString(w, Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Printf mirrors fmt.Printf.
|
// Printf mirrors fmt.Printf.
|
||||||
//
|
|
||||||
func Printf(format string, args ...any) (int, error) {
|
func Printf(format string, args ...any) (int, error) {
|
||||||
return Fprintf(stdio.Stdout, format, args...)
|
return Fprintf(stdio.Stdout, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Println mirrors fmt.Println.
|
// Println mirrors fmt.Println.
|
||||||
//
|
|
||||||
func Println(args ...any) (int, error) {
|
func Println(args ...any) (int, error) {
|
||||||
return io.WriteString(stdio.Stdout, Sprint(args...)+"\n")
|
return io.WriteString(stdio.Stdout, Sprint(args...)+"\n")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,31 +9,26 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Marshal mirrors encoding/json.Marshal.
|
// Marshal mirrors encoding/json.Marshal.
|
||||||
//
|
|
||||||
func Marshal(v any) ([]byte, error) {
|
func Marshal(v any) ([]byte, error) {
|
||||||
return json.Marshal(v)
|
return json.Marshal(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalIndent mirrors encoding/json.MarshalIndent.
|
// MarshalIndent mirrors encoding/json.MarshalIndent.
|
||||||
//
|
|
||||||
func MarshalIndent(v any, prefix, indent string) ([]byte, error) {
|
func MarshalIndent(v any, prefix, indent string) ([]byte, error) {
|
||||||
return json.MarshalIndent(v, prefix, indent)
|
return json.MarshalIndent(v, prefix, indent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDecoder mirrors encoding/json.NewDecoder.
|
// NewDecoder mirrors encoding/json.NewDecoder.
|
||||||
//
|
|
||||||
func NewDecoder(r io.Reader) *json.Decoder {
|
func NewDecoder(r io.Reader) *json.Decoder {
|
||||||
return json.NewDecoder(r)
|
return json.NewDecoder(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEncoder mirrors encoding/json.NewEncoder.
|
// NewEncoder mirrors encoding/json.NewEncoder.
|
||||||
//
|
|
||||||
func NewEncoder(w io.Writer) *json.Encoder {
|
func NewEncoder(w io.Writer) *json.Encoder {
|
||||||
return json.NewEncoder(w)
|
return json.NewEncoder(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal mirrors encoding/json.Unmarshal.
|
// Unmarshal mirrors encoding/json.Unmarshal.
|
||||||
//
|
|
||||||
func Unmarshal(data []byte, v any) error {
|
func Unmarshal(data []byte, v any) error {
|
||||||
return json.Unmarshal(data, v)
|
return json.Unmarshal(data, v)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,50 +23,41 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Stdin exposes process stdin without importing os.
|
// Stdin exposes process stdin without importing os.
|
||||||
//
|
|
||||||
var Stdin = stdio.Stdin
|
var Stdin = stdio.Stdin
|
||||||
|
|
||||||
// Stdout exposes process stdout without importing os.
|
// Stdout exposes process stdout without importing os.
|
||||||
//
|
|
||||||
var Stdout = stdio.Stdout
|
var Stdout = stdio.Stdout
|
||||||
|
|
||||||
// Stderr exposes process stderr without importing os.
|
// Stderr exposes process stderr without importing os.
|
||||||
//
|
|
||||||
var Stderr = stdio.Stderr
|
var Stderr = stdio.Stderr
|
||||||
|
|
||||||
// Getenv mirrors os.Getenv.
|
// Getenv mirrors os.Getenv.
|
||||||
//
|
|
||||||
func Getenv(key string) string {
|
func Getenv(key string) string {
|
||||||
value, _ := syscall.Getenv(key)
|
value, _ := syscall.Getenv(key)
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getwd mirrors os.Getwd.
|
// Getwd mirrors os.Getwd.
|
||||||
//
|
|
||||||
func Getwd() (string, error) {
|
func Getwd() (string, error) {
|
||||||
return syscall.Getwd()
|
return syscall.Getwd()
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNotExist mirrors os.IsNotExist.
|
// IsNotExist mirrors os.IsNotExist.
|
||||||
//
|
|
||||||
func IsNotExist(err error) bool {
|
func IsNotExist(err error) bool {
|
||||||
return core.Is(err, fs.ErrNotExist)
|
return core.Is(err, fs.ErrNotExist)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MkdirAll mirrors os.MkdirAll.
|
// MkdirAll mirrors os.MkdirAll.
|
||||||
//
|
|
||||||
func MkdirAll(path string, _ fs.FileMode) error {
|
func MkdirAll(path string, _ fs.FileMode) error {
|
||||||
return coreio.Local.EnsureDir(path)
|
return coreio.Local.EnsureDir(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open mirrors os.Open.
|
// Open mirrors os.Open.
|
||||||
//
|
|
||||||
func Open(path string) (fs.File, error) {
|
func Open(path string) (fs.File, error) {
|
||||||
return coreio.Local.Open(path)
|
return coreio.Local.Open(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenFile mirrors the append/create/write mode used in this repo.
|
// OpenFile mirrors the append/create/write mode used in this repo.
|
||||||
//
|
|
||||||
func OpenFile(path string, flag int, _ fs.FileMode) (io.WriteCloser, error) {
|
func OpenFile(path string, flag int, _ fs.FileMode) (io.WriteCloser, error) {
|
||||||
if flag&O_APPEND != 0 {
|
if flag&O_APPEND != 0 {
|
||||||
return coreio.Local.Append(path)
|
return coreio.Local.Append(path)
|
||||||
|
|
@ -75,26 +66,22 @@ func OpenFile(path string, flag int, _ fs.FileMode) (io.WriteCloser, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadDir mirrors os.ReadDir.
|
// ReadDir mirrors os.ReadDir.
|
||||||
//
|
|
||||||
func ReadDir(path string) ([]fs.DirEntry, error) {
|
func ReadDir(path string) ([]fs.DirEntry, error) {
|
||||||
return coreio.Local.List(path)
|
return coreio.Local.List(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadFile mirrors os.ReadFile.
|
// ReadFile mirrors os.ReadFile.
|
||||||
//
|
|
||||||
func ReadFile(path string) ([]byte, error) {
|
func ReadFile(path string) ([]byte, error) {
|
||||||
content, err := coreio.Local.Read(path)
|
content, err := coreio.Local.Read(path)
|
||||||
return []byte(content), err
|
return []byte(content), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stat mirrors os.Stat.
|
// Stat mirrors os.Stat.
|
||||||
//
|
|
||||||
func Stat(path string) (fs.FileInfo, error) {
|
func Stat(path string) (fs.FileInfo, error) {
|
||||||
return coreio.Local.Stat(path)
|
return coreio.Local.Stat(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserHomeDir mirrors os.UserHomeDir.
|
// UserHomeDir mirrors os.UserHomeDir.
|
||||||
//
|
|
||||||
func UserHomeDir() (string, error) {
|
func UserHomeDir() (string, error) {
|
||||||
if home := Getenv("HOME"); home != "" {
|
if home := Getenv("HOME"); home != "" {
|
||||||
return home, nil
|
return home, nil
|
||||||
|
|
@ -107,7 +94,6 @@ func UserHomeDir() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteFile mirrors os.WriteFile.
|
// WriteFile mirrors os.WriteFile.
|
||||||
//
|
|
||||||
func WriteFile(path string, data []byte, perm fs.FileMode) error {
|
func WriteFile(path string, data []byte, perm fs.FileMode) error {
|
||||||
return coreio.Local.WriteMode(path, string(data), perm)
|
return coreio.Local.WriteMode(path, string(data), perm)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,13 +28,10 @@ func (w fdWriter) Write(p []byte) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stdin exposes process stdin without importing os.
|
// Stdin exposes process stdin without importing os.
|
||||||
//
|
|
||||||
var Stdin io.Reader = fdReader{fd: 0}
|
var Stdin io.Reader = fdReader{fd: 0}
|
||||||
|
|
||||||
// Stdout exposes process stdout without importing os.
|
// Stdout exposes process stdout without importing os.
|
||||||
//
|
|
||||||
var Stdout io.Writer = fdWriter{fd: 1}
|
var Stdout io.Writer = fdWriter{fd: 1}
|
||||||
|
|
||||||
// Stderr exposes process stderr without importing os.
|
// Stderr exposes process stderr without importing os.
|
||||||
//
|
|
||||||
var Stderr io.Writer = fdWriter{fd: 2}
|
var Stderr io.Writer = fdWriter{fd: 2}
|
||||||
|
|
|
||||||
|
|
@ -11,29 +11,24 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Builder provides a strings.Builder-like type without importing strings.
|
// Builder provides a strings.Builder-like type without importing strings.
|
||||||
//
|
|
||||||
type Builder = bytes.Buffer
|
type Builder = bytes.Buffer
|
||||||
|
|
||||||
// Contains mirrors strings.Contains.
|
// Contains mirrors strings.Contains.
|
||||||
//
|
|
||||||
func Contains(s, substr string) bool {
|
func Contains(s, substr string) bool {
|
||||||
return core.Contains(s, substr)
|
return core.Contains(s, substr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainsAny mirrors strings.ContainsAny.
|
// ContainsAny mirrors strings.ContainsAny.
|
||||||
//
|
|
||||||
func ContainsAny(s, chars string) bool {
|
func ContainsAny(s, chars string) bool {
|
||||||
return bytes.IndexAny([]byte(s), chars) >= 0
|
return bytes.IndexAny([]byte(s), chars) >= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// EqualFold mirrors strings.EqualFold.
|
// EqualFold mirrors strings.EqualFold.
|
||||||
//
|
|
||||||
func EqualFold(s, t string) bool {
|
func EqualFold(s, t string) bool {
|
||||||
return bytes.EqualFold([]byte(s), []byte(t))
|
return bytes.EqualFold([]byte(s), []byte(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields mirrors strings.Fields.
|
// Fields mirrors strings.Fields.
|
||||||
//
|
|
||||||
func Fields(s string) []string {
|
func Fields(s string) []string {
|
||||||
scanner := bufio.NewScanner(NewReader(s))
|
scanner := bufio.NewScanner(NewReader(s))
|
||||||
scanner.Split(bufio.ScanWords)
|
scanner.Split(bufio.ScanWords)
|
||||||
|
|
@ -45,37 +40,31 @@ func Fields(s string) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasPrefix mirrors strings.HasPrefix.
|
// HasPrefix mirrors strings.HasPrefix.
|
||||||
//
|
|
||||||
func HasPrefix(s, prefix string) bool {
|
func HasPrefix(s, prefix string) bool {
|
||||||
return core.HasPrefix(s, prefix)
|
return core.HasPrefix(s, prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasSuffix mirrors strings.HasSuffix.
|
// HasSuffix mirrors strings.HasSuffix.
|
||||||
//
|
|
||||||
func HasSuffix(s, suffix string) bool {
|
func HasSuffix(s, suffix string) bool {
|
||||||
return core.HasSuffix(s, suffix)
|
return core.HasSuffix(s, suffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join mirrors strings.Join.
|
// Join mirrors strings.Join.
|
||||||
//
|
|
||||||
func Join(elems []string, sep string) string {
|
func Join(elems []string, sep string) string {
|
||||||
return core.Join(sep, elems...)
|
return core.Join(sep, elems...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastIndex mirrors strings.LastIndex.
|
// LastIndex mirrors strings.LastIndex.
|
||||||
//
|
|
||||||
func LastIndex(s, substr string) int {
|
func LastIndex(s, substr string) int {
|
||||||
return bytes.LastIndex([]byte(s), []byte(substr))
|
return bytes.LastIndex([]byte(s), []byte(substr))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewReader mirrors strings.NewReader.
|
// NewReader mirrors strings.NewReader.
|
||||||
//
|
|
||||||
func NewReader(s string) *bytes.Reader {
|
func NewReader(s string) *bytes.Reader {
|
||||||
return bytes.NewReader([]byte(s))
|
return bytes.NewReader([]byte(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repeat mirrors strings.Repeat.
|
// Repeat mirrors strings.Repeat.
|
||||||
//
|
|
||||||
func Repeat(s string, count int) string {
|
func Repeat(s string, count int) string {
|
||||||
if count <= 0 {
|
if count <= 0 {
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -84,31 +73,26 @@ func Repeat(s string, count int) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReplaceAll mirrors strings.ReplaceAll.
|
// ReplaceAll mirrors strings.ReplaceAll.
|
||||||
//
|
|
||||||
func ReplaceAll(s, old, new string) string {
|
func ReplaceAll(s, old, new string) string {
|
||||||
return core.Replace(s, old, new)
|
return core.Replace(s, old, new)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace mirrors strings.Replace for replace-all call sites.
|
// Replace mirrors strings.Replace for replace-all call sites.
|
||||||
//
|
|
||||||
func Replace(s, old, new string, _ int) string {
|
func Replace(s, old, new string, _ int) string {
|
||||||
return ReplaceAll(s, old, new)
|
return ReplaceAll(s, old, new)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split mirrors strings.Split.
|
// Split mirrors strings.Split.
|
||||||
//
|
|
||||||
func Split(s, sep string) []string {
|
func Split(s, sep string) []string {
|
||||||
return core.Split(s, sep)
|
return core.Split(s, sep)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SplitN mirrors strings.SplitN.
|
// SplitN mirrors strings.SplitN.
|
||||||
//
|
|
||||||
func SplitN(s, sep string, n int) []string {
|
func SplitN(s, sep string, n int) []string {
|
||||||
return core.SplitN(s, sep, n)
|
return core.SplitN(s, sep, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SplitSeq mirrors strings.SplitSeq.
|
// SplitSeq mirrors strings.SplitSeq.
|
||||||
//
|
|
||||||
func SplitSeq(s, sep string) iter.Seq[string] {
|
func SplitSeq(s, sep string) iter.Seq[string] {
|
||||||
parts := Split(s, sep)
|
parts := Split(s, sep)
|
||||||
return func(yield func(string) bool) {
|
return func(yield func(string) bool) {
|
||||||
|
|
@ -121,31 +105,26 @@ func SplitSeq(s, sep string) iter.Seq[string] {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToLower mirrors strings.ToLower.
|
// ToLower mirrors strings.ToLower.
|
||||||
//
|
|
||||||
func ToLower(s string) string {
|
func ToLower(s string) string {
|
||||||
return core.Lower(s)
|
return core.Lower(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToUpper mirrors strings.ToUpper.
|
// ToUpper mirrors strings.ToUpper.
|
||||||
//
|
|
||||||
func ToUpper(s string) string {
|
func ToUpper(s string) string {
|
||||||
return core.Upper(s)
|
return core.Upper(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrimPrefix mirrors strings.TrimPrefix.
|
// TrimPrefix mirrors strings.TrimPrefix.
|
||||||
//
|
|
||||||
func TrimPrefix(s, prefix string) string {
|
func TrimPrefix(s, prefix string) string {
|
||||||
return core.TrimPrefix(s, prefix)
|
return core.TrimPrefix(s, prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrimSpace mirrors strings.TrimSpace.
|
// TrimSpace mirrors strings.TrimSpace.
|
||||||
//
|
|
||||||
func TrimSpace(s string) string {
|
func TrimSpace(s string) string {
|
||||||
return core.Trim(s)
|
return core.Trim(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrimSuffix mirrors strings.TrimSuffix.
|
// TrimSuffix mirrors strings.TrimSuffix.
|
||||||
//
|
|
||||||
func TrimSuffix(s, suffix string) string {
|
func TrimSuffix(s, suffix string) string {
|
||||||
return core.TrimSuffix(s, suffix)
|
return core.TrimSuffix(s, suffix)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,20 +13,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config configures a ForgejoSource.
|
// Config configures a ForgejoSource.
|
||||||
//
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Repos []string // "owner/repo" format
|
Repos []string // "owner/repo" format
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForgejoSource polls a Forgejo instance for pipeline signals from epic issues.
|
// ForgejoSource polls a Forgejo instance for pipeline signals from epic issues.
|
||||||
//
|
|
||||||
type ForgejoSource struct {
|
type ForgejoSource struct {
|
||||||
repos []string
|
repos []string
|
||||||
forge *forge.Client
|
forge *forge.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a ForgejoSource using the given forge client.
|
// New creates a ForgejoSource using the given forge client.
|
||||||
//
|
|
||||||
func New(cfg Config, client *forge.Client) *ForgejoSource {
|
func New(cfg Config, client *forge.Client) *ForgejoSource {
|
||||||
return &ForgejoSource{
|
return &ForgejoSource{
|
||||||
repos: cfg.Repos,
|
repos: cfg.Repos,
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,11 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// CompletionHandler manages issue state when an agent finishes work.
|
// CompletionHandler manages issue state when an agent finishes work.
|
||||||
//
|
|
||||||
type CompletionHandler struct {
|
type CompletionHandler struct {
|
||||||
forge *forge.Client
|
forge *forge.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCompletionHandler creates a handler for agent completion events.
|
// NewCompletionHandler creates a handler for agent completion events.
|
||||||
//
|
|
||||||
func NewCompletionHandler(client *forge.Client) *CompletionHandler {
|
func NewCompletionHandler(client *forge.Client) *CompletionHandler {
|
||||||
return &CompletionHandler{
|
return &CompletionHandler{
|
||||||
forge: client,
|
forge: client,
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ const (
|
||||||
|
|
||||||
// DispatchTicket is the JSON payload written to the agent's queue.
|
// DispatchTicket is the JSON payload written to the agent's queue.
|
||||||
// The ForgeToken is transferred separately via a .env file with 0600 permissions.
|
// The ForgeToken is transferred separately via a .env file with 0600 permissions.
|
||||||
//
|
|
||||||
type DispatchTicket struct {
|
type DispatchTicket struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
RepoOwner string `json:"repo_owner"`
|
RepoOwner string `json:"repo_owner"`
|
||||||
|
|
@ -55,7 +54,6 @@ type DispatchTicket struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DispatchHandler dispatches coding work to remote agent machines via SSH.
|
// DispatchHandler dispatches coding work to remote agent machines via SSH.
|
||||||
//
|
|
||||||
type DispatchHandler struct {
|
type DispatchHandler struct {
|
||||||
forge *forge.Client
|
forge *forge.Client
|
||||||
forgeURL string
|
forgeURL string
|
||||||
|
|
@ -64,7 +62,6 @@ type DispatchHandler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDispatchHandler creates a handler that dispatches tickets to agent machines.
|
// NewDispatchHandler creates a handler that dispatches tickets to agent machines.
|
||||||
//
|
|
||||||
func NewDispatchHandler(client *forge.Client, forgeURL, token string, spinner *agentci.Spinner) *DispatchHandler {
|
func NewDispatchHandler(client *forge.Client, forgeURL, token string, spinner *agentci.Spinner) *DispatchHandler {
|
||||||
return &DispatchHandler{
|
return &DispatchHandler{
|
||||||
forge: client,
|
forge: client,
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// EnableAutoMergeHandler merges a PR that is ready using squash strategy.
|
// EnableAutoMergeHandler merges a PR that is ready using squash strategy.
|
||||||
//
|
|
||||||
type EnableAutoMergeHandler struct {
|
type EnableAutoMergeHandler struct {
|
||||||
forge *forge.Client
|
forge *forge.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEnableAutoMergeHandler creates a handler that merges ready PRs.
|
// NewEnableAutoMergeHandler creates a handler that merges ready PRs.
|
||||||
//
|
|
||||||
func NewEnableAutoMergeHandler(f *forge.Client) *EnableAutoMergeHandler {
|
func NewEnableAutoMergeHandler(f *forge.Client) *EnableAutoMergeHandler {
|
||||||
return &EnableAutoMergeHandler{forge: f}
|
return &EnableAutoMergeHandler{forge: f}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// PublishDraftHandler marks a draft PR as ready for review once its checks pass.
|
// PublishDraftHandler marks a draft PR as ready for review once its checks pass.
|
||||||
//
|
|
||||||
type PublishDraftHandler struct {
|
type PublishDraftHandler struct {
|
||||||
forge *forge.Client
|
forge *forge.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPublishDraftHandler creates a handler that publishes draft PRs.
|
// NewPublishDraftHandler creates a handler that publishes draft PRs.
|
||||||
//
|
|
||||||
func NewPublishDraftHandler(f *forge.Client) *PublishDraftHandler {
|
func NewPublishDraftHandler(f *forge.Client) *PublishDraftHandler {
|
||||||
return &PublishDraftHandler{forge: f}
|
return &PublishDraftHandler{forge: f}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,11 @@ import (
|
||||||
// DismissReviewsHandler dismisses stale "request changes" reviews on a PR.
|
// DismissReviewsHandler dismisses stale "request changes" reviews on a PR.
|
||||||
// This replaces the GitHub-only ResolveThreadsHandler because Forgejo does
|
// This replaces the GitHub-only ResolveThreadsHandler because Forgejo does
|
||||||
// not have a thread resolution API.
|
// not have a thread resolution API.
|
||||||
//
|
|
||||||
type DismissReviewsHandler struct {
|
type DismissReviewsHandler struct {
|
||||||
forge *forge.Client
|
forge *forge.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDismissReviewsHandler creates a handler that dismisses stale reviews.
|
// NewDismissReviewsHandler creates a handler that dismisses stale reviews.
|
||||||
//
|
|
||||||
func NewDismissReviewsHandler(f *forge.Client) *DismissReviewsHandler {
|
func NewDismissReviewsHandler(f *forge.Client) *DismissReviewsHandler {
|
||||||
return &DismissReviewsHandler{forge: f}
|
return &DismissReviewsHandler{forge: f}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,11 @@ import (
|
||||||
|
|
||||||
// SendFixCommandHandler posts a comment on a PR asking for conflict or
|
// SendFixCommandHandler posts a comment on a PR asking for conflict or
|
||||||
// review fixes.
|
// review fixes.
|
||||||
//
|
|
||||||
type SendFixCommandHandler struct {
|
type SendFixCommandHandler struct {
|
||||||
forge *forge.Client
|
forge *forge.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSendFixCommandHandler creates a handler that posts fix commands.
|
// NewSendFixCommandHandler creates a handler that posts fix commands.
|
||||||
//
|
|
||||||
func NewSendFixCommandHandler(f *forge.Client) *SendFixCommandHandler {
|
func NewSendFixCommandHandler(f *forge.Client) *SendFixCommandHandler {
|
||||||
return &SendFixCommandHandler{forge: f}
|
return &SendFixCommandHandler{forge: f}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,11 @@ import (
|
||||||
|
|
||||||
// TickParentHandler ticks a child checkbox in the parent epic issue body
|
// TickParentHandler ticks a child checkbox in the parent epic issue body
|
||||||
// after the child's PR has been merged.
|
// after the child's PR has been merged.
|
||||||
//
|
|
||||||
type TickParentHandler struct {
|
type TickParentHandler struct {
|
||||||
forge *forge.Client
|
forge *forge.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTickParentHandler creates a handler that ticks parent epic checkboxes.
|
// NewTickParentHandler creates a handler that ticks parent epic checkboxes.
|
||||||
//
|
|
||||||
func NewTickParentHandler(f *forge.Client) *TickParentHandler {
|
func NewTickParentHandler(f *forge.Client) *TickParentHandler {
|
||||||
return &TickParentHandler{forge: f}
|
return &TickParentHandler{forge: f}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import (
|
||||||
var validPathComponent = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9._-]*$`)
|
var validPathComponent = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9._-]*$`)
|
||||||
|
|
||||||
// JournalEntry is a single line in the JSONL audit log.
|
// JournalEntry is a single line in the JSONL audit log.
|
||||||
//
|
|
||||||
type JournalEntry struct {
|
type JournalEntry struct {
|
||||||
Timestamp string `json:"ts"`
|
Timestamp string `json:"ts"`
|
||||||
Epic int `json:"epic"`
|
Epic int `json:"epic"`
|
||||||
|
|
@ -32,7 +31,6 @@ type JournalEntry struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignalSnapshot captures the structural state of a PR at the time of action.
|
// SignalSnapshot captures the structural state of a PR at the time of action.
|
||||||
//
|
|
||||||
type SignalSnapshot struct {
|
type SignalSnapshot struct {
|
||||||
PRState string `json:"pr_state"`
|
PRState string `json:"pr_state"`
|
||||||
IsDraft bool `json:"is_draft"`
|
IsDraft bool `json:"is_draft"`
|
||||||
|
|
@ -43,7 +41,6 @@ type SignalSnapshot struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResultSnapshot captures the outcome of an action.
|
// ResultSnapshot captures the outcome of an action.
|
||||||
//
|
|
||||||
type ResultSnapshot struct {
|
type ResultSnapshot struct {
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
|
|
@ -51,14 +48,12 @@ type ResultSnapshot struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Journal writes ActionResult entries to date-partitioned JSONL files.
|
// Journal writes ActionResult entries to date-partitioned JSONL files.
|
||||||
//
|
|
||||||
type Journal struct {
|
type Journal struct {
|
||||||
baseDir string
|
baseDir string
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewJournal creates a new Journal rooted at baseDir.
|
// NewJournal creates a new Journal rooted at baseDir.
|
||||||
//
|
|
||||||
func NewJournal(baseDir string) (*Journal, error) {
|
func NewJournal(baseDir string) (*Journal, error) {
|
||||||
if baseDir == "" {
|
if baseDir == "" {
|
||||||
return nil, coreerr.E("jobrunner.NewJournal", "base directory is required", nil)
|
return nil, coreerr.E("jobrunner.NewJournal", "base directory is required", nil)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// PollerConfig configures a Poller.
|
// PollerConfig configures a Poller.
|
||||||
//
|
|
||||||
type PollerConfig struct {
|
type PollerConfig struct {
|
||||||
Sources []JobSource
|
Sources []JobSource
|
||||||
Handlers []JobHandler
|
Handlers []JobHandler
|
||||||
|
|
@ -21,7 +20,6 @@ type PollerConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Poller discovers signals from sources and dispatches them to handlers.
|
// Poller discovers signals from sources and dispatches them to handlers.
|
||||||
//
|
|
||||||
type Poller struct {
|
type Poller struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
sources []JobSource
|
sources []JobSource
|
||||||
|
|
@ -33,7 +31,6 @@ type Poller struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPoller creates a Poller from the given config.
|
// NewPoller creates a Poller from the given config.
|
||||||
//
|
|
||||||
func NewPoller(cfg PollerConfig) *Poller {
|
func NewPoller(cfg PollerConfig) *Poller {
|
||||||
interval := cfg.PollInterval
|
interval := cfg.PollInterval
|
||||||
if interval <= 0 {
|
if interval <= 0 {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import (
|
||||||
|
|
||||||
// PipelineSignal is the structural snapshot of a child issue/PR.
|
// PipelineSignal is the structural snapshot of a child issue/PR.
|
||||||
// Carries structural state plus issue title/body for dispatch prompts.
|
// Carries structural state plus issue title/body for dispatch prompts.
|
||||||
//
|
|
||||||
type PipelineSignal struct {
|
type PipelineSignal struct {
|
||||||
EpicNumber int
|
EpicNumber int
|
||||||
ChildNumber int
|
ChildNumber int
|
||||||
|
|
@ -46,7 +45,6 @@ func (s *PipelineSignal) HasUnresolvedThreads() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionResult carries the outcome of a handler execution.
|
// ActionResult carries the outcome of a handler execution.
|
||||||
//
|
|
||||||
type ActionResult struct {
|
type ActionResult struct {
|
||||||
Action string `json:"action"`
|
Action string `json:"action"`
|
||||||
RepoOwner string `json:"repo_owner"`
|
RepoOwner string `json:"repo_owner"`
|
||||||
|
|
@ -62,7 +60,6 @@ type ActionResult struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// JobSource discovers actionable work from an external system.
|
// JobSource discovers actionable work from an external system.
|
||||||
//
|
|
||||||
type JobSource interface {
|
type JobSource interface {
|
||||||
Name() string
|
Name() string
|
||||||
Poll(ctx context.Context) ([]*PipelineSignal, error)
|
Poll(ctx context.Context) ([]*PipelineSignal, error)
|
||||||
|
|
@ -70,7 +67,6 @@ type JobSource interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// JobHandler processes a single pipeline signal.
|
// JobHandler processes a single pipeline signal.
|
||||||
//
|
|
||||||
type JobHandler interface {
|
type JobHandler interface {
|
||||||
Name() string
|
Name() string
|
||||||
Match(signal *PipelineSignal) bool
|
Match(signal *PipelineSignal) bool
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ package locales
|
||||||
|
|
||||||
import "embed"
|
import "embed"
|
||||||
|
|
||||||
//
|
|
||||||
//
|
//
|
||||||
//go:embed *.json
|
//go:embed *.json
|
||||||
var FS embed.FS
|
var FS embed.FS
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ import (
|
||||||
// CompiledManifest is the distribution-ready form of a manifest, written as
|
// CompiledManifest is the distribution-ready form of a manifest, written as
|
||||||
// core.json at the repository root (not inside .core/). It embeds the
|
// core.json at the repository root (not inside .core/). It embeds the
|
||||||
// original Manifest and adds build metadata stapled at compile time.
|
// original Manifest and adds build metadata stapled at compile time.
|
||||||
//
|
|
||||||
type CompiledManifest struct {
|
type CompiledManifest struct {
|
||||||
Manifest `json:",inline" yaml:",inline"`
|
Manifest `json:",inline" yaml:",inline"`
|
||||||
|
|
||||||
|
|
@ -27,7 +26,6 @@ type CompiledManifest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompileOptions controls how Compile populates the build metadata.
|
// CompileOptions controls how Compile populates the build metadata.
|
||||||
//
|
|
||||||
type CompileOptions struct {
|
type CompileOptions struct {
|
||||||
Commit string // Git commit hash
|
Commit string // Git commit hash
|
||||||
Tag string // Git tag (e.g. v1.0.0)
|
Tag string // Git tag (e.g. v1.0.0)
|
||||||
|
|
@ -37,7 +35,6 @@ type CompileOptions struct {
|
||||||
|
|
||||||
// Compile produces a CompiledManifest from a source manifest and build
|
// Compile produces a CompiledManifest from a source manifest and build
|
||||||
// options. If opts.SignKey is provided the manifest is signed first.
|
// options. If opts.SignKey is provided the manifest is signed first.
|
||||||
//
|
|
||||||
func Compile(m *Manifest, opts CompileOptions) (*CompiledManifest, error) {
|
func Compile(m *Manifest, opts CompileOptions) (*CompiledManifest, error) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return nil, coreerr.E("manifest.Compile", "nil manifest", nil)
|
return nil, coreerr.E("manifest.Compile", "nil manifest", nil)
|
||||||
|
|
@ -66,13 +63,11 @@ func Compile(m *Manifest, opts CompileOptions) (*CompiledManifest, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON serialises a CompiledManifest to JSON bytes.
|
// MarshalJSON serialises a CompiledManifest to JSON bytes.
|
||||||
//
|
|
||||||
func MarshalJSON(cm *CompiledManifest) ([]byte, error) {
|
func MarshalJSON(cm *CompiledManifest) ([]byte, error) {
|
||||||
return json.MarshalIndent(cm, "", " ")
|
return json.MarshalIndent(cm, "", " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseCompiled decodes a core.json into a CompiledManifest.
|
// ParseCompiled decodes a core.json into a CompiledManifest.
|
||||||
//
|
|
||||||
func ParseCompiled(data []byte) (*CompiledManifest, error) {
|
func ParseCompiled(data []byte) (*CompiledManifest, error) {
|
||||||
var cm CompiledManifest
|
var cm CompiledManifest
|
||||||
if err := json.Unmarshal(data, &cm); err != nil {
|
if err := json.Unmarshal(data, &cm); err != nil {
|
||||||
|
|
@ -85,7 +80,6 @@ const compiledPath = "core.json"
|
||||||
|
|
||||||
// WriteCompiled writes a CompiledManifest as core.json to the given root
|
// WriteCompiled writes a CompiledManifest as core.json to the given root
|
||||||
// directory. The file lives at the distribution root, not inside .core/.
|
// directory. The file lives at the distribution root, not inside .core/.
|
||||||
//
|
|
||||||
func WriteCompiled(medium io.Medium, root string, cm *CompiledManifest) error {
|
func WriteCompiled(medium io.Medium, root string, cm *CompiledManifest) error {
|
||||||
data, err := MarshalJSON(cm)
|
data, err := MarshalJSON(cm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -96,7 +90,6 @@ func WriteCompiled(medium io.Medium, root string, cm *CompiledManifest) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadCompiled reads and parses a core.json from the given root directory.
|
// LoadCompiled reads and parses a core.json from the given root directory.
|
||||||
//
|
|
||||||
func LoadCompiled(medium io.Medium, root string) (*CompiledManifest, error) {
|
func LoadCompiled(medium io.Medium, root string) (*CompiledManifest, error) {
|
||||||
path := filepath.Join(root, compiledPath)
|
path := filepath.Join(root, compiledPath)
|
||||||
data, err := medium.Read(path)
|
data, err := medium.Read(path)
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,11 @@ import (
|
||||||
const manifestPath = ".core/manifest.yaml"
|
const manifestPath = ".core/manifest.yaml"
|
||||||
|
|
||||||
// MarshalYAML serializes a manifest to YAML bytes.
|
// MarshalYAML serializes a manifest to YAML bytes.
|
||||||
//
|
|
||||||
func MarshalYAML(m *Manifest) ([]byte, error) {
|
func MarshalYAML(m *Manifest) ([]byte, error) {
|
||||||
return yaml.Marshal(m)
|
return yaml.Marshal(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load reads and parses a .core/manifest.yaml from the given root directory.
|
// Load reads and parses a .core/manifest.yaml from the given root directory.
|
||||||
//
|
|
||||||
func Load(medium io.Medium, root string) (*Manifest, error) {
|
func Load(medium io.Medium, root string) (*Manifest, error) {
|
||||||
path := filepath.Join(root, manifestPath)
|
path := filepath.Join(root, manifestPath)
|
||||||
data, err := medium.Read(path)
|
data, err := medium.Read(path)
|
||||||
|
|
@ -31,7 +29,6 @@ func Load(medium io.Medium, root string) (*Manifest, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadVerified reads, parses, and verifies the ed25519 signature.
|
// LoadVerified reads, parses, and verifies the ed25519 signature.
|
||||||
//
|
|
||||||
func LoadVerified(medium io.Medium, root string, pub ed25519.PublicKey) (*Manifest, error) {
|
func LoadVerified(medium io.Medium, root string, pub ed25519.PublicKey) (*Manifest, error) {
|
||||||
m, err := Load(medium, root)
|
m, err := Load(medium, root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Manifest represents a .core/manifest.yaml application manifest.
|
// Manifest represents a .core/manifest.yaml application manifest.
|
||||||
//
|
|
||||||
type Manifest struct {
|
type Manifest struct {
|
||||||
Code string `yaml:"code" json:"code"`
|
Code string `yaml:"code" json:"code"`
|
||||||
Name string `yaml:"name" json:"name"`
|
Name string `yaml:"name" json:"name"`
|
||||||
|
|
@ -34,7 +33,6 @@ type Manifest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ElementSpec describes a web component for GUI rendering.
|
// ElementSpec describes a web component for GUI rendering.
|
||||||
//
|
|
||||||
type ElementSpec struct {
|
type ElementSpec struct {
|
||||||
// Tag is the custom element tag name, e.g. "core-cool-widget".
|
// Tag is the custom element tag name, e.g. "core-cool-widget".
|
||||||
Tag string `yaml:"tag" json:"tag"`
|
Tag string `yaml:"tag" json:"tag"`
|
||||||
|
|
@ -50,7 +48,6 @@ func (m *Manifest) IsProvider() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Permissions declares the I/O capabilities a module requires.
|
// Permissions declares the I/O capabilities a module requires.
|
||||||
//
|
|
||||||
type Permissions struct {
|
type Permissions struct {
|
||||||
Read []string `yaml:"read" json:"read"`
|
Read []string `yaml:"read" json:"read"`
|
||||||
Write []string `yaml:"write" json:"write"`
|
Write []string `yaml:"write" json:"write"`
|
||||||
|
|
@ -59,7 +56,6 @@ type Permissions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DaemonSpec describes a long-running process managed by the runtime.
|
// DaemonSpec describes a long-running process managed by the runtime.
|
||||||
//
|
|
||||||
type DaemonSpec struct {
|
type DaemonSpec struct {
|
||||||
Binary string `yaml:"binary,omitempty" json:"binary,omitempty"`
|
Binary string `yaml:"binary,omitempty" json:"binary,omitempty"`
|
||||||
Args []string `yaml:"args,omitempty" json:"args,omitempty"`
|
Args []string `yaml:"args,omitempty" json:"args,omitempty"`
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ func signable(m *Manifest) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign computes the ed25519 signature and stores it in m.Sign (base64).
|
// Sign computes the ed25519 signature and stores it in m.Sign (base64).
|
||||||
//
|
|
||||||
func Sign(m *Manifest, priv ed25519.PrivateKey) error {
|
func Sign(m *Manifest, priv ed25519.PrivateKey) error {
|
||||||
msg, err := signable(m)
|
msg, err := signable(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -30,7 +29,6 @@ func Sign(m *Manifest, priv ed25519.PrivateKey) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify checks the ed25519 signature in m.Sign against the public key.
|
// Verify checks the ed25519 signature in m.Sign against the public key.
|
||||||
//
|
|
||||||
func Verify(m *Manifest, pub ed25519.PublicKey) (bool, error) {
|
func Verify(m *Manifest, pub ed25519.PublicKey) (bool, error) {
|
||||||
if m.Sign == "" {
|
if m.Sign == "" {
|
||||||
return false, coreerr.E("manifest.Verify", "no signature present", nil)
|
return false, coreerr.E("manifest.Verify", "no signature present", nil)
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// IndexVersion is the current marketplace index format version.
|
// IndexVersion is the current marketplace index format version.
|
||||||
//
|
|
||||||
const IndexVersion = 1
|
const IndexVersion = 1
|
||||||
|
|
||||||
// Builder constructs a marketplace Index by crawling directories for
|
// Builder constructs a marketplace Index by crawling directories for
|
||||||
// core.json (compiled manifests) or .core/manifest.yaml files.
|
// core.json (compiled manifests) or .core/manifest.yaml files.
|
||||||
//
|
|
||||||
type Builder struct {
|
type Builder struct {
|
||||||
// BaseURL is the prefix for constructing repository URLs, e.g.
|
// BaseURL is the prefix for constructing repository URLs, e.g.
|
||||||
// "https://forge.lthn.ai". When set, module Repo is derived as
|
// "https://forge.lthn.ai". When set, module Repo is derived as
|
||||||
|
|
@ -88,7 +86,6 @@ func (b *Builder) BuildFromDirs(dirs ...string) (*Index, error) {
|
||||||
// BuildFromManifests constructs an Index from pre-loaded manifests.
|
// BuildFromManifests constructs an Index from pre-loaded manifests.
|
||||||
// This is useful when manifests have already been collected (e.g. from
|
// This is useful when manifests have already been collected (e.g. from
|
||||||
// a Forge API crawl).
|
// a Forge API crawl).
|
||||||
//
|
|
||||||
func BuildFromManifests(manifests []*manifest.Manifest) *Index {
|
func BuildFromManifests(manifests []*manifest.Manifest) *Index {
|
||||||
var modules []Module
|
var modules []Module
|
||||||
seen := make(map[string]bool)
|
seen := make(map[string]bool)
|
||||||
|
|
@ -119,7 +116,6 @@ func BuildFromManifests(manifests []*manifest.Manifest) *Index {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteIndex serialises an Index to JSON and writes it to the given path.
|
// WriteIndex serialises an Index to JSON and writes it to the given path.
|
||||||
//
|
|
||||||
func WriteIndex(path string, idx *Index) error {
|
func WriteIndex(path string, idx *Index) error {
|
||||||
if err := coreio.Local.EnsureDir(filepath.Dir(path)); err != nil {
|
if err := coreio.Local.EnsureDir(filepath.Dir(path)); err != nil {
|
||||||
return coreerr.E("marketplace.WriteIndex", "mkdir failed", err)
|
return coreerr.E("marketplace.WriteIndex", "mkdir failed", err)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// DiscoveredProvider represents a runtime provider found on disk.
|
// DiscoveredProvider represents a runtime provider found on disk.
|
||||||
//
|
|
||||||
type DiscoveredProvider struct {
|
type DiscoveredProvider struct {
|
||||||
// Dir is the absolute path to the provider directory.
|
// Dir is the absolute path to the provider directory.
|
||||||
Dir string
|
Dir string
|
||||||
|
|
@ -27,7 +26,6 @@ type DiscoveredProvider struct {
|
||||||
// Each subdirectory is checked for a .core/manifest.yaml file. Directories
|
// Each subdirectory is checked for a .core/manifest.yaml file. Directories
|
||||||
// without a valid manifest are skipped with a log warning.
|
// without a valid manifest are skipped with a log warning.
|
||||||
// Only manifests with provider fields (namespace + binary) are returned.
|
// Only manifests with provider fields (namespace + binary) are returned.
|
||||||
//
|
|
||||||
func DiscoverProviders(dir string) ([]DiscoveredProvider, error) {
|
func DiscoverProviders(dir string) ([]DiscoveredProvider, error) {
|
||||||
entries, err := os.ReadDir(dir)
|
entries, err := os.ReadDir(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -73,7 +71,6 @@ func DiscoverProviders(dir string) ([]DiscoveredProvider, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProviderRegistryEntry records metadata about an installed provider.
|
// ProviderRegistryEntry records metadata about an installed provider.
|
||||||
//
|
|
||||||
type ProviderRegistryEntry struct {
|
type ProviderRegistryEntry struct {
|
||||||
Installed string `yaml:"installed" json:"installed"`
|
Installed string `yaml:"installed" json:"installed"`
|
||||||
Version string `yaml:"version" json:"version"`
|
Version string `yaml:"version" json:"version"`
|
||||||
|
|
@ -82,7 +79,6 @@ type ProviderRegistryEntry struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProviderRegistryFile represents the registry.yaml file tracking installed providers.
|
// ProviderRegistryFile represents the registry.yaml file tracking installed providers.
|
||||||
//
|
|
||||||
type ProviderRegistryFile struct {
|
type ProviderRegistryFile struct {
|
||||||
Version int `yaml:"version" json:"version"`
|
Version int `yaml:"version" json:"version"`
|
||||||
Providers map[string]ProviderRegistryEntry `yaml:"providers" json:"providers"`
|
Providers map[string]ProviderRegistryEntry `yaml:"providers" json:"providers"`
|
||||||
|
|
@ -90,7 +86,6 @@ type ProviderRegistryFile struct {
|
||||||
|
|
||||||
// LoadProviderRegistry reads a registry.yaml file from the given path.
|
// LoadProviderRegistry reads a registry.yaml file from the given path.
|
||||||
// Returns an empty registry if the file does not exist.
|
// Returns an empty registry if the file does not exist.
|
||||||
//
|
|
||||||
func LoadProviderRegistry(path string) (*ProviderRegistryFile, error) {
|
func LoadProviderRegistry(path string) (*ProviderRegistryFile, error) {
|
||||||
raw, err := coreio.Local.Read(path)
|
raw, err := coreio.Local.Read(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -116,7 +111,6 @@ func LoadProviderRegistry(path string) (*ProviderRegistryFile, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveProviderRegistry writes the registry to the given path.
|
// SaveProviderRegistry writes the registry to the given path.
|
||||||
//
|
|
||||||
func SaveProviderRegistry(path string, reg *ProviderRegistryFile) error {
|
func SaveProviderRegistry(path string, reg *ProviderRegistryFile) error {
|
||||||
if err := coreio.Local.EnsureDir(filepath.Dir(path)); err != nil {
|
if err := coreio.Local.EnsureDir(filepath.Dir(path)); err != nil {
|
||||||
return coreerr.E("marketplace.SaveProviderRegistry", "ensure directory", err)
|
return coreerr.E("marketplace.SaveProviderRegistry", "ensure directory", err)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import (
|
||||||
const storeGroup = "_modules"
|
const storeGroup = "_modules"
|
||||||
|
|
||||||
// Installer handles module installation from Git repos.
|
// Installer handles module installation from Git repos.
|
||||||
//
|
|
||||||
type Installer struct {
|
type Installer struct {
|
||||||
medium io.Medium
|
medium io.Medium
|
||||||
modulesDir string
|
modulesDir string
|
||||||
|
|
@ -29,7 +28,6 @@ type Installer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInstaller creates a new module installer.
|
// NewInstaller creates a new module installer.
|
||||||
//
|
|
||||||
func NewInstaller(m io.Medium, modulesDir string, st *store.Store) *Installer {
|
func NewInstaller(m io.Medium, modulesDir string, st *store.Store) *Installer {
|
||||||
return &Installer{
|
return &Installer{
|
||||||
medium: m,
|
medium: m,
|
||||||
|
|
@ -39,7 +37,6 @@ func NewInstaller(m io.Medium, modulesDir string, st *store.Store) *Installer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// InstalledModule holds stored metadata about an installed module.
|
// InstalledModule holds stored metadata about an installed module.
|
||||||
//
|
|
||||||
type InstalledModule struct {
|
type InstalledModule struct {
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Module is a marketplace entry pointing to a module's Git repo.
|
// Module is a marketplace entry pointing to a module's Git repo.
|
||||||
//
|
|
||||||
type Module struct {
|
type Module struct {
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
@ -20,7 +19,6 @@ type Module struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Index is the root marketplace catalog.
|
// Index is the root marketplace catalog.
|
||||||
//
|
|
||||||
type Index struct {
|
type Index struct {
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
Modules []Module `json:"modules"`
|
Modules []Module `json:"modules"`
|
||||||
|
|
@ -28,7 +26,6 @@ type Index struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseIndex decodes a marketplace index.json.
|
// ParseIndex decodes a marketplace index.json.
|
||||||
//
|
|
||||||
func ParseIndex(data []byte) (*Index, error) {
|
func ParseIndex(data []byte) (*Index, error) {
|
||||||
var idx Index
|
var idx Index
|
||||||
if err := json.Unmarshal(data, &idx); err != nil {
|
if err := json.Unmarshal(data, &idx); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,5 @@ import "embed"
|
||||||
// Assets holds the built UI bundle (core-scm.js and related files).
|
// Assets holds the built UI bundle (core-scm.js and related files).
|
||||||
// The directory is populated by running `npm run build` in the ui/ directory.
|
// The directory is populated by running `npm run build` in the ui/ directory.
|
||||||
//
|
//
|
||||||
//
|
|
||||||
//
|
|
||||||
//go:embed all:ui/dist
|
//go:embed all:ui/dist
|
||||||
var Assets embed.FS
|
var Assets embed.FS
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import (
|
||||||
// ScmProvider wraps go-scm marketplace, manifest, and registry operations
|
// ScmProvider wraps go-scm marketplace, manifest, and registry operations
|
||||||
// as a service provider. It implements Provider, Streamable, Describable,
|
// as a service provider. It implements Provider, Streamable, Describable,
|
||||||
// and Renderable.
|
// and Renderable.
|
||||||
//
|
|
||||||
type ScmProvider struct {
|
type ScmProvider struct {
|
||||||
index *marketplace.Index
|
index *marketplace.Index
|
||||||
installer *marketplace.Installer
|
installer *marketplace.Installer
|
||||||
|
|
@ -46,7 +45,6 @@ var (
|
||||||
// NewProvider creates an SCM provider backed by the given marketplace index,
|
// NewProvider creates an SCM provider backed by the given marketplace index,
|
||||||
// installer, and registry. The WS hub is used to emit real-time events.
|
// installer, and registry. The WS hub is used to emit real-time events.
|
||||||
// Pass nil for any dependency that is not available.
|
// Pass nil for any dependency that is not available.
|
||||||
//
|
|
||||||
func NewProvider(idx *marketplace.Index, inst *marketplace.Installer, reg *repos.Registry, hub *ws.Hub) *ScmProvider {
|
func NewProvider(idx *marketplace.Index, inst *marketplace.Installer, reg *repos.Registry, hub *ws.Hub) *ScmProvider {
|
||||||
return &ScmProvider{
|
return &ScmProvider{
|
||||||
index: idx,
|
index: idx,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
// PluginConfig holds configuration for a single installed plugin.
|
// PluginConfig holds configuration for a single installed plugin.
|
||||||
//
|
|
||||||
type PluginConfig struct {
|
type PluginConfig struct {
|
||||||
Name string `json:"name" yaml:"name"`
|
Name string `json:"name" yaml:"name"`
|
||||||
Version string `json:"version" yaml:"version"`
|
Version string `json:"version" yaml:"version"`
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Installer handles plugin installation from GitHub.
|
// Installer handles plugin installation from GitHub.
|
||||||
//
|
|
||||||
type Installer struct {
|
type Installer struct {
|
||||||
medium io.Medium
|
medium io.Medium
|
||||||
registry *Registry
|
registry *Registry
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInstaller creates a new plugin installer.
|
// NewInstaller creates a new plugin installer.
|
||||||
//
|
|
||||||
func NewInstaller(m io.Medium, registry *Registry) *Installer {
|
func NewInstaller(m io.Medium, registry *Registry) *Installer {
|
||||||
return &Installer{
|
return &Installer{
|
||||||
medium: m,
|
medium: m,
|
||||||
|
|
@ -182,8 +180,6 @@ func (i *Installer) cloneRepo(ctx context.Context, org, repo, version, dest stri
|
||||||
// Accepted formats:
|
// Accepted formats:
|
||||||
// - "org/repo" -> org="org", repo="repo", version=""
|
// - "org/repo" -> org="org", repo="repo", version=""
|
||||||
// - "org/repo@v1.0" -> org="org", repo="repo", version="v1.0"
|
// - "org/repo@v1.0" -> org="org", repo="repo", version="v1.0"
|
||||||
//
|
|
||||||
//
|
|
||||||
func ParseSource(source string) (org, repo, version string, err error) {
|
func ParseSource(source string) (org, repo, version string, err error) {
|
||||||
source, err = url.PathUnescape(source)
|
source, err = url.PathUnescape(source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Loader loads plugins from the filesystem.
|
// Loader loads plugins from the filesystem.
|
||||||
//
|
|
||||||
type Loader struct {
|
type Loader struct {
|
||||||
medium io.Medium
|
medium io.Medium
|
||||||
baseDir string
|
baseDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLoader creates a new plugin loader.
|
// NewLoader creates a new plugin loader.
|
||||||
//
|
|
||||||
func NewLoader(m io.Medium, baseDir string) *Loader {
|
func NewLoader(m io.Medium, baseDir string) *Loader {
|
||||||
return &Loader{
|
return &Loader{
|
||||||
medium: m,
|
medium: m,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import (
|
||||||
|
|
||||||
// Manifest represents a plugin.json manifest file.
|
// Manifest represents a plugin.json manifest file.
|
||||||
// Each plugin repository must contain a plugin.json at its root.
|
// Each plugin repository must contain a plugin.json at its root.
|
||||||
//
|
|
||||||
type Manifest struct {
|
type Manifest struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
|
|
@ -23,7 +22,6 @@ type Manifest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadManifest reads and parses a plugin.json file from the given path.
|
// LoadManifest reads and parses a plugin.json file from the given path.
|
||||||
//
|
|
||||||
func LoadManifest(m io.Medium, path string) (*Manifest, error) {
|
func LoadManifest(m io.Medium, path string) (*Manifest, error) {
|
||||||
content, err := m.Read(path)
|
content, err := m.Read(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ package plugin
|
||||||
import "context"
|
import "context"
|
||||||
|
|
||||||
// Plugin is the interface that all plugins must implement.
|
// Plugin is the interface that all plugins must implement.
|
||||||
//
|
|
||||||
type Plugin interface {
|
type Plugin interface {
|
||||||
// Name returns the plugin's unique identifier.
|
// Name returns the plugin's unique identifier.
|
||||||
Name() string
|
Name() string
|
||||||
|
|
@ -36,7 +35,6 @@ type Plugin interface {
|
||||||
|
|
||||||
// BasePlugin provides a default implementation of Plugin.
|
// BasePlugin provides a default implementation of Plugin.
|
||||||
// Embed this in concrete plugin types to inherit default behaviour.
|
// Embed this in concrete plugin types to inherit default behaviour.
|
||||||
//
|
|
||||||
type BasePlugin struct {
|
type BasePlugin struct {
|
||||||
PluginName string
|
PluginName string
|
||||||
PluginVersion string
|
PluginVersion string
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ const registryFilename = "registry.json"
|
||||||
|
|
||||||
// Registry manages installed plugins.
|
// Registry manages installed plugins.
|
||||||
// Plugin metadata is stored in a registry.json file under the base path.
|
// Plugin metadata is stored in a registry.json file under the base path.
|
||||||
//
|
|
||||||
type Registry struct {
|
type Registry struct {
|
||||||
medium io.Medium
|
medium io.Medium
|
||||||
basePath string // e.g., ~/.core/plugins/
|
basePath string // e.g., ~/.core/plugins/
|
||||||
|
|
@ -24,7 +23,6 @@ type Registry struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRegistry creates a new plugin registry.
|
// NewRegistry creates a new plugin registry.
|
||||||
//
|
|
||||||
func NewRegistry(m io.Medium, basePath string) *Registry {
|
func NewRegistry(m io.Medium, basePath string) *Registry {
|
||||||
return &Registry{
|
return &Registry{
|
||||||
medium: m,
|
medium: m,
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import (
|
||||||
|
|
||||||
// GitState holds per-machine git sync state for a workspace.
|
// GitState holds per-machine git sync state for a workspace.
|
||||||
// Stored at .core/git.yaml and .gitignored (not shared across machines).
|
// Stored at .core/git.yaml and .gitignored (not shared across machines).
|
||||||
//
|
|
||||||
type GitState struct {
|
type GitState struct {
|
||||||
Version int `yaml:"version"`
|
Version int `yaml:"version"`
|
||||||
Repos map[string]*RepoGitState `yaml:"repos,omitempty"`
|
Repos map[string]*RepoGitState `yaml:"repos,omitempty"`
|
||||||
|
|
@ -21,7 +20,6 @@ type GitState struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RepoGitState tracks the last known git state for a single repo.
|
// RepoGitState tracks the last known git state for a single repo.
|
||||||
//
|
|
||||||
type RepoGitState struct {
|
type RepoGitState struct {
|
||||||
LastPull time.Time `yaml:"last_pull,omitempty"`
|
LastPull time.Time `yaml:"last_pull,omitempty"`
|
||||||
LastPush time.Time `yaml:"last_push,omitempty"`
|
LastPush time.Time `yaml:"last_push,omitempty"`
|
||||||
|
|
@ -32,7 +30,6 @@ type RepoGitState struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AgentState tracks which agent last touched which repos.
|
// AgentState tracks which agent last touched which repos.
|
||||||
//
|
|
||||||
type AgentState struct {
|
type AgentState struct {
|
||||||
LastSeen time.Time `yaml:"last_seen"`
|
LastSeen time.Time `yaml:"last_seen"`
|
||||||
Active []string `yaml:"active,omitempty"`
|
Active []string `yaml:"active,omitempty"`
|
||||||
|
|
@ -40,7 +37,6 @@ type AgentState struct {
|
||||||
|
|
||||||
// LoadGitState reads .core/git.yaml from the given workspace root directory.
|
// LoadGitState reads .core/git.yaml from the given workspace root directory.
|
||||||
// Returns a new empty GitState if the file does not exist.
|
// Returns a new empty GitState if the file does not exist.
|
||||||
//
|
|
||||||
func LoadGitState(m io.Medium, root string) (*GitState, error) {
|
func LoadGitState(m io.Medium, root string) (*GitState, error) {
|
||||||
path := filepath.Join(root, ".core", "git.yaml")
|
path := filepath.Join(root, ".core", "git.yaml")
|
||||||
|
|
||||||
|
|
@ -69,7 +65,6 @@ func LoadGitState(m io.Medium, root string) (*GitState, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveGitState writes .core/git.yaml to the given workspace root directory.
|
// SaveGitState writes .core/git.yaml to the given workspace root directory.
|
||||||
//
|
|
||||||
func SaveGitState(m io.Medium, root string, gs *GitState) error {
|
func SaveGitState(m io.Medium, root string, gs *GitState) error {
|
||||||
coreDir := filepath.Join(root, ".core")
|
coreDir := filepath.Join(root, ".core")
|
||||||
if err := m.EnsureDir(coreDir); err != nil {
|
if err := m.EnsureDir(coreDir); err != nil {
|
||||||
|
|
@ -90,7 +85,6 @@ func SaveGitState(m io.Medium, root string, gs *GitState) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGitState returns a new empty GitState with version 1.
|
// NewGitState returns a new empty GitState with version 1.
|
||||||
//
|
|
||||||
func NewGitState() *GitState {
|
func NewGitState() *GitState {
|
||||||
return &GitState{
|
return &GitState{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import (
|
||||||
|
|
||||||
// KBConfig holds knowledge base configuration for a workspace.
|
// KBConfig holds knowledge base configuration for a workspace.
|
||||||
// Stored at .core/kb.yaml and checked into git.
|
// Stored at .core/kb.yaml and checked into git.
|
||||||
//
|
|
||||||
type KBConfig struct {
|
type KBConfig struct {
|
||||||
Version int `yaml:"version"`
|
Version int `yaml:"version"`
|
||||||
Wiki WikiConfig `yaml:"wiki"`
|
Wiki WikiConfig `yaml:"wiki"`
|
||||||
|
|
@ -21,7 +20,6 @@ type KBConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WikiConfig controls local wiki mirror behaviour.
|
// WikiConfig controls local wiki mirror behaviour.
|
||||||
//
|
|
||||||
type WikiConfig struct {
|
type WikiConfig struct {
|
||||||
// Enabled toggles wiki cloning on sync.
|
// Enabled toggles wiki cloning on sync.
|
||||||
Enabled bool `yaml:"enabled"`
|
Enabled bool `yaml:"enabled"`
|
||||||
|
|
@ -33,7 +31,6 @@ type WikiConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// KBSearch configures vector search against the OpenBrain Qdrant collection.
|
// KBSearch configures vector search against the OpenBrain Qdrant collection.
|
||||||
//
|
|
||||||
type KBSearch struct {
|
type KBSearch struct {
|
||||||
// QdrantHost is the Qdrant server (gRPC).
|
// QdrantHost is the Qdrant server (gRPC).
|
||||||
QdrantHost string `yaml:"qdrant_host"`
|
QdrantHost string `yaml:"qdrant_host"`
|
||||||
|
|
@ -50,7 +47,6 @@ type KBSearch struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultKBConfig returns sensible defaults for knowledge base config.
|
// DefaultKBConfig returns sensible defaults for knowledge base config.
|
||||||
//
|
|
||||||
func DefaultKBConfig() *KBConfig {
|
func DefaultKBConfig() *KBConfig {
|
||||||
return &KBConfig{
|
return &KBConfig{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
|
|
@ -72,7 +68,6 @@ func DefaultKBConfig() *KBConfig {
|
||||||
|
|
||||||
// LoadKBConfig reads .core/kb.yaml from the given workspace root directory.
|
// LoadKBConfig reads .core/kb.yaml from the given workspace root directory.
|
||||||
// Returns defaults if the file does not exist.
|
// Returns defaults if the file does not exist.
|
||||||
//
|
|
||||||
func LoadKBConfig(m io.Medium, root string) (*KBConfig, error) {
|
func LoadKBConfig(m io.Medium, root string) (*KBConfig, error) {
|
||||||
path := filepath.Join(root, ".core", "kb.yaml")
|
path := filepath.Join(root, ".core", "kb.yaml")
|
||||||
|
|
||||||
|
|
@ -94,7 +89,6 @@ func LoadKBConfig(m io.Medium, root string) (*KBConfig, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveKBConfig writes .core/kb.yaml to the given workspace root directory.
|
// SaveKBConfig writes .core/kb.yaml to the given workspace root directory.
|
||||||
//
|
|
||||||
func SaveKBConfig(m io.Medium, root string, kb *KBConfig) error {
|
func SaveKBConfig(m io.Medium, root string, kb *KBConfig) error {
|
||||||
coreDir := filepath.Join(root, ".core")
|
coreDir := filepath.Join(root, ".core")
|
||||||
if err := m.EnsureDir(coreDir); err != nil {
|
if err := m.EnsureDir(coreDir); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Registry represents a collection of repositories defined in repos.yaml.
|
// Registry represents a collection of repositories defined in repos.yaml.
|
||||||
//
|
|
||||||
type Registry struct {
|
type Registry struct {
|
||||||
Version int `yaml:"version"`
|
Version int `yaml:"version"`
|
||||||
Org string `yaml:"org"`
|
Org string `yaml:"org"`
|
||||||
|
|
@ -27,7 +26,6 @@ type Registry struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegistryDefaults contains default values applied to all repos.
|
// RegistryDefaults contains default values applied to all repos.
|
||||||
//
|
|
||||||
type RegistryDefaults struct {
|
type RegistryDefaults struct {
|
||||||
CI string `yaml:"ci"`
|
CI string `yaml:"ci"`
|
||||||
License string `yaml:"license"`
|
License string `yaml:"license"`
|
||||||
|
|
@ -35,7 +33,6 @@ type RegistryDefaults struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RepoType indicates the role of a repository in the ecosystem.
|
// RepoType indicates the role of a repository in the ecosystem.
|
||||||
//
|
|
||||||
type RepoType string
|
type RepoType string
|
||||||
|
|
||||||
// Repository type constants for ecosystem classification.
|
// Repository type constants for ecosystem classification.
|
||||||
|
|
@ -55,7 +52,6 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Repo represents a single repository in the registry.
|
// Repo represents a single repository in the registry.
|
||||||
//
|
|
||||||
type Repo struct {
|
type Repo struct {
|
||||||
Name string `yaml:"-"` // Set from map key
|
Name string `yaml:"-"` // Set from map key
|
||||||
Type string `yaml:"type"`
|
Type string `yaml:"type"`
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import (
|
||||||
|
|
||||||
// WorkConfig holds sync policy for a workspace.
|
// WorkConfig holds sync policy for a workspace.
|
||||||
// Stored at .core/work.yaml and checked into git (shared across the team).
|
// Stored at .core/work.yaml and checked into git (shared across the team).
|
||||||
//
|
|
||||||
type WorkConfig struct {
|
type WorkConfig struct {
|
||||||
Version int `yaml:"version"`
|
Version int `yaml:"version"`
|
||||||
Sync SyncConfig `yaml:"sync"`
|
Sync SyncConfig `yaml:"sync"`
|
||||||
|
|
@ -22,7 +21,6 @@ type WorkConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SyncConfig controls how and when repos are synced.
|
// SyncConfig controls how and when repos are synced.
|
||||||
//
|
|
||||||
type SyncConfig struct {
|
type SyncConfig struct {
|
||||||
Interval time.Duration `yaml:"interval"`
|
Interval time.Duration `yaml:"interval"`
|
||||||
AutoPull bool `yaml:"auto_pull"`
|
AutoPull bool `yaml:"auto_pull"`
|
||||||
|
|
@ -31,7 +29,6 @@ type SyncConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AgentPolicy controls multi-agent clash prevention.
|
// AgentPolicy controls multi-agent clash prevention.
|
||||||
//
|
|
||||||
type AgentPolicy struct {
|
type AgentPolicy struct {
|
||||||
Heartbeat time.Duration `yaml:"heartbeat"`
|
Heartbeat time.Duration `yaml:"heartbeat"`
|
||||||
StaleAfter time.Duration `yaml:"stale_after"`
|
StaleAfter time.Duration `yaml:"stale_after"`
|
||||||
|
|
@ -39,7 +36,6 @@ type AgentPolicy struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultWorkConfig returns sensible defaults for workspace sync.
|
// DefaultWorkConfig returns sensible defaults for workspace sync.
|
||||||
//
|
|
||||||
func DefaultWorkConfig() *WorkConfig {
|
func DefaultWorkConfig() *WorkConfig {
|
||||||
return &WorkConfig{
|
return &WorkConfig{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
|
|
@ -60,7 +56,6 @@ func DefaultWorkConfig() *WorkConfig {
|
||||||
|
|
||||||
// LoadWorkConfig reads .core/work.yaml from the given workspace root directory.
|
// LoadWorkConfig reads .core/work.yaml from the given workspace root directory.
|
||||||
// Returns defaults if the file does not exist.
|
// Returns defaults if the file does not exist.
|
||||||
//
|
|
||||||
func LoadWorkConfig(m io.Medium, root string) (*WorkConfig, error) {
|
func LoadWorkConfig(m io.Medium, root string) (*WorkConfig, error) {
|
||||||
path := filepath.Join(root, ".core", "work.yaml")
|
path := filepath.Join(root, ".core", "work.yaml")
|
||||||
|
|
||||||
|
|
@ -82,7 +77,6 @@ func LoadWorkConfig(m io.Medium, root string) (*WorkConfig, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveWorkConfig writes .core/work.yaml to the given workspace root directory.
|
// SaveWorkConfig writes .core/work.yaml to the given workspace root directory.
|
||||||
//
|
|
||||||
func SaveWorkConfig(m io.Medium, root string, wc *WorkConfig) error {
|
func SaveWorkConfig(m io.Medium, root string, wc *WorkConfig) error {
|
||||||
coreDir := filepath.Join(root, ".core")
|
coreDir := filepath.Join(root, ".core")
|
||||||
if err := m.EnsureDir(coreDir); err != nil {
|
if err := m.EnsureDir(coreDir); err != nil {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue