Documentation
¶
Overview ¶
Package launch resolves a launch request into a runnable command without touching the terminal, so both the CLI and a TUI can drive it.
Every condition a user must see comes back as a value: an advisory Warning, or one of the typed errors below. Nothing here writes to stdout, stderr, or reads from stdin.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNoModel = errors.New("no model selected")
ErrNoModel reports that no model was selected. It is deliberately bare: the CLI's message names a CLI flag and the binary, which this package has no business knowing. Phase 2 turns this branch into "open the picker".
Functions ¶
func CheckSupported ¶
CheckSupported reports why an agent cannot be pointed at the bound provider. It is Plan's first guard, and `profile add` calls it directly to refuse saving a profile for an unsupported agent without planning a launch.
Types ¶
type NotInstalledError ¶
NotInstalledError reports a missing agent binary. The hint is a separate field rather than baked into the message so a caller can render it as something other than a line of error text.
func (*NotInstalledError) Error ¶
func (e *NotInstalledError) Error() string
type Plan ¶
type Plan struct {
Spec *agent.Spec
Model catalog.Model
Command agent.Command
// AgentRequest is the request Command was built from. The fork-and-wait
// path re-uses it for ConfigWriter.Apply, which cannot run at plan time
// (it writes).
AgentRequest agent.Request
// Staged are launcher-owned files Launch materializes before the
// handoff. Computed here (purely) so Launch stays a straight line.
Staged []agent.StagedFile
Warnings []Warning
}
Plan is a resolved launch: a runnable command plus the conditions the caller must render and, where Warning.Question is set, get approved.
type Request ¶
type Request struct {
// Spec must be non-nil; Plan dereferences it unconditionally starting
// with its first guard, CheckSupported.
Spec *agent.Spec
ModelID string
ExtraArgs []string
// Refresh bypasses the cached catalog.
Refresh bool
}
Request is a launch request.
type Service ¶
type Service struct {
// LoadCatalog returns the model catalog with its provenance, honoring a
// refresh request. Required: there is no endpoint this package could
// default to. openrouter.Snapshotter builds this tool's.
LoadCatalog func(ctx context.Context, refresh bool) (catalog.Snapshot, error)
// APIKey resolves the credential a launch carries. Required.
//
// Plan returns this error UNWRAPPED. The TUI tests it with errors.Is to
// decide whether to prompt for a key in place rather than abort the
// session, so a %w around it here would still satisfy errors.Is — but a
// reformatting into a new error would not, and that is the regression
// TestPlanReturnsTheKeyErrorUnwrapped exists to catch.
APIKey func() (string, error)
// RecordSelection persists the agent and model just launched, so the next
// run can preselect them. nil means this tool does not remember
// selections; it is not an error, and Launch carries on either way — a
// remembered choice is a convenience, never a precondition for starting
// an agent.
RecordSelection func(agentName, modelID string) error
// StageDir names the directory launcher-owned staged files live in, and
// which stageFiles refuses to write outside of. Required whenever a
// launcher stages anything. It is one field rather than two so the value
// a launcher computes its staged paths against and the value the boundary
// check enforces cannot drift apart.
StageDir func() (string, error)
// Run performs the process handoff. nil means agent.Run.
Run func(agent.Command) error
// RunWait performs the fork-and-wait handoff for ConfigWriter agents.
// nil means agent.RunWait.
RunWait func(agent.Command) error
}
Service resolves launch requests and hands off to agents.
Every field below is a function the CALLER supplies, and that is the whole design of this package: a planner shared by more than one launcher tool cannot know which endpoint to fetch a catalog from, where that tool keeps its cache, what its settings file looks like, or which directory is "ours" to stage files in. Each of those was once a package-level call into this tool's own config and OpenRouter client; each is now a seam the composition root fills (see cli.newService).
Run and RunWait keep working defaults because the process handoff genuinely is the same everywhere — it is a syscall, not a policy.
func (*Service) Launch ¶
Launch records the selection and then hands off to the agent.
The order is load-bearing: on Unix the handoff is syscall.Exec, which replaces the process, so nothing after it runs. Recording afterwards would mean never recording at all. Save and handoff live in one function so that no call site can get the order wrong.
warn is called synchronously for any non-fatal problem encountered before the handoff. It must not block, and may be nil. It cannot be a return value for the same reason the ordering matters: on Unix, Launch does not return on success, so a returned warning would never be seen.
A settings store that cannot be read or written costs the user their remembered last selection. That is a convenience, not a precondition, so it warns rather than refusing to start the agent — and a Service with no RecordSelection at all simply skips the step.
func (*Service) Plan ¶
Plan resolves req into a runnable command. It performs IO - through the caller's catalog loader and key resolver - but never touches the terminal: every condition a user must see comes back as a Warning or a typed error.
Warnings accumulated before a fatal guard are returned alongside the error, not discarded. Callers must render Plan.Warnings before inspecting err - "p, err := svc.Plan(...); if err != nil { return err }" silently drops them, and a stale catalog is frequently the reason a later guard failed.
The guard order is load-bearing. It decides which of several simultaneous problems the user is told about first, and the empty-model check sits deliberately ahead of the install check so that a user with no agent installed still reaches the model picker in Phase 2.
Confirmation is NOT performed here. The caller renders the warnings, obtains approval, and only then calls Launch.
type UnknownModelError ¶
UnknownModelError reports a slug that matched nothing, carrying the suggestions as data so a caller can offer them as choices rather than as a formatted list.
func (*UnknownModelError) Error ¶
func (e *UnknownModelError) Error() string
type UnsupportedAgentError ¶
type UnsupportedAgentError struct {
Agent string
// Provider is the bound provider's display name. Empty is legitimate —
// a Spec built without a Binding has none — and Error() then says "this
// provider" rather than leaving a blank where a name belongs.
Provider string
Reason string
}
UnsupportedAgentError reports an agent that cannot be pointed at the bound provider at all.
func (*UnsupportedAgentError) Error ¶
func (e *UnsupportedAgentError) Error() string
type UnsupportedPlatformError ¶
UnsupportedPlatformError reports an agent that cannot run on this platform. Error() is the launcher's own message unchanged, so CLI output is what it always was; Agent is carried for callers that want to name the agent themselves.
func (*UnsupportedPlatformError) Error ¶
func (e *UnsupportedPlatformError) Error() string
func (*UnsupportedPlatformError) Unwrap ¶
func (e *UnsupportedPlatformError) Unwrap() error
type Warning ¶
type Warning struct {
Kind WarningKind
// Message is the diagnostic text, rendered after the caller's own
// "warning: " prefix.
Message string
// Question is non-empty when the caller must get the user's approval
// before launching, and is the prompt to put to them. Carrying the
// wording here rather than a bare Confirm bool means a caller cannot
// ask "Launch anyway?" about a warning that is not about launching.
Question string
}
Warning is an advisory condition the caller renders.
type WarningKind ¶
type WarningKind int
WarningKind identifies an advisory condition so a caller can render it in its own idiom instead of parsing Message.
const ( // WarnStaleCatalog reports that a catalog refresh failed and cached data // was served instead. WarnStaleCatalog WarningKind = iota // WarnIncompatibleModel reports a pairing the agent may not fully // support. Advisory by design: Claude Code works with many non-Anthropic // models, so hard-blocking would refuse valid setups. WarnIncompatibleModel // WarnSelectionNotSaved reports that the last selection could not be // persisted. The launch proceeds regardless. WarnSelectionNotSaved // WarnShadowedCredential reports agent-side stored credentials or state // that outrank the environment this launch provides. Advisory: the // wrong-account risk is made visible, the user decides. WarnShadowedCredential )