Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions cmd/onecli/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,63 @@ func (cmd *HelpCmd) Run(out *output.Writer) error {
{Name: "projects delete", Description: "Delete a project.", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "ID of the project to delete."},
}},
{Name: "org secrets list", Description: "List all org-scoped secrets."},
{Name: "org secrets create", Description: "Create a new org-scoped secret.", Args: []ArgInfo{
{Name: "--name", Required: true, Description: "Display name for the secret."},
{Name: "--type", Required: true, Description: "Secret type: 'anthropic', 'openai', or 'generic'."},
{Name: "--value", Required: true, Description: "Secret value (e.g. API key)."},
{Name: "--host-pattern", Required: true, Description: "Host pattern to match."},
}},
{Name: "org secrets update", Description: "Update an org-scoped secret.", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "ID of the secret to update."},
}},
{Name: "org secrets delete", Description: "Delete an org-scoped secret.", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "ID of the secret to delete."},
}},
{Name: "org rules list", Description: "List all org-scoped policy rules."},
{Name: "org rules get", Description: "Get a single org-scoped policy rule.", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "ID of the rule to retrieve."},
}},
{Name: "org rules create", Description: "Create a new org-scoped policy rule.", Args: []ArgInfo{
{Name: "--name", Required: true, Description: "Display name for the rule."},
{Name: "--host-pattern", Required: true, Description: "Host pattern to match."},
{Name: "--action", Required: true, Description: "Action: 'block' or 'rate_limit'."},
}},
{Name: "org rules update", Description: "Update an org-scoped policy rule.", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "ID of the rule to update."},
}},
{Name: "org rules delete", Description: "Delete an org-scoped policy rule.", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "ID of the rule to delete."},
}},
{Name: "org rules permissions get", Description: "Get tool permissions for a provider.", Args: []ArgInfo{
{Name: "--provider", Required: true, Description: "Provider name (e.g. 'github', 'gmail')."},
}},
{Name: "org rules permissions set", Description: "Set tool permissions for a provider.", Args: []ArgInfo{
{Name: "--provider", Required: true, Description: "Provider name (e.g. 'github', 'gmail')."},
{Name: "--json", Required: true, Description: "JSON payload with 'changes' array."},
}},
{Name: "org connections list", Description: "List all org-scoped connections.", Args: []ArgInfo{
{Name: "--provider", Description: "Filter by provider name."},
}},
{Name: "org connections delete", Description: "Delete an org-scoped connection.", Args: []ArgInfo{
{Name: "--id", Required: true, Description: "ID of the connection to delete."},
}},
{Name: "org apps configured", Description: "List providers with org-level credentials configured."},
{Name: "org apps get", Description: "Get app config status for a provider.", Args: []ArgInfo{
{Name: "--provider", Required: true, Description: "Provider name (e.g. 'github', 'gmail')."},
}},
{Name: "org apps configure", Description: "Save BYOC credentials at the org level.", Args: []ArgInfo{
{Name: "--provider", Required: true, Description: "Provider name (e.g. 'github', 'gmail')."},
{Name: "--client-id", Required: true, Description: "OAuth client ID."},
{Name: "--client-secret", Required: true, Description: "OAuth client secret."},
}},
{Name: "org apps remove", Description: "Remove BYOC credentials at the org level.", Args: []ArgInfo{
{Name: "--provider", Required: true, Description: "Provider name (e.g. 'github', 'gmail')."},
}},
{Name: "org apps toggle", Description: "Enable or disable an app config at the org level.", Args: []ArgInfo{
{Name: "--provider", Required: true, Description: "Provider name (e.g. 'github', 'gmail')."},
{Name: "--enabled", Required: true, Description: "Set to true to enable, false to disable."},
}},
{Name: "auth login", Description: "Store API key for authentication."},
{Name: "auth logout", Description: "Remove stored API key."},
{Name: "auth status", Description: "Show authentication status."},
Expand Down
3 changes: 3 additions & 0 deletions cmd/onecli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type CLI struct {
Apps AppsCmd `cmd:"" help:"Manage app connections."`
Rules RulesCmd `cmd:"" help:"Manage policy rules."`
Projects ProjectsCmd `cmd:"" help:"Manage projects."`
Org OrgCmd `cmd:"" help:"Organization-scoped management (secrets, rules, connections, apps)."`
Auth AuthCmd `cmd:"" help:"Manage authentication."`
Config ConfigCmd `cmd:"" help:"Manage configuration settings."`
Migrate MigrateCmd `cmd:"" help:"Migrate data to OneCLI Cloud."`
Expand Down Expand Up @@ -145,6 +146,8 @@ func hintForCommand(cmd, host string) string {
return "Manage your policy rules \u2192 " + host
case "projects":
return "Manage your projects \u2192 " + host
case "org":
return "Manage organization-level resources \u2192 " + host
case "auth":
return "Manage authentication \u2192 " + host
case "config":
Expand Down
9 changes: 9 additions & 0 deletions cmd/onecli/org.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package main

// OrgCmd is the `onecli org` command group for organization-scoped operations.
type OrgCmd struct {
Secrets OrgSecretsCmd `cmd:"" help:"Manage org-scoped secrets."`
Rules OrgRulesCmd `cmd:"" help:"Manage org-scoped policy rules."`
Connections OrgConnectionsCmd `cmd:"" help:"Manage org-scoped connections."`
Apps OrgAppsCmd `cmd:"" help:"Manage org-scoped app configuration."`
}
153 changes: 153 additions & 0 deletions cmd/onecli/org_apps.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package main

import (
"encoding/json"
"fmt"

"github.com/onecli/onecli-cli/internal/api"
"github.com/onecli/onecli-cli/pkg/output"
"github.com/onecli/onecli-cli/pkg/validate"
)

// OrgAppsCmd is the `onecli org apps` command group.
type OrgAppsCmd struct {
Configured OrgAppsConfiguredCmd `cmd:"" help:"List providers with org-level credentials configured."`
Get OrgAppsGetCmd `cmd:"" help:"Get app config status for a provider."`
Configure OrgAppsConfigureCmd `cmd:"" help:"Save BYOC credentials for a provider at the org level."`
Remove OrgAppsRemoveCmd `cmd:"" help:"Remove BYOC credentials for a provider at the org level."`
Toggle OrgAppsToggleCmd `cmd:"" help:"Enable or disable an app config at the org level."`
}

// OrgAppsConfiguredCmd is `onecli org apps configured`.
type OrgAppsConfiguredCmd struct {
Fields string `optional:"" help:"Comma-separated list of fields to include in output."`
}

func (c *OrgAppsConfiguredCmd) Run(out *output.Writer) error {
client, err := newClient()
if err != nil {
return err
}
providers, err := client.ListConfiguredProviders(newContext())
if err != nil {
return err
}
return out.WriteFiltered(providers, c.Fields)
}

// OrgAppsGetCmd is `onecli org apps get`.
type OrgAppsGetCmd struct {
Provider string `required:"" help:"Provider name (e.g. 'github', 'gmail')."`
Fields string `optional:"" help:"Comma-separated list of fields to include in output."`
}

func (c *OrgAppsGetCmd) Run(out *output.Writer) error {
if err := validate.ResourceID(c.Provider); err != nil {
return fmt.Errorf("invalid provider: %w", err)
}
client, err := newClient()
if err != nil {
return err
}
config, err := client.GetOrgAppConfig(newContext(), c.Provider)
if err != nil {
return err
}
return out.WriteFiltered(config, c.Fields)
}

// OrgAppsConfigureCmd is `onecli org apps configure`.
type OrgAppsConfigureCmd struct {
Provider string `required:"" help:"Provider name (e.g. 'github', 'gmail')."`
ClientID string `required:"" name:"client-id" help:"OAuth client ID."`
ClientSecret string `required:"" name:"client-secret" help:"OAuth client secret."`
Json string `optional:"" help:"Raw JSON payload. Overrides individual flags."`
DryRun bool `optional:"" name:"dry-run" help:"Validate the request without executing it."`
}

func (c *OrgAppsConfigureCmd) Run(out *output.Writer) error {
var input api.ConfigAppInput
if c.Json != "" {
if err := json.Unmarshal([]byte(c.Json), &input); err != nil {
return fmt.Errorf("invalid JSON payload: %w", err)
}
} else {
input = api.ConfigAppInput{
ClientID: c.ClientID,
ClientSecret: c.ClientSecret,
}
}

if err := validate.ResourceID(c.Provider); err != nil {
return fmt.Errorf("invalid provider: %w", err)
}

if c.DryRun {
preview := map[string]string{
"provider": c.Provider,
"clientId": input.ClientID,
"clientSecret": "***",
}
return out.WriteDryRun("Would configure org app", preview)
}

client, err := newClient()
if err != nil {
return err
}
if err := client.UpsertOrgAppConfig(newContext(), c.Provider, input); err != nil {
return err
}
return out.Write(map[string]string{"status": "configured", "provider": c.Provider})
}

// OrgAppsRemoveCmd is `onecli org apps remove`.
type OrgAppsRemoveCmd struct {
Provider string `required:"" help:"Provider name (e.g. 'github', 'gmail')."`
DryRun bool `optional:"" name:"dry-run" help:"Validate the request without executing it."`
}

func (c *OrgAppsRemoveCmd) Run(out *output.Writer) error {
if err := validate.ResourceID(c.Provider); err != nil {
return fmt.Errorf("invalid provider: %w", err)
}
if c.DryRun {
return out.WriteDryRun("Would remove org app config", map[string]string{"provider": c.Provider})
}
client, err := newClient()
if err != nil {
return err
}
if err := client.DeleteOrgAppConfig(newContext(), c.Provider); err != nil {
return err
}
return out.Write(map[string]string{"status": "removed", "provider": c.Provider})
}

// OrgAppsToggleCmd is `onecli org apps toggle`.
type OrgAppsToggleCmd struct {
Provider string `required:"" help:"Provider name (e.g. 'github', 'gmail')."`
Enabled bool `required:"" help:"Set to true to enable, false to disable."`
DryRun bool `optional:"" name:"dry-run" help:"Validate the request without executing it."`
}

func (c *OrgAppsToggleCmd) Run(out *output.Writer) error {
if err := validate.ResourceID(c.Provider); err != nil {
return fmt.Errorf("invalid provider: %w", err)
}
if c.DryRun {
return out.WriteDryRun("Would toggle org app config", map[string]any{"provider": c.Provider, "enabled": c.Enabled})
}
client, err := newClient()
if err != nil {
return err
}
if err := client.ToggleOrgAppConfig(newContext(), c.Provider, c.Enabled); err != nil {
return err
}
status := "disabled"
if c.Enabled {
status = "enabled"
}
return out.Write(map[string]string{"status": status, "provider": c.Provider})
}
81 changes: 81 additions & 0 deletions cmd/onecli/org_connections.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package main

import (
"fmt"

"github.com/onecli/onecli-cli/pkg/output"
"github.com/onecli/onecli-cli/pkg/validate"
)

// OrgConnectionsCmd is the `onecli org connections` command group.
type OrgConnectionsCmd struct {
List OrgConnectionsListCmd `cmd:"" help:"List all org-scoped connections."`
Delete OrgConnectionsDeleteCmd `cmd:"" help:"Delete an org-scoped connection."`
}

// OrgConnectionsListCmd is `onecli org connections list`.
type OrgConnectionsListCmd struct {
Provider string `optional:"" help:"Filter by provider name (e.g. 'github', 'gmail')."`
Fields string `optional:"" help:"Comma-separated list of fields to include in output."`
Quiet string `optional:"" name:"quiet" help:"Output only the specified field, one per line."`
Max int `optional:"" default:"20" help:"Maximum number of results to return."`
}

func (c *OrgConnectionsListCmd) Run(out *output.Writer) error {
client, err := newClient()
if err != nil {
return err
}

if c.Provider != "" {
if err := validate.ResourceID(c.Provider); err != nil {
return fmt.Errorf("invalid provider: %w", err)
}
connections, err := client.ListOrgConnectionsByProvider(newContext(), c.Provider)
if err != nil {
return err
}
if c.Max > 0 && len(connections) > c.Max {
connections = connections[:c.Max]
}
if c.Quiet != "" {
return out.WriteQuiet(connections, c.Quiet)
}
return out.WriteFiltered(connections, c.Fields)
}

connections, err := client.ListOrgConnections(newContext())
if err != nil {
return err
}
if c.Max > 0 && len(connections) > c.Max {
connections = connections[:c.Max]
}
if c.Quiet != "" {
return out.WriteQuiet(connections, c.Quiet)
}
return out.WriteFiltered(connections, c.Fields)
}

// OrgConnectionsDeleteCmd is `onecli org connections delete`.
type OrgConnectionsDeleteCmd struct {
ID string `required:"" help:"ID of the connection to delete."`
DryRun bool `optional:"" name:"dry-run" help:"Validate the request without executing it."`
}

func (c *OrgConnectionsDeleteCmd) Run(out *output.Writer) error {
if err := validate.ResourceID(c.ID); err != nil {
return fmt.Errorf("invalid connection ID: %w", err)
}
if c.DryRun {
return out.WriteDryRun("Would delete org connection", map[string]string{"id": c.ID})
}
client, err := newClient()
if err != nil {
return err
}
if err := client.DeleteOrgConnection(newContext(), c.ID); err != nil {
return err
}
return out.Write(map[string]string{"status": "deleted", "id": c.ID})
}
Loading
Loading