mirror of
https://github.com/ollama/ollama.git
synced 2026-04-18 08:54:13 +02:00
Compare commits
1 Commits
pdevine/sa
...
jmorganca/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f1930cfd6 |
12
cmd/cmd.go
12
cmd/cmd.go
@@ -1826,6 +1826,18 @@ func NewCLI() *cobra.Command {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If no args, run launch to show interactive app selector
|
||||||
|
if len(args) == 0 {
|
||||||
|
if err := checkServerHeartbeat(cmd, args); err != nil {
|
||||||
|
cobra.CheckErr(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := config.RunLaunch(cmd, args, "", false); err != nil {
|
||||||
|
cobra.CheckErr(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
cmd.Print(cmd.UsageString())
|
cmd.Print(cmd.UsageString())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,12 @@ var integrations = map[string]Runner{
|
|||||||
"openclaw": &Openclaw{},
|
"openclaw": &Openclaw{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsIntegration returns true if the given name is a valid integration.
|
||||||
|
func IsIntegration(name string) bool {
|
||||||
|
_, ok := integrations[strings.ToLower(name)]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
// recommendedModels are shown when the user has no models or as suggestions.
|
// recommendedModels are shown when the user has no models or as suggestions.
|
||||||
// Order matters: local models first, then cloud models.
|
// Order matters: local models first, then cloud models.
|
||||||
var recommendedModels = []selectItem{
|
var recommendedModels = []selectItem{
|
||||||
@@ -76,7 +82,7 @@ var integrationAliases = map[string]bool{
|
|||||||
|
|
||||||
func selectIntegration() (string, error) {
|
func selectIntegration() (string, error) {
|
||||||
if len(integrations) == 0 {
|
if len(integrations) == 0 {
|
||||||
return "", fmt.Errorf("no integrations available")
|
return "", fmt.Errorf("no apps available")
|
||||||
}
|
}
|
||||||
|
|
||||||
names := slices.Sorted(maps.Keys(integrations))
|
names := slices.Sorted(maps.Keys(integrations))
|
||||||
@@ -93,14 +99,14 @@ func selectIntegration() (string, error) {
|
|||||||
items = append(items, selectItem{Name: name, Description: description})
|
items = append(items, selectItem{Name: name, Description: description})
|
||||||
}
|
}
|
||||||
|
|
||||||
return selectPrompt("Select integration:", items)
|
return selectPrompt("Select app:", items)
|
||||||
}
|
}
|
||||||
|
|
||||||
// selectModels lets the user select models for an integration
|
// selectModels lets the user select models for an integration
|
||||||
func selectModels(ctx context.Context, name, current string) ([]string, error) {
|
func selectModels(ctx context.Context, name, current string) ([]string, error) {
|
||||||
r, ok := integrations[name]
|
r, ok := integrations[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("unknown integration: %s", name)
|
return nil, fmt.Errorf("unknown app: %s", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := api.ClientFromEnvironment()
|
client, err := api.ClientFromEnvironment()
|
||||||
@@ -306,7 +312,7 @@ func ensureAuth(ctx context.Context, client *api.Client, cloudModels map[string]
|
|||||||
func runIntegration(name, modelName string, args []string) error {
|
func runIntegration(name, modelName string, args []string) error {
|
||||||
r, ok := integrations[name]
|
r, ok := integrations[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("unknown integration: %s", name)
|
return fmt.Errorf("unknown app: %s", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "\nLaunching %s with %s...\n", r, modelName)
|
fmt.Fprintf(os.Stderr, "\nLaunching %s with %s...\n", r, modelName)
|
||||||
@@ -335,33 +341,10 @@ func syncAliases(ctx context.Context, client *api.Client, ac AliasConfigurer, na
|
|||||||
return saveAliases(name, aliases)
|
return saveAliases(name, aliases)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LaunchCmd returns the cobra command for launching integrations.
|
// RunLaunch executes the launch logic for the given integration and arguments.
|
||||||
func LaunchCmd(checkServerHeartbeat func(cmd *cobra.Command, args []string) error) *cobra.Command {
|
// This can be called directly from the root command (with empty modelFlag/configFlag)
|
||||||
var modelFlag string
|
// or via the launch subcommand.
|
||||||
var configFlag bool
|
func RunLaunch(cmd *cobra.Command, args []string, modelFlag string, configFlag bool) error {
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "launch [INTEGRATION] [-- [EXTRA_ARGS...]]",
|
|
||||||
Short: "Launch an integration with Ollama",
|
|
||||||
Long: `Launch an integration configured with Ollama models.
|
|
||||||
|
|
||||||
Supported integrations:
|
|
||||||
claude Claude Code
|
|
||||||
codex Codex
|
|
||||||
droid Droid
|
|
||||||
opencode OpenCode
|
|
||||||
openclaw OpenClaw (aliases: clawdbot, moltbot)
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
ollama launch
|
|
||||||
ollama launch claude
|
|
||||||
ollama launch claude --model <model>
|
|
||||||
ollama launch droid --config (does not auto-launch)
|
|
||||||
ollama launch codex -- -p myprofile (pass extra args to integration)
|
|
||||||
ollama launch codex -- --sandbox workspace-write`,
|
|
||||||
Args: cobra.ArbitraryArgs,
|
|
||||||
PreRunE: checkServerHeartbeat,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
// Extract integration name and args to pass through using -- separator
|
// Extract integration name and args to pass through using -- separator
|
||||||
var name string
|
var name string
|
||||||
var passArgs []string
|
var passArgs []string
|
||||||
@@ -370,7 +353,7 @@ Examples:
|
|||||||
if dashIdx == -1 {
|
if dashIdx == -1 {
|
||||||
// No "--" separator: only allow 0 or 1 args (integration name)
|
// No "--" separator: only allow 0 or 1 args (integration name)
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
return fmt.Errorf("unexpected arguments: %v\nUse '--' to pass extra arguments to the integration", args[1:])
|
return fmt.Errorf("unexpected arguments: %v\nUse '--' to pass extra arguments to the app", args[1:])
|
||||||
}
|
}
|
||||||
if len(args) == 1 {
|
if len(args) == 1 {
|
||||||
name = args[0]
|
name = args[0]
|
||||||
@@ -378,7 +361,7 @@ Examples:
|
|||||||
} else {
|
} else {
|
||||||
// "--" was used: args before it = integration name, args after = passthrough
|
// "--" was used: args before it = integration name, args after = passthrough
|
||||||
if dashIdx > 1 {
|
if dashIdx > 1 {
|
||||||
return fmt.Errorf("expected at most 1 integration name before '--', got %d", dashIdx)
|
return fmt.Errorf("expected at most 1 app name before '--', got %d", dashIdx)
|
||||||
}
|
}
|
||||||
if dashIdx == 1 {
|
if dashIdx == 1 {
|
||||||
name = args[0]
|
name = args[0]
|
||||||
@@ -399,7 +382,7 @@ Examples:
|
|||||||
|
|
||||||
r, ok := integrations[strings.ToLower(name)]
|
r, ok := integrations[strings.ToLower(name)]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("unknown integration: %s", name)
|
return fmt.Errorf("unknown app: %s", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle AliasConfigurer integrations (claude, codex)
|
// Handle AliasConfigurer integrations (claude, codex)
|
||||||
@@ -561,11 +544,44 @@ Examples:
|
|||||||
if launch, _ := confirmPrompt(fmt.Sprintf("\nLaunch %s now?", r)); launch {
|
if launch, _ := confirmPrompt(fmt.Sprintf("\nLaunch %s now?", r)); launch {
|
||||||
return runIntegration(name, models[0], passArgs)
|
return runIntegration(name, models[0], passArgs)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(os.Stderr, "Run 'ollama launch %s' to start with %s\n", strings.ToLower(name), models[0])
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return runIntegration(name, models[0], passArgs)
|
if runner, isRunner := r.(Runner); isRunner {
|
||||||
|
return runner.Run(models[0], passArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LaunchCmd returns the cobra command for launching integrations.
|
||||||
|
func LaunchCmd(checkServerHeartbeat func(cmd *cobra.Command, args []string) error) *cobra.Command {
|
||||||
|
var modelFlag string
|
||||||
|
var configFlag bool
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "launch [APP] [-- [EXTRA_ARGS...]]",
|
||||||
|
Short: "Launch an app with Ollama",
|
||||||
|
Long: `Launch an app configured with Ollama models.
|
||||||
|
|
||||||
|
Supported apps:
|
||||||
|
claude Claude Code
|
||||||
|
codex Codex
|
||||||
|
droid Droid
|
||||||
|
opencode OpenCode
|
||||||
|
openclaw OpenClaw (aliases: clawdbot, moltbot)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
ollama launch
|
||||||
|
ollama launch claude
|
||||||
|
ollama launch claude --model <model>
|
||||||
|
ollama launch droid --config (does not auto-launch)
|
||||||
|
ollama launch codex -- -p myprofile (pass extra args to app)
|
||||||
|
ollama launch codex -- --sandbox workspace-write`,
|
||||||
|
Args: cobra.ArbitraryArgs,
|
||||||
|
PreRunE: checkServerHeartbeat,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return RunLaunch(cmd, args, modelFlag, configFlag)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,8 +98,8 @@ func TestLaunchCmd(t *testing.T) {
|
|||||||
cmd := LaunchCmd(mockCheck)
|
cmd := LaunchCmd(mockCheck)
|
||||||
|
|
||||||
t.Run("command structure", func(t *testing.T) {
|
t.Run("command structure", func(t *testing.T) {
|
||||||
if cmd.Use != "launch [INTEGRATION] [-- [EXTRA_ARGS...]]" {
|
if cmd.Use != "launch [APP] [-- [EXTRA_ARGS...]]" {
|
||||||
t.Errorf("Use = %q, want %q", cmd.Use, "launch [INTEGRATION] [-- [EXTRA_ARGS...]]")
|
t.Errorf("Use = %q, want %q", cmd.Use, "launch [APP] [-- [EXTRA_ARGS...]]")
|
||||||
}
|
}
|
||||||
if cmd.Short == "" {
|
if cmd.Short == "" {
|
||||||
t.Error("Short description should not be empty")
|
t.Error("Short description should not be empty")
|
||||||
@@ -133,8 +133,8 @@ func TestRunIntegration_UnknownIntegration(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("expected error for unknown integration, got nil")
|
t.Error("expected error for unknown integration, got nil")
|
||||||
}
|
}
|
||||||
if !strings.Contains(err.Error(), "unknown integration") {
|
if !strings.Contains(err.Error(), "unknown app") {
|
||||||
t.Errorf("error should mention 'unknown integration', got: %v", err)
|
t.Errorf("error should mention 'unknown app', got: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user