201 lines
6.7 KiB
Go
201 lines
6.7 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"gitea.maximumdirect.net/eric/promptkit"
|
|
appconfig "gitea.maximumdirect.net/eric/scriptorium/internal/config"
|
|
appformat "gitea.maximumdirect.net/eric/scriptorium/internal/format"
|
|
)
|
|
|
|
type promptInspectionConfig struct {
|
|
configPath, promptDir, promptID, promptVersion, outputPath string
|
|
configExplicit bool
|
|
outputFormat appformat.OutputFormat
|
|
}
|
|
|
|
type profileInspectionConfig struct {
|
|
configPath, profileDir, profileID, outputPath string
|
|
configExplicit bool
|
|
outputFormat appformat.OutputFormat
|
|
}
|
|
|
|
// emptyPromptDefinitionFS satisfies Promptkit's engine-level prompt-source
|
|
// requirement without exposing the caller's working directory. Profile
|
|
// inspection never reads this source.
|
|
type emptyPromptDefinitionFS struct{}
|
|
|
|
func (emptyPromptDefinitionFS) Open(name string) (fs.File, error) {
|
|
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
|
|
}
|
|
|
|
func inspectCommand(args []string, stdout, stderr io.Writer) int {
|
|
if len(args) == 0 {
|
|
fmt.Fprintln(stderr, "inspect parse error: inspection mode is required")
|
|
return ExitRuntimeError
|
|
}
|
|
if args[0] == "profile" {
|
|
return inspectProfileCommand(args[1:], stdout, stderr)
|
|
}
|
|
if args[0] != "prompt" {
|
|
fmt.Fprintln(stderr, "inspect parse error: unknown inspection mode")
|
|
return ExitRuntimeError
|
|
}
|
|
cfg, err := parsePromptInspectionArgs(args[1:])
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "inspect parse error: %v\n", err)
|
|
return ExitRuntimeError
|
|
}
|
|
settings, err := resolveAppSettingsForPromptInspection(cfg)
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "inspect error: %v\n", err)
|
|
return ExitRuntimeError
|
|
}
|
|
engine, err := newEngine(settings)
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "engine error: %v\n", err)
|
|
return ExitRuntimeError
|
|
}
|
|
inspection, err := engine.InspectPrompt(context.Background(), cfg.promptID, cfg.promptVersion)
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "inspect error: %v\n", err)
|
|
return ExitRuntimeError
|
|
}
|
|
data, err := appformat.FormatPromptInspection(inspection, cfg.outputFormat)
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "inspect error: %v\n", err)
|
|
return ExitRuntimeError
|
|
}
|
|
if err := writeOutput(stdout, cfg.outputPath, data); err != nil {
|
|
fmt.Fprintf(stderr, "output write error: %v\n", err)
|
|
return ExitRuntimeError
|
|
}
|
|
return ExitOK
|
|
}
|
|
|
|
func inspectProfileCommand(args []string, stdout, stderr io.Writer) int {
|
|
cfg, err := parseProfileInspectionArgs(args)
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "inspect parse error: %v\n", err)
|
|
return ExitRuntimeError
|
|
}
|
|
settings, err := resolveAppSettingsForProfileInspection(cfg)
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "inspect error: %v\n", err)
|
|
return ExitRuntimeError
|
|
}
|
|
engine, err := newEngine(settings, promptkit.WithPromptFS(emptyPromptDefinitionFS{}, "."))
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "engine error: %v\n", err)
|
|
return ExitRuntimeError
|
|
}
|
|
inspection, err := engine.InspectProfile(context.Background(), cfg.profileID)
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "inspect error: %v\n", err)
|
|
return ExitRuntimeError
|
|
}
|
|
data, err := appformat.FormatProfileInspection(inspection, cfg.outputFormat)
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "inspect error: %v\n", err)
|
|
return ExitRuntimeError
|
|
}
|
|
if err := writeOutput(stdout, cfg.outputPath, data); err != nil {
|
|
fmt.Fprintf(stderr, "output write error: %v\n", err)
|
|
return ExitRuntimeError
|
|
}
|
|
return ExitOK
|
|
}
|
|
|
|
func parseProfileInspectionArgs(args []string) (*profileInspectionConfig, error) {
|
|
cfg := &profileInspectionConfig{outputFormat: appformat.DefaultOutputFormat}
|
|
fs := flag.NewFlagSet("inspect profile", flag.ContinueOnError)
|
|
fs.SetOutput(io.Discard)
|
|
registerConfigPathFlag(fs, &cfg.configPath)
|
|
fs.StringVar(&cfg.profileDir, "profile-dir", "", "directory containing execution profiles")
|
|
fs.StringVar(&cfg.profileID, "profile", "", "profile ID to inspect")
|
|
rawFormat := ""
|
|
fs.StringVar(&rawFormat, "format", "", "output format: text or json")
|
|
fs.StringVar(&cfg.outputPath, "out", "", "optional output file path")
|
|
if err := fs.Parse(args); err != nil {
|
|
return nil, err
|
|
}
|
|
cfg.configExplicit = flagWasSet(fs, "config")
|
|
if fs.NArg() > 0 {
|
|
return nil, fmt.Errorf("unexpected positional args: %v", fs.Args())
|
|
}
|
|
if strings.TrimSpace(cfg.profileID) == "" {
|
|
return nil, errors.New("--profile is required")
|
|
}
|
|
format, err := appformat.ParseOutputFormat(rawFormat)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cfg.outputFormat = format
|
|
if cfg.outputPath != "" {
|
|
cfg.outputPath = filepath.Clean(cfg.outputPath)
|
|
}
|
|
return cfg, nil
|
|
}
|
|
|
|
func resolveAppSettingsForProfileInspection(cfg *profileInspectionConfig) (engineSettings, error) {
|
|
settings, err := resolveAppSettingsWithConfigPresence(cfg.configPath, cfg.configExplicit, appconfig.CLIOverrides{
|
|
ProfileDir: cfg.profileDir,
|
|
})
|
|
if err != nil {
|
|
return engineSettings{}, err
|
|
}
|
|
return engineSettings{profileDir: settings.ProfileDir, backends: settings.Backends}, nil
|
|
}
|
|
|
|
func parsePromptInspectionArgs(args []string) (*promptInspectionConfig, error) {
|
|
cfg := &promptInspectionConfig{outputFormat: appformat.DefaultOutputFormat}
|
|
fs := flag.NewFlagSet("inspect prompt", flag.ContinueOnError)
|
|
fs.SetOutput(io.Discard)
|
|
registerConfigPathFlag(fs, &cfg.configPath)
|
|
fs.StringVar(&cfg.promptDir, "prompt-dir", "", "directory containing prompt definitions")
|
|
fs.StringVar(&cfg.promptID, "prompt", "", "prompt ID to inspect")
|
|
fs.StringVar(&cfg.promptVersion, "prompt-version", "", "optional prompt version")
|
|
rawFormat := ""
|
|
fs.StringVar(&rawFormat, "format", "", "output format: text or json")
|
|
fs.StringVar(&cfg.outputPath, "out", "", "optional output file path")
|
|
if err := fs.Parse(args); err != nil {
|
|
return nil, err
|
|
}
|
|
cfg.configExplicit = flagWasSet(fs, "config")
|
|
if fs.NArg() > 0 {
|
|
return nil, fmt.Errorf("unexpected positional args: %v", fs.Args())
|
|
}
|
|
if strings.TrimSpace(cfg.promptID) == "" {
|
|
return nil, errors.New("--prompt is required")
|
|
}
|
|
format, err := appformat.ParseOutputFormat(rawFormat)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cfg.outputFormat = format
|
|
if cfg.outputPath != "" {
|
|
cfg.outputPath = filepath.Clean(cfg.outputPath)
|
|
}
|
|
return cfg, nil
|
|
}
|
|
|
|
func resolveAppSettingsForPromptInspection(cfg *promptInspectionConfig) (engineSettings, error) {
|
|
settings, err := resolveAppSettingsWithConfigPresence(cfg.configPath, cfg.configExplicit, appconfig.CLIOverrides{
|
|
PromptDir: cfg.promptDir,
|
|
})
|
|
if err != nil {
|
|
return engineSettings{}, err
|
|
}
|
|
if strings.TrimSpace(settings.PromptDir) == "" {
|
|
return engineSettings{}, errors.New(errPromptDirRequired)
|
|
}
|
|
return engineSettings{promptDir: settings.PromptDir, backends: settings.Backends}, nil
|
|
}
|