Go CLI: fix list provider instance tasks (#16345)

This commit is contained in:
Jin Hai
2026-06-25 15:49:31 +08:00
committed by GitHub
parent 3f3a2ece3d
commit edfa9be67f
6 changed files with 183 additions and 234 deletions

View File

@@ -115,7 +115,7 @@ func (p *Parser) parseAdminListCommands() (*Command, error) {
case TokenIngestion:
return p.parseAdminListIngestionTasks()
case TokenAPI:
return p.parseListApiCommand()
return p.parseAdminListAPIServers()
default:
return nil, fmt.Errorf("unknown LIST target: %s", p.curToken.Value)
}
@@ -336,6 +336,26 @@ func (p *Parser) parseAdminListModels() (*Command, error) {
return cmd, nil
}
func (p *Parser) parseAdminListAPIServers() (*Command, error) {
p.nextToken() // consume API
var cmd *Command
switch p.curToken.Type {
case TokenServer:
p.nextToken()
cmd = NewCommand("admin_list_api_servers")
default:
return nil, fmt.Errorf("expected SERVER after API")
}
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// endregion LIST commands
// region SHOW commands
@@ -2227,14 +2247,44 @@ func (p *Parser) parseAdminUseCommand() (*Command, error) {
p.nextToken() // consume USE
switch p.curToken.Type {
case TokenAPI:
return p.parseUseAPIServer()
return p.parseAdminUseAPIServer()
case TokenAdmin:
return p.parseUseAdminServer()
return p.parseAdminUseAdminServer()
default:
return nil, fmt.Errorf("expected API or ADMIN after USE")
}
}
func (p *Parser) parseAdminUseAPIServer() (*Command, error) {
p.nextToken() // consume API
serverName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
cmd := NewCommand("admin_use_api_server")
cmd.Params["server_name"] = serverName
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAdminUseAdminServer() (*Command, error) {
p.nextToken() // consume ADMIN
cmd := NewCommand("admin_use_admin_server")
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseStartIngestion() (*Command, error) {
p.nextToken() // consume Start

View File

@@ -88,7 +88,7 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) {
case "admin_show_version_command":
return c.AdminShowVersionCommand(cmd)
case "admin_show_current":
return c.CommonShowCurrent(cmd)
return c.CommonShowCurrentCommand(cmd)
case "admin_list_variables":
return c.AdminListVariablesCommand(cmd)
case "admin_list_configs":
@@ -260,8 +260,8 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) {
return c.ShowAdminServer(cmd)
case "show_api_server":
return c.ShowAPIServer(cmd)
case "list_api_server":
return c.ListAPIServer(cmd)
case "admin_list_api_servers":
return c.CommonListAPIServers(cmd)
case "add_api_server":
return c.AddAPIServer(cmd)
case "delete_api_server":
@@ -272,10 +272,10 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) {
return nil, fmt.Errorf("cannot delete admin server in admin mode")
case "save_config_command":
return c.SaveServerConfig(cmd)
case "use_api_server":
return c.UseAPIServer(cmd)
case "use_admin_server":
return c.UseAdminServer(cmd)
case "admin_use_api_server":
return c.CommonUseAPIServerCommand(cmd)
case "admin_use_admin_server":
return c.CommonUseAdminServerCommand(cmd)
default:
return nil, fmt.Errorf("command '%s' would be executed with API", cmd.Type)
}
@@ -324,14 +324,14 @@ func (c *CLI) ExecuteUserCommand(cmd *Command) (ResponseIf, error) {
return c.APIDeleteAPIKeyCommand(cmd)
case "api_set_api_key":
return c.APISetAPIKey(cmd)
case "show_token":
return c.ShowToken(cmd)
case "api_unset_api_key":
return c.APIUnsetAPIKey(cmd)
case "show_version":
return c.ShowServerVersion(cmd)
return c.APIUnsetAPIKeyCommand(cmd)
case "api_show_version":
return c.APIShowVersionCommand(cmd)
case "api_show_api_key":
return c.APIShowAPIKeyCommand(cmd)
case "show_current":
return c.CommonShowCurrent(cmd)
return c.CommonShowCurrentCommand(cmd)
case "api_list_available_providers":
return c.CommonAvailableProvidersCommand(cmd)
case "show_provider":
@@ -342,6 +342,8 @@ func (c *CLI) ExecuteUserCommand(cmd *Command) (ResponseIf, error) {
return c.CommonListInstanceModelsCommand(cmd)
case "api_list_provider_instance_models_sync":
return c.CommonListInstanceModelsSyncCommand(cmd)
case "api_list_provider_instance_tasks":
return c.APIListModelInstanceTasksCommand(cmd)
case "show_provider_model":
return c.CommonShowProviderModelCommand(cmd)
case "show_model":
@@ -404,17 +406,15 @@ func (c *CLI) ExecuteUserCommand(cmd *Command) (ResponseIf, error) {
case "use_model":
return c.UseModel(cmd)
case "use_api_server":
return c.UseAPIServer(cmd)
return c.CommonUseAPIServerCommand(cmd)
case "use_admin_server":
return c.UseAdminServer(cmd)
return c.CommonUseAdminServerCommand(cmd)
case "set_default_model":
return c.SetDefaultModel(cmd)
case "reset_default_model":
return c.ResetDefaultModel(cmd)
case "api_list_default_models":
return c.ListDefaultModels(cmd)
case "list_tasks_user_command":
return c.ListTasksUserCommand(cmd)
case "show_task_user_command":
return c.ShowTaskUserCommand(cmd)
case "create_chunk_store":
@@ -460,8 +460,8 @@ func (c *CLI) ExecuteUserCommand(cmd *Command) (ResponseIf, error) {
return c.ShowAdminServer(cmd)
case "show_api_server":
return c.ShowAPIServer(cmd)
case "list_api_server":
return c.ListAPIServer(cmd)
case "api_list_api_servers":
return c.CommonListAPIServers(cmd)
case "add_api_server":
return c.AddAPIServer(cmd)
case "delete_api_server":

View File

@@ -1056,7 +1056,7 @@ func (c *CLI) ListDefaultModels(cmd *Command) (ResponseIf, error) {
return &result, nil
}
func (c *CLI) CommonShowCurrent(cmd *Command) (ResponseIf, error) {
func (c *CLI) CommonShowCurrentCommand(cmd *Command) (ResponseIf, error) {
var result *CommonDataResponse
switch c.Config.CLIMode {
@@ -1117,7 +1117,7 @@ func (c *CLI) ShowAPIServer(cmd *Command) (ResponseIf, error) {
return result, nil
}
func (c *CLI) ListAPIServer(cmd *Command) (ResponseIf, error) {
func (c *CLI) CommonListAPIServers(cmd *Command) (ResponseIf, error) {
var result CommonResponse
result.Data = make([]map[string]interface{}, 0)
@@ -1317,7 +1317,7 @@ func (c *CLI) SaveServerConfig(cmd *Command) (ResponseIf, error) {
return nil, fmt.Errorf("invalid server type")
}
return nil, nil
return nil, fmt.Errorf("save server config isn't implemented")
}
func (c *CLI) GetAdminServerInfo() (ResponseIf, error) {
@@ -1363,12 +1363,17 @@ func (c *CLI) GetAPIServerInfo(serverName string) (ResponseIf, error) {
if apiServerConfig.UserPassword != nil {
result.Data["user_password"] = strings.Repeat("*", len(*apiServerConfig.UserPassword))
}
if c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].LoginToken != nil {
result.Data["auth"] = "login"
} else if c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].APIKey != nil {
result.Data["auth"] = "api key"
if c.Config.APIClientConfig.CurrentAPIServer == "" {
result.Data["auth"] = "unknown"
} else {
result.Data["auth"] = "no auth"
if c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].LoginToken != nil {
result.Data["auth"] = "login"
} else if c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].APIKey != nil {
result.Data["auth"] = "api key"
} else {
result.Data["auth"] = "no auth"
}
}
}
return &result, nil
@@ -1524,7 +1529,7 @@ func FlattenMap(data map[string]interface{}, prefix string, result *[]map[string
}
}
func (c *CLI) UseAPIServer(cmd *Command) (ResponseIf, error) {
func (c *CLI) CommonUseAPIServerCommand(cmd *Command) (ResponseIf, error) {
serverName, ok := cmd.Params["server_name"].(string)
if !ok {
return nil, fmt.Errorf("server_name not provided")
@@ -1564,7 +1569,7 @@ func (c *CLI) UseAPIServer(cmd *Command) (ResponseIf, error) {
}
func (c *CLI) UseAdminServer(cmd *Command) (ResponseIf, error) {
func (c *CLI) CommonUseAdminServerCommand(cmd *Command) (ResponseIf, error) {
if c.Config.CLIMode == AdminMode {
return nil, fmt.Errorf("already in admin mode")
@@ -1589,7 +1594,10 @@ func (c *CLI) getDatasetIDByName(datasetName string) (string, error) {
if err != nil {
return "", err
}
commonResponse := response.(*CommonResponse)
commonResponse, ok := response.(*CommonResponse)
if !ok {
return "", fmt.Errorf("invalid response")
}
for _, dataset := range commonResponse.Data {
if dataset["name"] == datasetName {
return dataset["id"].(string), nil

View File

@@ -151,7 +151,7 @@ func (p *Parser) parseUserCommand() (*Command, error) {
case TokenList:
return p.parseAPIListCommands()
case TokenShow:
return p.parseShowCommand()
return p.parseAPIShowCommands()
case TokenCreate:
return p.parseCreateCommand()
case TokenDrop:

View File

@@ -39,7 +39,7 @@ import (
// Show server version to show RAGFlow server version
// Returns benchmark result map if iterations > 1, otherwise prints status
func (c *CLI) ShowServerVersion(cmd *Command) (ResponseIf, error) {
func (c *CLI) APIShowVersionCommand(cmd *Command) (ResponseIf, error) {
// Get iterations from command params (for benchmark)
iterations := 1
if val, ok := cmd.Params["iterations"].(int); ok && val > 1 {
@@ -1099,8 +1099,8 @@ func (c *CLI) APISetAPIKey(cmd *Command) (ResponseIf, error) {
return &successResult, nil
}
// ShowToken displays the current API key
func (c *CLI) ShowToken(cmd *Command) (ResponseIf, error) {
// APIShowAPIKeyCommand displays the current API key
func (c *CLI) APIShowAPIKeyCommand(cmd *Command) (ResponseIf, error) {
if c.Config.CLIMode != APIMode {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
@@ -1109,22 +1109,18 @@ func (c *CLI) ShowToken(cmd *Command) (ResponseIf, error) {
return nil, fmt.Errorf("no API key is currently set")
}
//fmt.Printf("Token: %s\n", c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].APIKey)
var result CommonResponse
var result CommonDataResponse
result.Code = 0
result.Message = ""
result.Data = []map[string]interface{}{
{
"token": c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].APIKey,
},
result.Data = map[string]interface{}{
"token": *c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].APIKey,
}
result.Duration = 0
return &result, nil
}
// APIUnsetAPIKey removes the current API key
func (c *CLI) APIUnsetAPIKey(cmd *Command) (ResponseIf, error) {
// APIUnsetAPIKeyCommand removes the current API key
func (c *CLI) APIUnsetAPIKeyCommand(cmd *Command) (ResponseIf, error) {
if c.Config.CLIMode != APIMode {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
@@ -2588,7 +2584,7 @@ func (c *CLI) ParseFileUserCommand(cmd *Command) (ResponseIf, error) {
return &result, nil
}
func (c *CLI) ListTasksUserCommand(cmd *Command) (ResponseIf, error) {
func (c *CLI) APIListModelInstanceTasksCommand(cmd *Command) (ResponseIf, error) {
if c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].APIKey == nil && c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].LoginToken == nil {
return nil, fmt.Errorf("API key not set. Please login first")
}
@@ -2597,18 +2593,14 @@ func (c *CLI) ListTasksUserCommand(cmd *Command) (ResponseIf, error) {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
var providerName, instanceName string
providerName, ok := cmd.Params["provider_name"].(string)
if !ok {
return nil, fmt.Errorf("no provider name")
}
// Check if composite_instance_name is provided in command
if compositeModelName, ok := cmd.Params["composite_instance_name"].(string); ok && compositeModelName != "" {
names := strings.Split(compositeModelName, "@")
if len(names) != 2 {
return nil, fmt.Errorf("model name must be in format 'instance@provider'")
}
providerName = names[1]
instanceName = names[0]
} else {
return nil, fmt.Errorf("no provider name or instance name")
instanceName, ok := cmd.Params["instance_name"].(string)
if !ok {
return nil, fmt.Errorf("no instance name")
}
url := fmt.Sprintf("/providers/%s/instances/%s/tasks", providerName, instanceName)

View File

@@ -142,10 +142,6 @@ func (p *Parser) parseAPIListCommands() (*Command, error) {
return p.parseAPIListSearches()
case TokenKeys:
return p.parseAPIListAPIKeys()
case TokenSupported:
return p.parseListModelsOfProvider()
case TokenModels:
return p.parseListModelsOfProvider()
case TokenProviders:
return p.parseAPIListProviders()
case TokenProvider:
@@ -156,12 +152,8 @@ func (p *Parser) parseAPIListCommands() (*Command, error) {
return p.parseAPIListDefaultModels()
case TokenAvailable:
return p.parseAPIListAvailableProviders()
case TokenFiles:
return p.parseListFiles()
case TokenQuotedString:
return p.parseListQuotedStringCommand()
case TokenAPI:
return p.parseListApiCommand()
return p.parseAPIListAPIServers()
default:
return nil, fmt.Errorf("unknown LIST target: %s", p.curToken.Value)
}
@@ -345,7 +337,7 @@ func (p *Parser) parseAPIListAPIKeys() (*Command, error) {
return cmd, nil
}
// parseAPIListProviders parses LIST PROVIDERS command
// LIST PROVIDERS
func (p *Parser) parseAPIListProviders() (*Command, error) {
p.nextToken() // consume PROVIDERS
// Semicolon is optional
@@ -369,7 +361,9 @@ func (p *Parser) parseAPIListProviderCommands() (*Command, error) {
case TokenInstances:
return p.parseListProviderInstances(providerName)
case TokenInstance:
return p.parseListProviderInstanceModels(providerName)
return p.parseListProviderInstanceCommands(providerName)
case TokenModels:
return p.parseListProviderModels(providerName)
default:
return nil, fmt.Errorf("unknown LIST target: %s", p.curToken.Value)
}
@@ -390,7 +384,7 @@ func (p *Parser) parseListProviderInstances(providerName string) (*Command, erro
// LIST PROVIDER 'provider_name' INSTANCE 'instance_name' MODELS
// LIST PROVIDER 'provider_name' INSTANCE 'instance_name' MODELS SYNC, get model list by API from remote server
func (p *Parser) parseListProviderInstanceModels(providerName string) (*Command, error) {
func (p *Parser) parseListProviderInstanceCommands(providerName string) (*Command, error) {
p.nextToken() // consume INSTANCE
instanceName, err := p.parseQuotedString()
@@ -399,6 +393,20 @@ func (p *Parser) parseListProviderInstanceModels(providerName string) (*Command,
}
p.nextToken()
switch p.curToken.Type {
case TokenModels:
return p.parseListProviderInstanceModels(providerName, instanceName)
case TokenTasks:
return p.parseListProviderInstanceTasks(providerName, instanceName)
default:
return nil, fmt.Errorf("unknown LIST target: %s", p.curToken.Value)
}
}
func (p *Parser) parseListProviderInstanceModels(providerName, instanceName string) (*Command, error) {
p.nextToken() // consume MODELS
if p.curToken.Type != TokenModels {
return nil, fmt.Errorf("expected MODELS")
}
@@ -418,6 +426,33 @@ func (p *Parser) parseListProviderInstanceModels(providerName string) (*Command,
return cmd, nil
}
func (p *Parser) parseListProviderInstanceTasks(providerName, instanceName string) (*Command, error) {
p.nextToken() // consume TASKS
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("api_list_provider_instance_tasks")
cmd.Params["provider_name"] = providerName
cmd.Params["instance_name"] = instanceName
return cmd, nil
}
func (p *Parser) parseListProviderModels(providerName string) (*Command, error) {
p.nextToken() // consume MODELS
cmd := NewCommand("api_list_provider_models")
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd.Params["provider_name"] = providerName
return cmd, nil
}
func (p *Parser) parseAPIListDefaultModels() (*Command, error) {
p.nextToken() // consume DEFAULT
if p.curToken.Type != TokenModels {
@@ -441,50 +476,6 @@ func (p *Parser) parseAPIListAvailableProviders() (*Command, error) {
return NewCommand("api_list_available_providers"), nil
}
func (p *Parser) parseListFiles() (*Command, error) {
p.nextToken() // consume FILES
if p.curToken.Type != TokenOf {
return nil, fmt.Errorf("expected OF")
}
p.nextToken()
if p.curToken.Type != TokenDataset {
return nil, fmt.Errorf("expected DATASET")
}
p.nextToken()
datasetName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd := NewCommand("list_user_dataset_files")
cmd.Params["dataset_name"] = datasetName
p.nextToken()
// Semicolon is optional for UNSET TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseListQuotedStringCommand() (*Command, error) {
str, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken() // consume str
switch p.curToken.Type {
case TokenTasks:
p.nextToken() // consume TASKS
cmd := NewCommand("list_tasks_user_command")
cmd.Params["composite_instance_name"] = str
return cmd, nil
default:
return nil, fmt.Errorf("unknown command: %s", str)
}
}
func (p *Parser) parseShowQuotedStringCommand() (*Command, error) {
str, err := p.parseQuotedString()
if err != nil {
@@ -517,27 +508,17 @@ func (p *Parser) parseShowQuotedStringCommand() (*Command, error) {
}
}
func (p *Parser) parseShowCommand() (*Command, error) {
func (p *Parser) parseAPIShowCommands() (*Command, error) {
p.nextToken() // consume SHOW
switch p.curToken.Type {
case TokenVersion:
p.nextToken()
// Semicolon is optional for SHOW TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("show_version"), nil
case TokenToken:
p.nextToken()
// Semicolon is optional for SHOW TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("show_token"), nil
return p.parseAPIShowVersion()
case TokenKey:
return p.parseAPIShowKey()
case TokenCurrent:
p.nextToken()
// Semicolon is optional for SHOW TOKEN
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
@@ -565,6 +546,26 @@ func (p *Parser) parseShowCommand() (*Command, error) {
}
}
func (p *Parser) parseAPIShowVersion() (*Command, error) {
p.nextToken() // consume VERSION
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_show_version"), nil
}
func (p *Parser) parseAPIShowKey() (*Command, error) {
p.nextToken() // consume KEY
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_show_api_key"), nil
}
func (p *Parser) parseShowVariable() (*Command, error) {
p.nextToken() // consume VAR
varName, err := p.parseIdentifier()
@@ -2487,105 +2488,6 @@ func (p *Parser) parseRetrieveCommand() (*Command, error) {
return cmd, nil
}
func (p *Parser) parseListModelsOfProvider() (*Command, error) {
if p.curToken.Type == TokenSupported {
// List supported models
p.nextToken()
cmd := NewCommand("list_supported_models")
if p.curToken.Type != TokenModels {
return nil, fmt.Errorf("expected MODELS")
}
p.nextToken()
if p.curToken.Type != TokenFrom {
return nil, fmt.Errorf("expected FROM")
}
p.nextToken()
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expected quoted string for provider name")
}
firstName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expected quoted string for instance name")
}
secondName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
cmd.Params["provider_name"] = firstName
cmd.Params["instance_name"] = secondName
// Semicolon is optional for UNSET TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
if p.curToken.Type != TokenModels {
return nil, fmt.Errorf("expected MODELS")
}
p.nextToken()
if p.curToken.Type != TokenFrom {
// LIST MODELS
cmd := NewCommand("list_all_models")
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
p.nextToken()
// Parse first quoted string (could be instance_name or provider_name)
firstName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
// Check if there's a second quoted string (provider_name)
// If so, format is: LIST MODELS FROM <instance_name> <provider_name>
// If not, format is: LIST MODELS FROM <provider_name>
if p.curToken.Type == TokenQuotedString {
var instanceName string
// Two arguments: instance_name and provider_name
instanceName, err = p.parseQuotedString()
if err != nil {
return nil, err
}
cmd := NewCommand("list_instance_models")
cmd.Params["instance_name"] = instanceName
cmd.Params["provider_name"] = firstName
p.nextToken()
// Semicolon is optional for UNSET TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// Only one argument: provider_name
cmd := NewCommand("list_provider_models")
cmd.Params["provider_name"] = firstName
// Semicolon is optional for UNSET TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseEnableCommand() (*Command, error) {
p.nextToken() // consume ENABLE
@@ -3677,7 +3579,7 @@ func (p *Parser) parseUserStatement() (*Command, error) {
case TokenDelete:
return p.parseDeleteCommand()
case TokenShow:
return p.parseShowCommand()
return p.parseAPIShowCommands()
case TokenCreate:
return p.parseCreateCommand()
case TokenDrop:
@@ -4299,17 +4201,14 @@ func (p *Parser) parseUserShowAPI() (*Command, error) {
return cmd, nil
}
func (p *Parser) parseListApiCommand() (*Command, error) {
func (p *Parser) parseAPIListAPIServers() (*Command, error) {
p.nextToken() // consume API
var cmd *Command
switch p.curToken.Type {
case TokenServer:
p.nextToken()
cmd = NewCommand("list_api_server")
p.nextToken()
cmd = NewCommand("api_list_api_servers")
default:
return nil, fmt.Errorf("expected SERVER after API")
}