Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pkg/cli/deps_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ func DisplayDependencyReport(report *DependencyReport) {

// DisplayDependencyReportJSON outputs the dependency report in JSON format
func DisplayDependencyReportJSON(report *DependencyReport) error {
depsReportLog.Printf("Generating JSON dependency report: %d total, %d outdated, %d advisories", report.TotalDeps, len(report.Outdated), len(report.Advisories))

// Calculate percentages
outdatedPercentage := 0.0
if report.DirectDeps > 0 {
Expand Down Expand Up @@ -270,6 +272,8 @@ type DependencyInfoWithIndirect struct {

// parseGoModWithIndirect parses go.mod including indirect dependencies
func parseGoModWithIndirect(path string) ([]DependencyInfoWithIndirect, error) {
depsReportLog.Printf("Parsing go.mod file: %s", path)

content, err := os.ReadFile(path)
if err != nil {
return nil, err
Expand Down Expand Up @@ -313,6 +317,7 @@ func parseGoModWithIndirect(path string) ([]DependencyInfoWithIndirect, error) {
}
}

depsReportLog.Printf("Parsed go.mod: %d total dependencies", len(deps))
return deps, nil
}

Expand Down
8 changes: 8 additions & 0 deletions pkg/parser/import_cycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import "sort"
// findCyclePath uses DFS to find a complete cycle path in the dependency graph.
// Returns a path showing the full chain including the back-edge (e.g., ["b.md", "c.md", "d.md", "b.md"]).
func findCyclePath(cycleNodes map[string]bool, dependencies map[string][]string) []string {
importLog.Printf("Finding cycle path among %d cycle nodes", len(cycleNodes))

// Pick any node in the cycle as a starting point (use sorted order for determinism)
var startNode string
sortedNodes := make([]string, 0, len(cycleNodes))
Expand All @@ -18,16 +20,21 @@ func findCyclePath(cycleNodes map[string]bool, dependencies map[string][]string)
if len(sortedNodes) > 0 {
startNode = sortedNodes[0]
} else {
importLog.Print("No cycle nodes found, cannot determine cycle path")
return nil
}

importLog.Printf("Starting DFS cycle detection from node: %s", startNode)

// Use DFS to find a path from startNode back to itself
visited := make(map[string]bool)
path := []string{}
if dfsForCycle(startNode, startNode, cycleNodes, dependencies, visited, &path, true) {
importLog.Printf("Cycle path found: %v", path)
return path
}

importLog.Print("DFS completed but no cycle path could be constructed")
return nil
}

Expand All @@ -53,6 +60,7 @@ func dfsForCycle(current, target string, cycleNodes map[string]bool, dependencie
for _, dep := range sortedDeps {
// Found the cycle - we've reached the target again
if !isFirst && dep == target {
importLog.Printf("Cycle back-edge found: %s -> %s", current, dep)
*path = append(*path, dep) // Add the back-edge
return true
}
Expand Down
13 changes: 11 additions & 2 deletions pkg/parser/schedule_cron_detection.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ func IsDailyCron(cron string) bool {
}
}

return fields[2] == "*" && fields[3] == "*" && fields[4] == "*"
result := fields[2] == "*" && fields[3] == "*" && fields[4] == "*"
if result {
log.Printf("Cron expression classified as daily: %q (minute=%s, hour=%s)", cron, minute, hour)
}
return result
}

// IsHourlyCron checks if a cron expression represents an hourly interval with a fixed minute
Expand Down Expand Up @@ -67,7 +71,11 @@ func IsHourlyCron(cron string) bool {
}

// Check remaining fields are wildcards
return fields[2] == "*" && fields[3] == "*" && fields[4] == "*"
result := fields[2] == "*" && fields[3] == "*" && fields[4] == "*"
if result {
log.Printf("Cron expression classified as hourly: %q (minute=%s, hour=%s)", cron, minute, hour)
}
return result
}

// IsWeeklyCron checks if a cron expression represents a weekly schedule at a fixed time
Expand Down Expand Up @@ -125,6 +133,7 @@ func IsCronExpression(input string) bool {
// A cron expression has exactly 5 fields
fields := strings.Fields(input)
if len(fields) != 5 {
log.Printf("Input is not a cron expression (expected 5 fields, got %d): %q", len(fields), input)
return false
}

Expand Down
7 changes: 7 additions & 0 deletions pkg/parser/schema_compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,18 +137,23 @@ func GetSafeOutputTypeKeys() ([]string, error) {
}

func validateWithSchema(frontmatter map[string]any, schemaJSON, context string) error {
schemaCompilerLog.Printf("Validating frontmatter against schema for context: %s (%d fields)", context, len(frontmatter))

// Determine which cached schema to use based on the schemaJSON
var schema *jsonschema.Schema
var err error

switch schemaJSON {
case mainWorkflowSchema:
schemaCompilerLog.Print("Using cached main workflow schema")
schema, err = getCompiledMainWorkflowSchema()
case mcpConfigSchema:
schemaCompilerLog.Print("Using cached MCP config schema")
schema, err = getCompiledMcpConfigSchema()
default:
// Fallback for unknown schemas (shouldn't happen in normal operation)
// Compile the schema on-the-fly
schemaCompilerLog.Print("Compiling unknown schema on-the-fly")
schema, err = compileSchema(schemaJSON, "http://contoso.com/schema.json")
}

Expand Down Expand Up @@ -177,9 +182,11 @@ func validateWithSchema(frontmatter map[string]any, schemaJSON, context string)

// Validate the normalized frontmatter
if err := schema.Validate(normalizedFrontmatter); err != nil {
schemaCompilerLog.Printf("Schema validation failed for %s: %v", context, err)
return err
}

schemaCompilerLog.Printf("Schema validation passed for context: %s", context)
return nil
}

Expand Down
10 changes: 7 additions & 3 deletions pkg/workflow/codex_mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,13 @@ func (e *CodexEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]an
// renderCodexMCPConfigWithContext generates custom MCP server configuration for a single tool in codex workflow config.toml
// This version includes workflowData to determine if localhost URLs should be rewritten
func (e *CodexEngine) renderCodexMCPConfigWithContext(yaml *strings.Builder, toolName string, toolConfig map[string]any, workflowData *WorkflowData) error {
yaml.WriteString(" \n")
fmt.Fprintf(yaml, " [mcp_servers.%s]\n", toolName)

// Determine if localhost URLs should be rewritten to host.docker.internal
// This is needed when firewall is enabled (agent is not disabled)
rewriteLocalhost := shouldRewriteLocalhostToDocker(workflowData)
codexMCPLog.Printf("Rendering TOML MCP config for custom tool: %s (rewrite_localhost=%v)", toolName, rewriteLocalhost)

yaml.WriteString(" \n")
fmt.Fprintf(yaml, " [mcp_servers.%s]\n", toolName)

// Use the shared MCP config renderer with TOML format
renderer := MCPConfigRenderer{
Expand All @@ -180,6 +181,7 @@ func (e *CodexEngine) renderCodexMCPConfigWithContext(yaml *strings.Builder, too

err := renderSharedMCPConfig(yaml, toolName, toolConfig, renderer)
if err != nil {
codexMCPLog.Printf("Failed to render TOML MCP config for tool %s: %v", toolName, err)
return err
}

Expand All @@ -191,6 +193,7 @@ func (e *CodexEngine) renderCodexMCPConfigWithContext(yaml *strings.Builder, too
func (e *CodexEngine) renderCodexJSONMCPConfigWithContext(yaml *strings.Builder, toolName string, toolConfig map[string]any, isLast bool, workflowData *WorkflowData) error {
// Determine if localhost URLs should be rewritten to host.docker.internal
rewriteLocalhost := shouldRewriteLocalhostToDocker(workflowData)
codexMCPLog.Printf("Rendering JSON MCP config for gateway tool: %s (isLast=%v, rewrite_localhost=%v)", toolName, isLast, rewriteLocalhost)

// Use the shared renderer with JSON format for gateway
renderer := MCPConfigRenderer{
Expand All @@ -203,6 +206,7 @@ func (e *CodexEngine) renderCodexJSONMCPConfigWithContext(yaml *strings.Builder,

err := renderSharedMCPConfig(yaml, toolName, toolConfig, renderer)
if err != nil {
codexMCPLog.Printf("Failed to render JSON MCP config for tool %s: %v", toolName, err)
return err
}

Expand Down
Loading