// Package deliver is executus's output-egress seam. // // Where a run's final output and any generated artifacts go is host-specific: // Mort posts a Discord embed (with a paste fallback and state-react emoji), // Gadfly consolidates findings into one PR comment, a CLI host prints to stdout. // The harness depends only on the Delivery interface and ships two defaults so a // new host needs no wiring: Discard (return output to the caller only) and // Stdout. package deliver import ( "context" "fmt" "io" "os" ) // Target names where output should land. Its fields are host-interpreted (a // Discord channel ID, a PR number, etc.); the harness never parses them. type Target struct { Kind string // host-defined: "channel", "dm", "thread", "stdout", "comment", ... ID string } // Artifact is a generated file accompanying a run's output (an image, a report, // a STL, ...). Bytes are owned by the caller; a Delivery must not retain them // past the call without copying. type Artifact struct { Name string MIME string Bytes []byte } // Delivery is the output seam. Deliver returns a host-defined id for the posted // output when one exists (a message ID, a paste URL); an empty id is fine. // Implementations must be nil-safe for a nil/empty artifacts slice. type Delivery interface { Deliver(ctx context.Context, t Target, output string, artifacts []Artifact) (id string, err error) DeliverError(ctx context.Context, t Target, runErr error) error } // Discard is the light-host default: it drops the output (the caller already has // it as the run Result). Gadfly uses this — it reads results in-process and does // its own consolidation. type Discard struct{} func (Discard) Deliver(context.Context, Target, string, []Artifact) (string, error) { return "", nil } func (Discard) DeliverError(context.Context, Target, error) error { return nil } // Stdout writes output (and an artifact manifest) to an io.Writer, defaulting to // os.Stdout. Handy for local/dev and example hosts. type Stdout struct{ W io.Writer } func (s Stdout) w() io.Writer { if s.W != nil { return s.W } return os.Stdout } func (s Stdout) Deliver(_ context.Context, t Target, output string, artifacts []Artifact) (string, error) { w := s.w() if t.Kind != "" || t.ID != "" { fmt.Fprintf(w, "[%s:%s]\n", t.Kind, t.ID) } fmt.Fprintln(w, output) for _, a := range artifacts { fmt.Fprintf(w, " \n", a.Name, a.MIME, len(a.Bytes)) } return "", nil } func (s Stdout) DeliverError(_ context.Context, _ Target, runErr error) error { fmt.Fprintf(s.w(), "ERROR: %v\n", runErr) return nil }