| | @@ -129,24 +129,29 @@ |
| 129 | 129 | fmt.Fprintf(os.Stderr, "unknown subcommand: channels %s\n", args[1]) |
| 130 | 130 | os.Exit(1) |
| 131 | 131 | } |
| 132 | 132 | case "backend", "backends": |
| 133 | 133 | if len(args) < 2 { |
| 134 | | - fmt.Fprintf(os.Stderr, "usage: scuttlectl backend rename <old-name> <new-name>\n") |
| 134 | + fmt.Fprintf(os.Stderr, "usage: scuttlectl backend <list|get|delete|rename> [args]\n") |
| 135 | 135 | os.Exit(1) |
| 136 | 136 | } |
| 137 | 137 | switch args[1] { |
| 138 | + case "list": |
| 139 | + cmdBackendList(api, *jsonFlag) |
| 140 | + case "get": |
| 141 | + requireArgs(args, 3, "scuttlectl backend get <name>") |
| 142 | + cmdBackendGet(api, args[2], *jsonFlag) |
| 143 | + case "delete", "rm": |
| 144 | + requireArgs(args, 3, "scuttlectl backend delete <name>") |
| 145 | + cmdBackendDelete(api, args[2]) |
| 138 | 146 | case "rename": |
| 139 | 147 | requireArgs(args, 4, "scuttlectl backend rename <old-name> <new-name>") |
| 140 | 148 | cmdBackendRename(api, args[2], args[3]) |
| 141 | 149 | default: |
| 142 | 150 | fmt.Fprintf(os.Stderr, "unknown subcommand: backend %s\n", args[1]) |
| 143 | 151 | os.Exit(1) |
| 144 | 152 | } |
| 145 | | - case "logs": |
| 146 | | - fmt.Fprintln(os.Stderr, "logs tail: not yet implemented (requires scribe HTTP endpoint)") |
| 147 | | - os.Exit(1) |
| 148 | 153 | default: |
| 149 | 154 | fmt.Fprintf(os.Stderr, "unknown command: %s\n", args[0]) |
| 150 | 155 | usage() |
| 151 | 156 | os.Exit(1) |
| 152 | 157 | } |
| | @@ -393,10 +398,65 @@ |
| 393 | 398 | |
| 394 | 399 | func cmdChannelDelete(api *apiclient.Client, channel string) { |
| 395 | 400 | die(api.DeleteChannel(channel)) |
| 396 | 401 | fmt.Printf("Channel deleted: #%s\n", strings.TrimPrefix(channel, "#")) |
| 397 | 402 | } |
| 403 | + |
| 404 | +func cmdBackendList(api *apiclient.Client, asJSON bool) { |
| 405 | + raw, err := api.ListLLMBackends() |
| 406 | + die(err) |
| 407 | + if asJSON { |
| 408 | + printJSON(raw) |
| 409 | + return |
| 410 | + } |
| 411 | + var body struct { |
| 412 | + Backends []struct { |
| 413 | + Name string `json:"name"` |
| 414 | + Provider string `json:"provider"` |
| 415 | + } `json:"backends"` |
| 416 | + } |
| 417 | + must(json.Unmarshal(raw, &body)) |
| 418 | + if len(body.Backends) == 0 { |
| 419 | + fmt.Println("no backends") |
| 420 | + return |
| 421 | + } |
| 422 | + tw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) |
| 423 | + fmt.Fprintln(tw, "NAME\tPROVIDER") |
| 424 | + for _, b := range body.Backends { |
| 425 | + fmt.Fprintf(tw, "%s\t%s\n", b.Name, b.Provider) |
| 426 | + } |
| 427 | + tw.Flush() |
| 428 | +} |
| 429 | + |
| 430 | +func cmdBackendGet(api *apiclient.Client, name string, asJSON bool) { |
| 431 | + raw, err := api.GetLLMBackend(name) |
| 432 | + die(err) |
| 433 | + if asJSON { |
| 434 | + printJSON(raw) |
| 435 | + return |
| 436 | + } |
| 437 | + var b struct { |
| 438 | + Name string `json:"name"` |
| 439 | + Provider string `json:"provider"` |
| 440 | + Model string `json:"model"` |
| 441 | + BaseURL string `json:"base_url,omitempty"` |
| 442 | + } |
| 443 | + must(json.Unmarshal(raw, &b)) |
| 444 | + tw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) |
| 445 | + fmt.Fprintf(tw, "name\t%s\n", b.Name) |
| 446 | + fmt.Fprintf(tw, "provider\t%s\n", b.Provider) |
| 447 | + fmt.Fprintf(tw, "model\t%s\n", b.Model) |
| 448 | + if b.BaseURL != "" { |
| 449 | + fmt.Fprintf(tw, "base_url\t%s\n", b.BaseURL) |
| 450 | + } |
| 451 | + tw.Flush() |
| 452 | +} |
| 453 | + |
| 454 | +func cmdBackendDelete(api *apiclient.Client, name string) { |
| 455 | + die(api.DeleteLLMBackend(name)) |
| 456 | + fmt.Printf("Backend deleted: %s\n", name) |
| 457 | +} |
| 398 | 458 | |
| 399 | 459 | func cmdBackendRename(api *apiclient.Client, oldName, newName string) { |
| 400 | 460 | raw, err := api.GetLLMBackend(oldName) |
| 401 | 461 | die(err) |
| 402 | 462 | |
| | @@ -450,20 +510,22 @@ |
| 450 | 510 | setup [path] interactive wizard — write scuttlebot.yaml (no token needed) |
| 451 | 511 | status daemon + ergo health |
| 452 | 512 | agents list list all registered agents |
| 453 | 513 | agent get <nick> get a single agent |
| 454 | 514 | agent register <nick> register a new agent, print credentials |
| 455 | | - [--type worker|orchestrator|observer] |
| 515 | + [--type worker|orchestrator|observer|operator] |
| 456 | 516 | [--channels #a,#b,#c] |
| 457 | 517 | agent revoke <nick> revoke agent credentials |
| 458 | 518 | agent delete <nick> permanently remove agent from registry |
| 459 | 519 | agent rotate <nick> rotate agent password |
| 460 | 520 | channels list list active channels |
| 461 | 521 | channels users <channel> list users in a channel |
| 462 | 522 | channels delete <channel> part bridge from channel (closes when empty) |
| 463 | | - backend rename <old> <new> rename an LLM backend |
| 464 | | - logs tail tail scribe log (coming soon) |
| 523 | + backend list list LLM backends |
| 524 | + backend get <name> show a single backend |
| 525 | + backend delete <name> remove a backend |
| 526 | + backend rename <old> <new> rename a backend |
| 465 | 527 | admin list list admin accounts |
| 466 | 528 | admin add <username> add admin (prompts for password) |
| 467 | 529 | admin remove <username> remove admin |
| 468 | 530 | admin passwd <username> change admin password (prompts) |
| 469 | 531 | `, version) |
| 470 | 532 | |