diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 29db881..960f0d5 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -12,7 +12,7 @@ builds: env: - CGO_ENABLED=0 ldflags: - - -X 'github.com/nullify-platform/logger/pkg/logger.Version={{ .Version }}' + - -X 'github.com/nullify-platform/cli/internal/logger.Version={{ .Version }}' - -X 'github.com/nullify-platform/cli/internal/api.Version={{ .Version }}' - -X 'github.com/nullify-platform/cli/cmd/cli/cmd.buildCommit={{ .Commit }}' - -X 'github.com/nullify-platform/cli/cmd/cli/cmd.buildDate={{ .Date }}' diff --git a/Makefile b/Makefile index 450e2a8..5843884 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: build clean +.PHONY: build clean lint lint-go lint-docker # set the version as the latest commit sha if it's not already defined ifndef VERSION @@ -11,7 +11,7 @@ VERSION := $(shell git rev-list -1 HEAD)$(TAINT) endif GOENV := CGO_ENABLED=0 -GOFLAGS := -ldflags "-X 'github.com/nullify-platform/logger/pkg/logger.Version=$(VERSION)'" +GOFLAGS := -ldflags "-X 'github.com/nullify-platform/cli/internal/logger.Version=$(VERSION)'" all: build diff --git a/README.md b/README.md index f364fb7..552f868 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,6 @@ Scan, triage, fix, and track security vulnerabilities across your entire stack - **Unified findings** — Query vulnerabilities across all scanner types (SAST, SCA, secrets, DAST, CSPM) in a single command - **AI-powered remediation** — Generate fix patches and open PRs automatically -- **Interactive AI chat** — Ask security questions, triage findings, and build remediation plans with Nullify's AI agents - **CI/CD quality gates** — Block deployments when critical findings are present - **DAST/Pentest scanning** — Run API security scans in the cloud or locally via Docker - **MCP server** — 50+ tools for AI coding assistants (Claude Code, Cursor, VS Code, and more) @@ -87,8 +86,6 @@ nullify status # View findings across all scanner types nullify findings -# Chat with Nullify's AI security agents -nullify chat ``` ## Authentication @@ -130,8 +127,6 @@ nullify auth switch | `nullify init` | Interactive setup wizard (instance, auth, MCP config) | | `nullify status` | Security posture overview with finding counts by scanner | | `nullify findings` | List findings across all scanner types with filters | -| `nullify chat [message]` | Interactive AI chat or single-shot query | - ### Authentication | Command | Description | @@ -210,22 +205,6 @@ nullify pentest \ Add `--use-host-network` if the target is running directly on the host machine. -## Interactive Chat - -```sh -# Start an interactive REPL session -nullify chat - -# Single-shot query -nullify chat "what are my critical findings?" - -# Resume a previous conversation -nullify chat --chat-id abc123 "tell me more" - -# Provide additional context -nullify chat --system-prompt "focus on PCI compliance" -``` - ## CI/CD Integration ### Quality Gate diff --git a/cmd/cli/cmd/auth.go b/cmd/cli/cmd/auth.go index 6c24b63..a6fa5a8 100644 --- a/cmd/cli/cmd/auth.go +++ b/cmd/cli/cmd/auth.go @@ -11,7 +11,7 @@ import ( "github.com/nullify-platform/cli/internal/auth" "github.com/nullify-platform/cli/internal/lib" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" "github.com/spf13/cobra" ) @@ -27,7 +27,7 @@ var loginCmd = &cobra.Command{ Long: "Authenticate with your Nullify instance. Opens your browser to log in with your identity provider.", Run: func(cmd *cobra.Command, args []string) { ctx := setupLogger(cmd.Context()) - defer logger.L(ctx).Sync() + defer logger.Close(ctx) // Wrap context with signal handling so Ctrl+C triggers graceful cancellation ctx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) @@ -69,7 +69,7 @@ var logoutCmd = &cobra.Command{ Long: "Clear stored credentials for the current or specified host.", Run: func(cmd *cobra.Command, args []string) { ctx := setupLogger(cmd.Context()) - defer logger.L(ctx).Sync() + defer logger.Close(ctx) logoutHost := resolveHostForAuth(ctx) @@ -132,7 +132,7 @@ var tokenCmd = &cobra.Command{ Long: "Print the current access token. Useful for piping to other tools.", Run: func(cmd *cobra.Command, args []string) { ctx := setupLogger(cmd.Context()) - defer logger.L(ctx).Sync() + defer logger.Close(ctx) hostForToken := resolveHostForAuth(ctx) diff --git a/cmd/cli/cmd/chat.go b/cmd/cli/cmd/chat.go deleted file mode 100644 index 62b2be7..0000000 --- a/cmd/cli/cmd/chat.go +++ /dev/null @@ -1,89 +0,0 @@ -package cmd - -import ( - "errors" - "fmt" - "os" - "strings" - - "github.com/nullify-platform/cli/internal/chat" - "github.com/nullify-platform/cli/internal/lib" - "github.com/nullify-platform/logger/pkg/logger" - "github.com/spf13/cobra" -) - -var chatCmd = &cobra.Command{ - Use: "chat [message]", - Short: "Chat with Nullify's AI security agents", - Long: `Interactive chat with Nullify's AI agents for security assistance. - -Without arguments, starts an interactive REPL session. -With a message argument, sends it and streams the response (single-shot mode). - -Examples: - nullify chat # Interactive mode - nullify chat "what are my critical findings?" # Single-shot mode - nullify chat --chat-id abc123 "follow up" # Resume conversation`, - Run: func(cmd *cobra.Command, args []string) { - ctx := setupLogger(cmd.Context()) - defer logger.L(ctx).Sync() - - authCtx, err := resolveCommandAuth(ctx) - if err != nil { - if errors.Is(err, lib.ErrNoToken) { - fmt.Fprintf(os.Stderr, "Error: not authenticated. Run 'nullify auth login' first.\n") - } else { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - } - os.Exit(ExitAuthError) - } - - // Connect via WebSocket - conn, err := chat.Dial(ctx, authCtx.Host, authCtx.Token) - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(ExitNetworkError) - } - - // Build client options - var opts []chat.ClientOption - - chatID, _ := cmd.Flags().GetString("chat-id") - if chatID != "" { - opts = append(opts, chat.WithChatID(chatID)) - } - - systemPrompt, _ := cmd.Flags().GetString("system-prompt") - if systemPrompt != "" { - opts = append(opts, chat.WithSystemPrompt(systemPrompt)) - } - - client := chat.NewClient(conn, authCtx.QueryParams, opts...) - defer client.Close() - - if len(args) > 0 { - // Single-shot mode - message := strings.Join(args, " ") - if err := chat.RunSingleShot(ctx, client, message); err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } - } else { - // Interactive REPL mode - if err := chat.RunREPL(ctx, client); err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } - } - }, -} - -func init() { - // Note: The generated "chat" command from commands package is registered on rootCmd. - // This handwritten chat command uses a different name to avoid conflicts. - // We add it directly since the generated chat commands handle different API endpoints. - rootCmd.AddCommand(chatCmd) - - chatCmd.Flags().String("system-prompt", "", "Extra system prompt context for the AI agent") - chatCmd.Flags().String("chat-id", "", "Resume an existing chat conversation by ID") -} diff --git a/cmd/cli/cmd/ci.go b/cmd/cli/cmd/ci.go index 1bf2a29..84e2ab6 100644 --- a/cmd/cli/cmd/ci.go +++ b/cmd/cli/cmd/ci.go @@ -11,7 +11,7 @@ import ( "github.com/nullify-platform/cli/internal/auth" "github.com/nullify-platform/cli/internal/client" "github.com/nullify-platform/cli/internal/lib" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" ) @@ -43,7 +43,7 @@ Exit codes: nullify ci gate --repo my-org/my-repo`, Run: func(cmd *cobra.Command, args []string) { ctx := setupLogger(cmd.Context()) - defer logger.L(ctx).Sync() + defer logger.Close(ctx) ciHost := resolveHost(ctx) token, err := lib.GetNullifyToken(ctx, ciHost, nullifyToken, githubToken) @@ -159,7 +159,7 @@ var ciReportCmd = &cobra.Command{ nullify ci report --repo my-org/my-repo`, Run: func(cmd *cobra.Command, args []string) { ctx := setupLogger(cmd.Context()) - defer logger.L(ctx).Sync() + defer logger.Close(ctx) ciHost := resolveHost(ctx) token, err := lib.GetNullifyToken(ctx, ciHost, nullifyToken, githubToken) diff --git a/cmd/cli/cmd/findings.go b/cmd/cli/cmd/findings.go index 77e0a63..628fcac 100644 --- a/cmd/cli/cmd/findings.go +++ b/cmd/cli/cmd/findings.go @@ -8,8 +8,8 @@ import ( "github.com/nullify-platform/cli/internal/auth" "github.com/nullify-platform/cli/internal/client" "github.com/nullify-platform/cli/internal/lib" + "github.com/nullify-platform/cli/internal/logger" "github.com/nullify-platform/cli/internal/output" - "github.com/nullify-platform/logger/pkg/logger" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" ) @@ -30,7 +30,7 @@ Auto-detects the current repository from git if --repo is not specified.`, nullify findings -o table --repo my-org/my-repo`, Run: func(cmd *cobra.Command, args []string) { ctx := setupLogger(cmd.Context()) - defer logger.L(ctx).Sync() + defer logger.Close(ctx) findingsHost := resolveHost(ctx) token, err := lib.GetNullifyToken(ctx, findingsHost, nullifyToken, githubToken) diff --git a/cmd/cli/cmd/fix.go b/cmd/cli/cmd/fix.go index 484952d..d60ef8b 100644 --- a/cmd/cli/cmd/fix.go +++ b/cmd/cli/cmd/fix.go @@ -9,8 +9,8 @@ import ( "github.com/nullify-platform/cli/internal/auth" "github.com/nullify-platform/cli/internal/client" "github.com/nullify-platform/cli/internal/lib" + "github.com/nullify-platform/cli/internal/logger" "github.com/nullify-platform/cli/internal/output" - "github.com/nullify-platform/logger/pkg/logger" "github.com/spf13/cobra" ) @@ -31,7 +31,7 @@ Supports SAST and SCA dependency findings.`, Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { ctx := setupLogger(cmd.Context()) - defer logger.L(ctx).Sync() + defer logger.Close(ctx) findingID := args[0] diff --git a/cmd/cli/cmd/init.go b/cmd/cli/cmd/init.go index 70fe452..d908c8f 100644 --- a/cmd/cli/cmd/init.go +++ b/cmd/cli/cmd/init.go @@ -4,8 +4,8 @@ import ( "fmt" "os" + "github.com/nullify-platform/cli/internal/logger" "github.com/nullify-platform/cli/internal/wizard" - "github.com/nullify-platform/logger/pkg/logger" "github.com/spf13/cobra" ) @@ -15,7 +15,7 @@ var initCmd = &cobra.Command{ Long: "Interactive setup wizard that configures your Nullify domain, authentication, repository detection, and MCP integration.", Run: func(cmd *cobra.Command, args []string) { ctx := setupLogger(cmd.Context()) - defer logger.L(ctx).Sync() + defer logger.Close(ctx) fmt.Println("Welcome to Nullify CLI setup!") fmt.Println("This wizard will help you get started.") diff --git a/cmd/cli/cmd/mcp.go b/cmd/cli/cmd/mcp.go index efbf668..f9cc7ac 100644 --- a/cmd/cli/cmd/mcp.go +++ b/cmd/cli/cmd/mcp.go @@ -9,8 +9,8 @@ import ( "github.com/nullify-platform/cli/internal/client" "github.com/nullify-platform/cli/internal/lib" + "github.com/nullify-platform/cli/internal/logger" "github.com/nullify-platform/cli/internal/mcp" - "github.com/nullify-platform/logger/pkg/logger" "github.com/spf13/cobra" ) @@ -26,7 +26,7 @@ var mcpServeCmd = &cobra.Command{ Long: "Start the Nullify MCP server over stdio. Configure your AI tool to run 'nullify mcp serve'.", Run: func(cmd *cobra.Command, args []string) { ctx := setupLogger(cmd.Context()) - defer logger.L(ctx).Sync() + defer logger.Close(ctx) authCtx, err := resolveCommandAuth(ctx) if err != nil { diff --git a/cmd/cli/cmd/open.go b/cmd/cli/cmd/open.go index 388c1ec..e3db4ae 100644 --- a/cmd/cli/cmd/open.go +++ b/cmd/cli/cmd/open.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/nullify-platform/cli/internal/auth" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" "github.com/spf13/cobra" ) @@ -15,7 +15,7 @@ var openCmd = &cobra.Command{ Short: "Open the Nullify dashboard in your browser", Run: func(cmd *cobra.Command, args []string) { ctx := setupLogger(cmd.Context()) - defer logger.L(ctx).Sync() + defer logger.Close(ctx) openHost := resolveHost(ctx) // Strip "api." prefix to get the dashboard URL diff --git a/cmd/cli/cmd/pentest.go b/cmd/cli/cmd/pentest.go index e153359..cf30865 100644 --- a/cmd/cli/cmd/pentest.go +++ b/cmd/cli/cmd/pentest.go @@ -3,8 +3,8 @@ package cmd import ( "os" + "github.com/nullify-platform/cli/internal/logger" "github.com/nullify-platform/cli/internal/pentest" - "github.com/nullify-platform/logger/pkg/logger" "github.com/spf13/cobra" ) @@ -14,7 +14,7 @@ var pentestCmd = &cobra.Command{ Long: "Run pentest scans against your API endpoints. Supports local Docker-based scanning and cloud-based scanning.", Run: func(cmd *cobra.Command, args []string) { ctx := setupLogger(cmd.Context()) - defer logger.L(ctx).Sync() + defer logger.Close(ctx) pentestArgs := getPentestArgs(cmd) nullifyClient := getNullifyClient(ctx) diff --git a/cmd/cli/cmd/repos.go b/cmd/cli/cmd/repos.go index 4419154..551ee08 100644 --- a/cmd/cli/cmd/repos.go +++ b/cmd/cli/cmd/repos.go @@ -5,8 +5,8 @@ import ( "net/url" "os" + "github.com/nullify-platform/cli/internal/logger" "github.com/nullify-platform/cli/internal/output" - "github.com/nullify-platform/logger/pkg/logger" "github.com/spf13/cobra" ) @@ -16,7 +16,7 @@ var reposCmd = &cobra.Command{ Example: " nullify repos\n nullify repos -o table", Run: func(cmd *cobra.Command, args []string) { ctx := setupLogger(cmd.Context()) - defer logger.L(ctx).Sync() + defer logger.Close(ctx) apiClient := getAPIClient() diff --git a/cmd/cli/cmd/root.go b/cmd/cli/cmd/root.go index 238dc6e..f174eba 100644 --- a/cmd/cli/cmd/root.go +++ b/cmd/cli/cmd/root.go @@ -11,7 +11,7 @@ import ( "github.com/nullify-platform/cli/internal/client" "github.com/nullify-platform/cli/internal/commands" "github.com/nullify-platform/cli/internal/lib" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" "github.com/spf13/cobra" ) diff --git a/cmd/cli/cmd/status.go b/cmd/cli/cmd/status.go index eebcbe7..670253f 100644 --- a/cmd/cli/cmd/status.go +++ b/cmd/cli/cmd/status.go @@ -12,8 +12,8 @@ import ( "github.com/nullify-platform/cli/internal/auth" "github.com/nullify-platform/cli/internal/client" "github.com/nullify-platform/cli/internal/lib" + "github.com/nullify-platform/cli/internal/logger" "github.com/nullify-platform/cli/internal/output" - "github.com/nullify-platform/logger/pkg/logger" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" ) @@ -26,7 +26,7 @@ var securityStatusCmd = &cobra.Command{ nullify status -o table`, Run: func(cmd *cobra.Command, args []string) { ctx := setupLogger(cmd.Context()) - defer logger.L(ctx).Sync() + defer logger.Close(ctx) statusHost := resolveHost(ctx) token, err := lib.GetNullifyToken(ctx, statusHost, nullifyToken, githubToken) diff --git a/cmd/cli/cmd/update.go b/cmd/cli/cmd/update.go index f1c3e38..33243f8 100644 --- a/cmd/cli/cmd/update.go +++ b/cmd/cli/cmd/update.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/google/go-github/v84/github" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" "github.com/spf13/cobra" ) diff --git a/cmd/cli/cmd/version.go b/cmd/cli/cmd/version.go index 944bc00..2860312 100644 --- a/cmd/cli/cmd/version.go +++ b/cmd/cli/cmd/version.go @@ -4,7 +4,7 @@ import ( "fmt" "runtime" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" "github.com/spf13/cobra" ) diff --git a/cmd/cli/cmd/whoami.go b/cmd/cli/cmd/whoami.go index 4bd2019..cbde7c9 100644 --- a/cmd/cli/cmd/whoami.go +++ b/cmd/cli/cmd/whoami.go @@ -6,8 +6,8 @@ import ( "os" "github.com/nullify-platform/cli/internal/auth" + "github.com/nullify-platform/cli/internal/logger" "github.com/nullify-platform/cli/internal/output" - "github.com/nullify-platform/logger/pkg/logger" "github.com/spf13/cobra" ) @@ -16,7 +16,7 @@ var whoamiCmd = &cobra.Command{ Short: "Show current authentication status", Run: func(cmd *cobra.Command, args []string) { ctx := setupLogger(cmd.Context()) - defer logger.L(ctx).Sync() + defer logger.Close(ctx) whoamiHost := resolveHost(ctx) diff --git a/go.mod b/go.mod index bfef730..f9054b8 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,7 @@ go 1.26.0 require ( github.com/docker/docker v28.5.2+incompatible github.com/google/go-github/v84 v84.0.0 - github.com/gorilla/websocket v1.5.3 github.com/mark3labs/mcp-go v0.44.1 - github.com/nullify-platform/logger v1.31.0 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 golang.org/x/sync v0.19.0 @@ -15,27 +13,8 @@ require ( require ( github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/aws/aws-lambda-go v1.52.0 // indirect - github.com/aws/aws-sdk-go-v2 v1.41.1 // indirect - github.com/aws/aws-sdk-go-v2/config v1.32.7 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.19.7 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sns v1.39.11 // indirect - github.com/aws/aws-sdk-go-v2/service/sqs v1.42.21 // indirect - github.com/aws/aws-sdk-go-v2/service/ssm v1.67.8 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 // indirect - github.com/aws/smithy-go v1.24.0 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect - github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect @@ -67,23 +46,13 @@ require ( go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect go.opentelemetry.io/otel v1.40.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.40.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.40.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0 // indirect go.opentelemetry.io/otel/metric v1.40.0 // indirect - go.opentelemetry.io/otel/sdk v1.40.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect go.opentelemetry.io/otel/trace v1.40.0 // indirect - go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/net v0.49.0 // indirect golang.org/x/sys v0.40.0 // indirect - golang.org/x/text v0.33.0 // indirect golang.org/x/time v0.9.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect google.golang.org/grpc v1.78.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gotest.tools/v3 v3.5.1 // indirect @@ -91,6 +60,6 @@ require ( require ( github.com/stretchr/testify v1.11.1 - go.uber.org/zap v1.27.1 // indirect + go.uber.org/zap v1.27.1 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 417ffed..2c99398 100644 --- a/go.sum +++ b/go.sum @@ -2,42 +2,6 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/aws/aws-lambda-go v1.52.0 h1:5NfiRaVl9FafUIt2Ld/Bv22kT371mfAI+l1Hd+tV7ZE= -github.com/aws/aws-lambda-go v1.52.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A= -github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU= -github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= -github.com/aws/aws-sdk-go-v2/config v1.32.7 h1:vxUyWGUwmkQ2g19n7JY/9YL8MfAIl7bTesIUykECXmY= -github.com/aws/aws-sdk-go-v2/config v1.32.7/go.mod h1:2/Qm5vKUU/r7Y+zUk/Ptt2MDAEKAfUtKc1+3U1Mo3oY= -github.com/aws/aws-sdk-go-v2/credentials v1.19.7 h1:tHK47VqqtJxOymRrNtUXN5SP/zUTvZKeLx4tH6PGQc8= -github.com/aws/aws-sdk-go-v2/credentials v1.19.7/go.mod h1:qOZk8sPDrxhf+4Wf4oT2urYJrYt3RejHSzgAquYeppw= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMoozM8oXlgLG/n6WLaFGoea7/CddrCfIiSA+xdY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M= -github.com/aws/aws-sdk-go-v2/service/sns v1.39.11 h1:Ke7RS0NuP9Xwk31prXYcFGA1Qfn8QmNWcxyjKPcXZdc= -github.com/aws/aws-sdk-go-v2/service/sns v1.39.11/go.mod h1:hdZDKzao0PBfJJygT7T92x2uVcWc/htqlhrjFIjnHDM= -github.com/aws/aws-sdk-go-v2/service/sqs v1.42.21 h1:Oa0IhwDLVrcBHDlNo1aosG4CxO4HyvzDV5xUWqWcBc0= -github.com/aws/aws-sdk-go-v2/service/sqs v1.42.21/go.mod h1:t98Ssq+qtXKXl2SFtaSkuT6X42FSM//fnO6sfq5RqGM= -github.com/aws/aws-sdk-go-v2/service/ssm v1.67.8 h1:31Llf5VfrZ78YvYs7sWcS7L2m3waikzRc6q1nYenVS4= -github.com/aws/aws-sdk-go-v2/service/ssm v1.67.8/go.mod h1:/jgaDlU1UImoxTxhRNxXHvBAPqPZQ8oCjcPbbkR6kac= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 h1:v6EiMvhEYBoHABfbGB4alOYmCIrcgyPPiBE1wZAEbqk= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.9/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 h1:gd84Omyu9JLriJVCbGApcLzVR3XtmC4ZDPcAI6Ftvds= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/pJ1jOWYlFDJTjRQ= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ= -github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= -github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= @@ -72,8 +36,6 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= @@ -83,8 +45,6 @@ github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfh github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -110,8 +70,6 @@ github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/nullify-platform/logger v1.31.0 h1:5cKnFAPFBL4cf3CD5wNbyiJU1Tx5Kz6YImq16q28COo= -github.com/nullify-platform/logger v1.31.0/go.mod h1:vfmESJOksElkzkliDII+yt0ZwnKa5dno07KQvfp4yd8= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -144,16 +102,10 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.40.0 h1:9y5sHvAxWzft1WQ4BwqcvA+IFVUJ1Ya75mSAUnFEVwE= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.40.0/go.mod h1:eQqT90eR3X5Dbs1g9YSM30RavwLF725Ris5/XSXWvqE= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 h1:Ckwye2FpXkYgiHX7fyVrN1uA/UYd9ounqqTuSNAv0k4= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0/go.mod h1:teIFJh5pW2y+AN7riv6IBPX2DuesS3HgP39mwOspKwU= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.40.0 h1:ZrPRak/kS4xI3AVXy8F7pipuDXmDsrO8Lg+yQjBLjw0= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.40.0/go.mod h1:3y6kQCWztq6hyW8Z9YxQDDm0Je9AJoFar2G0yDcmhRk= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0 h1:8UPA4IbVZxpsD76ihGOQiFml99GPAEZLohDXvqHdi6U= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0/go.mod h1:MZ1T/+51uIVKlRzGw1Fo46KEWThjlCBZKl2LzY5nv4g= go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= @@ -181,8 +133,6 @@ golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= diff --git a/internal/auth/login.go b/internal/auth/login.go index 84fa656..d722ce4 100644 --- a/internal/auth/login.go +++ b/internal/auth/login.go @@ -13,8 +13,7 @@ import ( "sync" "time" - "github.com/nullify-platform/logger/pkg/logger" - "github.com/nullify-platform/logger/pkg/logger/tracer" + "github.com/nullify-platform/cli/internal/logger" ) var httpClient = &http.Client{Timeout: 30 * time.Second} @@ -48,9 +47,6 @@ p{color:#666;margin:0} ` func Login(ctx context.Context, host string) error { - ctx, span := tracer.FromContext(ctx).Start(ctx, "auth.Login") - defer span.End() - // 1. Start localhost server on random port listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { @@ -175,9 +171,6 @@ func Logout(host string) error { } func GetValidToken(ctx context.Context, host string) (string, error) { - ctx, span := tracer.FromContext(ctx).Start(ctx, "auth.GetValidToken") - defer span.End() - creds, err := LoadCredentials() if err != nil { return "", fmt.Errorf("not authenticated - run 'nullify auth login'") @@ -206,9 +199,6 @@ func GetValidToken(ctx context.Context, host string) (string, error) { } func createCLISession(ctx context.Context, host string, port int) (*cliSessionResponse, error) { - ctx, span := tracer.FromContext(ctx).Start(ctx, "auth.createCLISession") - defer span.End() - url := fmt.Sprintf("https://%s/auth/cli/session", apiHost(host)) bodyData, err := json.Marshal(map[string]int{"port": port}) @@ -243,9 +233,6 @@ func createCLISession(ctx context.Context, host string, port int) (*cliSessionRe } func fetchCLIToken(ctx context.Context, host string, sessionID string) (*cliTokenResponse, error) { - ctx, span := tracer.FromContext(ctx).Start(ctx, "auth.fetchCLIToken") - defer span.End() - url := fmt.Sprintf("https://%s/auth/cli/token", apiHost(host)) bodyData, err := json.Marshal(map[string]string{"session_id": sessionID}) @@ -280,9 +267,6 @@ func fetchCLIToken(ctx context.Context, host string, sessionID string) (*cliToke } func refreshToken(ctx context.Context, host string, refreshTok string) (string, error) { - ctx, span := tracer.FromContext(ctx).Start(ctx, "auth.refreshToken") - defer span.End() - refreshURL := fmt.Sprintf("https://%s/auth/refresh_token", apiHost(host)) req, err := http.NewRequestWithContext(ctx, http.MethodGet, refreshURL, nil) diff --git a/internal/chat/client.go b/internal/chat/client.go deleted file mode 100644 index bb2d01d..0000000 --- a/internal/chat/client.go +++ /dev/null @@ -1,119 +0,0 @@ -package chat - -import ( - "crypto/rand" - "fmt" - "math/big" - "sync" - "time" -) - -// Conn is the interface for a WebSocket connection, allowing testability. -type Conn interface { - WriteJSON(v any) error - ReadJSON(v any) error - Close() error -} - -// Client manages a chat session with the Nullify AI agents. -type Client struct { - conn Conn - chatID string - queryParams map[string]string - systemPrompt string - mu sync.Mutex -} - -// NewClient creates a new chat client with the given connection. -func NewClient(conn Conn, queryParams map[string]string, opts ...ClientOption) *Client { - c := &Client{ - conn: conn, - chatID: generateChatID(), - queryParams: queryParams, - } - - for _, opt := range opts { - opt(c) - } - - return c -} - -// ClientOption configures a chat client. -type ClientOption func(*Client) - -// WithChatID sets a specific chat ID (for resuming conversations). -func WithChatID(id string) ClientOption { - return func(c *Client) { - c.chatID = id - } -} - -// WithSystemPrompt sets an extra system prompt. -func WithSystemPrompt(prompt string) ClientOption { - return func(c *Client) { - c.systemPrompt = prompt - } -} - -// Send sends a message to the chat server. -func (c *Client) Send(message string) error { - c.mu.Lock() - defer c.mu.Unlock() - - payload := Payload{ - OwnerProvider: c.queryParams, - ChatID: c.chatID, - Message: message, - ExtraSystemPrompt: c.systemPrompt, - } - - return c.conn.WriteJSON(payload) -} - -// ReadResponses returns a channel that streams responses from the server. -// The channel is closed when a terminal status is received or an error occurs. -func (c *Client) ReadResponses() <-chan MessageResponse { - ch := make(chan MessageResponse, 16) - - go func() { - defer close(ch) - - for { - var resp MessageResponse - if err := c.conn.ReadJSON(&resp); err != nil { - ch <- MessageResponse{ - Status: StatusErrored, - MessageType: MessageTypeError, - Message: fmt.Sprintf("connection error: %v", err), - } - return - } - - ch <- resp - - if resp.IsTerminal() { - return - } - } - }() - - return ch -} - -// ChatID returns the current chat ID. -func (c *Client) ChatID() string { - return c.chatID -} - -// Close closes the underlying connection. -func (c *Client) Close() error { - return c.conn.Close() -} - -// generateChatID creates a simple unique ID for a chat session. -func generateChatID() string { - now := time.Now().UnixMilli() - n, _ := rand.Int(rand.Reader, big.NewInt(1<<32)) - return fmt.Sprintf("%d-%s", now, n.Text(36)) -} diff --git a/internal/chat/client_test.go b/internal/chat/client_test.go deleted file mode 100644 index aeaf55a..0000000 --- a/internal/chat/client_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package chat - -import ( - "encoding/json" - "errors" - "testing" - - "github.com/stretchr/testify/require" -) - -// mockConn is a test implementation of the Conn interface. -type mockConn struct { - written []any - toRead []MessageResponse - readIdx int - closed bool -} - -func (m *mockConn) WriteJSON(v any) error { - m.written = append(m.written, v) - return nil -} - -func (m *mockConn) ReadJSON(v any) error { - if m.readIdx >= len(m.toRead) { - return errors.New("connection closed") - } - resp := m.toRead[m.readIdx] - m.readIdx++ - - // Marshal and unmarshal to simulate real JSON round-trip - data, _ := json.Marshal(resp) - return json.Unmarshal(data, v) -} - -func (m *mockConn) Close() error { - m.closed = true - return nil -} - -func TestClientSend(t *testing.T) { - conn := &mockConn{} - c := NewClient(conn, map[string]string{"orgId": "test-org"}) - - err := c.Send("hello") - require.NoError(t, err) - require.Len(t, conn.written, 1) - - payload := conn.written[0].(Payload) - require.Equal(t, "hello", payload.Message) - require.Equal(t, c.ChatID(), payload.ChatID) - require.Equal(t, "test-org", payload.OwnerProvider["orgId"]) -} - -func TestClientReadResponses(t *testing.T) { - conn := &mockConn{ - toRead: []MessageResponse{ - {Status: StatusInProgress, MessageType: MessageTypeStatusMessage, Message: "thinking..."}, - {Status: StatusInProgress, MessageType: MessageTypeResponse, Message: "hello back"}, - {Status: StatusFinished, MessageType: MessageTypeResponse, Message: "done"}, - }, - } - - c := NewClient(conn, nil) - responses := c.ReadResponses() - - var received []MessageResponse - for resp := range responses { - received = append(received, resp) - } - - require.Len(t, received, 3) - require.Equal(t, "thinking...", received[0].Message) - require.Equal(t, "hello back", received[1].Message) - require.Equal(t, "done", received[2].Message) -} - -func TestClientReadResponsesConnectionError(t *testing.T) { - // When there are no messages to read, ReadJSON returns an error, - // and ReadResponses should emit an error response and close the channel. - conn := &mockConn{toRead: nil} - c := NewClient(conn, nil) - responses := c.ReadResponses() - - var received []MessageResponse - for resp := range responses { - received = append(received, resp) - } - - require.Len(t, received, 1) - require.Equal(t, StatusErrored, received[0].Status) - require.Contains(t, received[0].Message, "connection") -} - -func TestClientWithOptions(t *testing.T) { - conn := &mockConn{} - c := NewClient(conn, nil, - WithChatID("custom-id"), - WithSystemPrompt("be helpful"), - ) - - require.Equal(t, "custom-id", c.ChatID()) - - err := c.Send("test") - require.NoError(t, err) - - payload := conn.written[0].(Payload) - require.Equal(t, "custom-id", payload.ChatID) - require.Equal(t, "be helpful", payload.ExtraSystemPrompt) -} - -func TestMessageResponseIsTerminal(t *testing.T) { - require.True(t, (&MessageResponse{Status: StatusFinished}).IsTerminal()) - require.True(t, (&MessageResponse{Status: StatusErrored}).IsTerminal()) - require.True(t, (&MessageResponse{Status: StatusSurrendered}).IsTerminal()) - require.False(t, (&MessageResponse{Status: StatusInProgress}).IsTerminal()) -} diff --git a/internal/chat/dialer.go b/internal/chat/dialer.go deleted file mode 100644 index 79de8c5..0000000 --- a/internal/chat/dialer.go +++ /dev/null @@ -1,90 +0,0 @@ -package chat - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "strings" - - "github.com/gorilla/websocket" -) - -// Dial connects to the Nullify chat WebSocket. -func Dial(ctx context.Context, host string, token string) (Conn, error) { - url := buildWebSocketURL(host) - - header := http.Header{} - header.Set("Authorization", "Bearer "+token) - - dialer := websocket.DefaultDialer - conn, resp, err := dialer.DialContext(ctx, url, header) - if err != nil { - return nil, formatDialError(url, err, resp) - } - - return conn, nil -} - -func buildWebSocketURL(host string) string { - return fmt.Sprintf("wss://%s/chat/websocket", websocketHost(host)) -} - -func websocketHost(host string) string { - if strings.HasPrefix(host, "api.") { - return host - } - return "api." + host -} - -func formatDialError(url string, err error, resp *http.Response) error { - if resp == nil { - return fmt.Errorf("failed to connect to chat at %s: %w", url, err) - } - - defer resp.Body.Close() - - message := summarizeHandshakeBody(resp.Body) - base := fmt.Sprintf("failed to connect to chat at %s: websocket handshake failed with HTTP %d %s", url, resp.StatusCode, http.StatusText(resp.StatusCode)) - if message != "" { - base += ": " + message - } - - switch resp.StatusCode { - case http.StatusUnauthorized: - base += ". Check that your Nullify token is valid for this host." - case http.StatusForbidden: - base += ". The chat websocket is reachable, but this identity is not allowed to use it. Verify chat permissions for this host." - } - - return errors.New(base) -} - -func summarizeHandshakeBody(body io.Reader) string { - if body == nil { - return "" - } - - data, err := io.ReadAll(io.LimitReader(body, 4096)) - if err != nil { - return "" - } - - raw := strings.TrimSpace(string(data)) - if raw == "" { - return "" - } - - var parsed map[string]any - if json.Unmarshal(data, &parsed) == nil { - for _, key := range []string{"message", "error", "detail"} { - if value, ok := parsed[key].(string); ok && strings.TrimSpace(value) != "" { - return strings.TrimSpace(value) - } - } - } - - return raw -} diff --git a/internal/chat/dialer_test.go b/internal/chat/dialer_test.go deleted file mode 100644 index 2feda47..0000000 --- a/internal/chat/dialer_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package chat - -import ( - "errors" - "io" - "net/http" - "strings" - "testing" - - "github.com/gorilla/websocket" - "github.com/stretchr/testify/require" -) - -func TestBuildWebSocketURL(t *testing.T) { - require.Equal(t, "wss://api.acme.nullify.ai/chat/websocket", buildWebSocketURL("acme.nullify.ai")) - require.Equal(t, "wss://api.acme.nullify.ai/chat/websocket", buildWebSocketURL("api.acme.nullify.ai")) -} - -func TestFormatDialErrorWithForbiddenHandshake(t *testing.T) { - err := formatDialError( - "wss://api.acme.nullify.ai/chat/websocket", - websocket.ErrBadHandshake, - &http.Response{ - StatusCode: http.StatusForbidden, - Body: io.NopCloser(strings.NewReader(`{"message":"User is not authorized"}`)), - }, - ) - - require.EqualError(t, err, "failed to connect to chat at wss://api.acme.nullify.ai/chat/websocket: websocket handshake failed with HTTP 403 Forbidden: User is not authorized. The chat websocket is reachable, but this identity is not allowed to use it. Verify chat permissions for this host.") -} - -func TestFormatDialErrorWithoutResponse(t *testing.T) { - err := formatDialError("wss://api.acme.nullify.ai/chat/websocket", errors.New("tls: internal error"), nil) - require.EqualError(t, err, "failed to connect to chat at wss://api.acme.nullify.ai/chat/websocket: tls: internal error") -} - -func TestSummarizeHandshakeBodyReturnsRawBody(t *testing.T) { - message := summarizeHandshakeBody(strings.NewReader("plain text failure")) - require.Equal(t, "plain text failure", message) -} diff --git a/internal/chat/renderer.go b/internal/chat/renderer.go deleted file mode 100644 index ef7ad3b..0000000 --- a/internal/chat/renderer.go +++ /dev/null @@ -1,73 +0,0 @@ -package chat - -import ( - "fmt" - "os" -) - -const ( - ansiReset = "\033[0m" - ansiBold = "\033[1m" - ansiDim = "\033[2m" - ansiItalic = "\033[3m" - ansiRed = "\033[31m" - ansiYellow = "\033[33m" - ansiCyan = "\033[36m" -) - -// isTTY reports whether stdout is a terminal. -func isTTY() bool { - fi, err := os.Stdout.Stat() - if err != nil { - return false - } - return fi.Mode()&os.ModeCharDevice != 0 -} - -// ansi returns the escape code if stdout is a terminal, empty string otherwise. -func ansi(code string) string { - if colorsEnabled(isTTY()) { - return code - } - return "" -} - -func colorsEnabled(stdoutIsTTY bool) bool { - return stdoutIsTTY && os.Getenv("NO_COLOR") == "" -} - -// RenderToolCall renders a tool call message (dim text). -func RenderToolCall(message string) string { - return fmt.Sprintf("%s%s[tool] %s%s", ansi(ansiDim), ansi(ansiCyan), message, ansi(ansiReset)) -} - -// RenderStatus renders a status message (italic text). -func RenderStatus(message string) string { - return fmt.Sprintf("%s%s%s%s", ansi(ansiItalic), ansi(ansiYellow), message, ansi(ansiReset)) -} - -// RenderResponse renders a response message (normal text). -func RenderResponse(message string) string { - return message -} - -// RenderError renders an error message (red text). -func RenderError(message string) string { - return fmt.Sprintf("%s%s%s%s", ansi(ansiBold), ansi(ansiRed), message, ansi(ansiReset)) -} - -// RenderMessage renders a MessageResponse based on its type. -func RenderMessage(msg MessageResponse) string { - switch msg.MessageType { - case MessageTypeToolCall: - return RenderToolCall(msg.Message) - case MessageTypeStatusMessage: - return RenderStatus(msg.Message) - case MessageTypeResponse: - return RenderResponse(msg.Message) - case MessageTypeError: - return RenderError(msg.Message) - default: - return msg.Message - } -} diff --git a/internal/chat/renderer_test.go b/internal/chat/renderer_test.go deleted file mode 100644 index 45237fc..0000000 --- a/internal/chat/renderer_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package chat - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestColorsEnabledHonorsNoColor(t *testing.T) { - t.Setenv("NO_COLOR", "1") - require.False(t, colorsEnabled(true)) - - t.Setenv("NO_COLOR", "") - require.True(t, colorsEnabled(true)) - require.False(t, colorsEnabled(false)) -} - -func TestRenderToolCall(t *testing.T) { - result := RenderToolCall("calling search_findings") - require.Contains(t, result, "[tool]") - require.Contains(t, result, "calling search_findings") -} - -func TestRenderStatus(t *testing.T) { - result := RenderStatus("analyzing findings...") - require.Contains(t, result, "analyzing findings...") -} - -func TestRenderResponse(t *testing.T) { - result := RenderResponse("Here are your findings") - require.Equal(t, "Here are your findings", result) -} - -func TestRenderError(t *testing.T) { - result := RenderError("connection failed") - require.Contains(t, result, "connection failed") -} - -func TestRenderMessage(t *testing.T) { - tests := []struct { - name string - msg MessageResponse - contains string - }{ - {"tool call", MessageResponse{MessageType: MessageTypeToolCall, Message: "tool"}, "[tool]"}, - {"status", MessageResponse{MessageType: MessageTypeStatusMessage, Message: "status"}, "status"}, - {"response", MessageResponse{MessageType: MessageTypeResponse, Message: "response"}, "response"}, - {"error", MessageResponse{MessageType: MessageTypeError, Message: "error"}, "error"}, - {"unknown type", MessageResponse{MessageType: "other", Message: "fallback"}, "fallback"}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := RenderMessage(tt.msg) - require.Contains(t, result, tt.contains) - }) - } -} diff --git a/internal/chat/repl.go b/internal/chat/repl.go deleted file mode 100644 index 8b59c78..0000000 --- a/internal/chat/repl.go +++ /dev/null @@ -1,105 +0,0 @@ -package chat - -import ( - "bufio" - "context" - "fmt" - "io" - "os" - "os/signal" - "strings" -) - -// RunREPL starts an interactive chat REPL. -func RunREPL(ctx context.Context, client *Client) error { - reader := bufio.NewReader(os.Stdin) - - fmt.Printf("%sNullify Chat%s (chat ID: %s)\n", ansiBold, ansiReset, client.ChatID()) - fmt.Println("Type your message and press Enter. Press Ctrl+D to exit.") - fmt.Println() - - // Handle Ctrl+C gracefully (cancel current request, not exit) - sigCh := make(chan os.Signal, 1) - signal.Notify(sigCh, os.Interrupt) - defer signal.Stop(sigCh) - - for { - fmt.Print(ansiBold + "you> " + ansiReset) - - line, err := reader.ReadString('\n') - if err == io.EOF { - fmt.Println("\nGoodbye!") - return nil - } - if err != nil { - return fmt.Errorf("failed to read input: %w", err) - } - - message := strings.TrimSpace(line) - if message == "" { - continue - } - - if err := streamResponse(ctx, client, message, sigCh); err != nil { - fmt.Println(RenderError(err.Error())) - } - - fmt.Println() - } -} - -// RunSingleShot sends a single message and streams the response. -func RunSingleShot(ctx context.Context, client *Client, message string) error { - return streamResponse(ctx, client, message, nil) -} - -// streamResponse sends a message and reads responses until a terminal status. -// sigCh may be nil (single-shot mode), in which case interrupts are not handled. -func streamResponse(ctx context.Context, client *Client, message string, sigCh <-chan os.Signal) error { - if err := client.Send(message); err != nil { - return fmt.Errorf("failed to send message: %w", err) - } - - responses := client.ReadResponses() - - for { - if sigCh != nil { - // REPL mode: handle interrupt signal - select { - case <-ctx.Done(): - return ctx.Err() - case <-sigCh: - fmt.Println("\n(interrupted)") - for range responses { - } - return nil - case resp, ok := <-responses: - if !ok { - return nil - } - if rendered := RenderMessage(resp); rendered != "" { - fmt.Println(rendered) - } - if resp.IsTerminal() { - return nil - } - } - } else { - // Single-shot mode: no signal handling - select { - case <-ctx.Done(): - return ctx.Err() - case resp, ok := <-responses: - if !ok { - return nil - } - if rendered := RenderMessage(resp); rendered != "" { - fmt.Println(rendered) - } - if resp.IsTerminal() { - return nil - } - } - } - } -} diff --git a/internal/chat/types.go b/internal/chat/types.go deleted file mode 100644 index f6720d2..0000000 --- a/internal/chat/types.go +++ /dev/null @@ -1,37 +0,0 @@ -package chat - -// Status constants matching the-holonet protocol. -const ( - StatusInProgress = "In Progress" - StatusFinished = "Finished" - StatusErrored = "Errored" - StatusSurrendered = "Surrendered" -) - -// MessageType constants matching the-holonet protocol. -const ( - MessageTypeToolCall = "toolCall" - MessageTypeStatusMessage = "statusMessage" - MessageTypeResponse = "response" - MessageTypeError = "error" -) - -// Payload is the message sent to the Nullify chat WebSocket. -type Payload struct { - OwnerProvider map[string]string `json:"ownerProvider"` - ChatID string `json:"chatID"` - Message string `json:"message"` - ExtraSystemPrompt string `json:"extraSystemPrompt"` -} - -// MessageResponse is a message received from the Nullify chat WebSocket. -type MessageResponse struct { - Status string `json:"status"` - MessageType string `json:"messageType"` - Message string `json:"message"` -} - -// IsTerminal returns true if this response indicates the conversation turn is complete. -func (m *MessageResponse) IsTerminal() bool { - return m.Status == StatusFinished || m.Status == StatusErrored || m.Status == StatusSurrendered -} diff --git a/internal/client/client.go b/internal/client/client.go index b069153..6fe557a 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -8,7 +8,7 @@ import ( "strings" "time" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" ) // HTTPClient defines the interface for making HTTP requests. diff --git a/internal/client/dast_cloud_scan_start.go b/internal/client/dast_cloud_scan_start.go index e869680..c1da340 100644 --- a/internal/client/dast_cloud_scan_start.go +++ b/internal/client/dast_cloud_scan_start.go @@ -4,8 +4,8 @@ import ( "context" "fmt" + "github.com/nullify-platform/cli/internal/logger" "github.com/nullify-platform/cli/internal/models" - "github.com/nullify-platform/logger/pkg/logger" ) type DASTStartCloudScanInput struct { diff --git a/internal/client/dast_external_scan_create.go b/internal/client/dast_external_scan_create.go index 54b8186..058bb1d 100644 --- a/internal/client/dast_external_scan_create.go +++ b/internal/client/dast_external_scan_create.go @@ -5,8 +5,8 @@ import ( "fmt" "time" + "github.com/nullify-platform/cli/internal/logger" "github.com/nullify-platform/cli/internal/models" - "github.com/nullify-platform/logger/pkg/logger" ) type DASTCreateExternalScanInput struct { diff --git a/internal/client/dast_external_scan_update.go b/internal/client/dast_external_scan_update.go index 8a737f5..19f32c2 100644 --- a/internal/client/dast_external_scan_update.go +++ b/internal/client/dast_external_scan_update.go @@ -5,8 +5,8 @@ import ( "fmt" "net/url" + "github.com/nullify-platform/cli/internal/logger" "github.com/nullify-platform/cli/internal/models" - "github.com/nullify-platform/logger/pkg/logger" ) type DASTUpdateExternalScanInput struct { diff --git a/internal/client/refreshing_transport.go b/internal/client/refreshing_transport.go index f6de03b..9567f24 100644 --- a/internal/client/refreshing_transport.go +++ b/internal/client/refreshing_transport.go @@ -7,7 +7,7 @@ import ( "sync" "time" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" ) // TokenProvider is a function that returns a valid token. diff --git a/internal/client/transport.go b/internal/client/transport.go index 3b04d74..2f8b9d6 100644 --- a/internal/client/transport.go +++ b/internal/client/transport.go @@ -3,7 +3,7 @@ package client import ( "net/http" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" ) type authTransport struct { diff --git a/internal/commands/admin.go b/internal/commands/admin.go index 69fa0d6..fbbbedb 100644 --- a/internal/commands/admin.go +++ b/internal/commands/admin.go @@ -149,7 +149,7 @@ func RegisterAdminCommands(parent *cobra.Command, getClient func() *api.Client) cmd := &cobra.Command{ Use: "get-config", Short: "Usage Limits Get", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -868,7 +868,7 @@ func RegisterAdminCommands(parent *cobra.Command, getClient func() *api.Client) cmd := &cobra.Command{ Use: "get-finding", Short: "Get Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1427,7 +1427,7 @@ func RegisterAdminCommands(parent *cobra.Command, getClient func() *api.Client) cmd := &cobra.Command{ Use: "patch-sla", Short: "Patch SLA", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1634,7 +1634,7 @@ func RegisterAdminCommands(parent *cobra.Command, getClient func() *api.Client) cmd := &cobra.Command{ Use: "get-teams", Short: "Get Teams by Provider", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1667,7 +1667,7 @@ func RegisterAdminCommands(parent *cobra.Command, getClient func() *api.Client) cmd := &cobra.Command{ Use: "get-teams", Short: "Get Team", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1700,7 +1700,7 @@ func RegisterAdminCommands(parent *cobra.Command, getClient func() *api.Client) cmd := &cobra.Command{ Use: "get-findings", Short: "Get Team Findings mapping", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1791,7 +1791,7 @@ func RegisterAdminCommands(parent *cobra.Command, getClient func() *api.Client) cmd := &cobra.Command{ Use: "delete-savedViews", Short: "Delete UI Saved View", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1824,7 +1824,7 @@ func RegisterAdminCommands(parent *cobra.Command, getClient func() *api.Client) cmd := &cobra.Command{ Use: "patch-savedViews", Short: "Patch UI Saved View", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1975,7 +1975,7 @@ func RegisterAdminCommands(parent *cobra.Command, getClient func() *api.Client) cmd := &cobra.Command{ Use: "delete-mapping", Short: "Delete User Mapping", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -2008,7 +2008,7 @@ func RegisterAdminCommands(parent *cobra.Command, getClient func() *api.Client) cmd := &cobra.Command{ Use: "update-mapping", Short: "Edit User Mapping", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -2186,7 +2186,7 @@ func RegisterAdminCommands(parent *cobra.Command, getClient func() *api.Client) cmd := &cobra.Command{ Use: "get-users", Short: "Get Users by Provider", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -2219,7 +2219,7 @@ func RegisterAdminCommands(parent *cobra.Command, getClient func() *api.Client) cmd := &cobra.Command{ Use: "get-users", Short: "Get User", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} diff --git a/internal/commands/context.go b/internal/commands/context.go index 2bb42e6..4702a17 100644 --- a/internal/commands/context.go +++ b/internal/commands/context.go @@ -109,7 +109,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "patch-applications", Short: "Update Application", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -142,7 +142,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-applications", Short: "Get Application", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -175,7 +175,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "delete-applications", Short: "Delete Application", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -605,7 +605,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-active", Short: "List Active Dependencies", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -638,7 +638,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-at", Short: "Dependencies at commit", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -671,7 +671,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-diff", Short: "Dependency diff between two commits", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -706,7 +706,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-exposure", Short: "Project package exposure by version filter (semver or hash)", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -742,7 +742,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-history", Short: "Package exposure history (windows)", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -842,7 +842,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-memories", Short: "Get Memory", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -876,7 +876,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "delete-memories", Short: "Delete Memory", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -909,7 +909,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "patch-memories", Short: "Update Memory", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -942,7 +942,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-versions", Short: "Get Memory Versions", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1096,7 +1096,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "delete-repositories", Short: "Delete Repository", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1129,7 +1129,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "patch-repositories", Short: "Update Repository", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1162,7 +1162,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-repositories", Short: "Get Repository", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1195,7 +1195,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-projects", Short: "List Projects", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1231,7 +1231,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "patch-projects", Short: "Update Project", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1264,7 +1264,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-projects", Short: "Get Project", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1297,7 +1297,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-schema", Short: "Get Project Schema", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1330,7 +1330,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-metadata", Short: "Get Project Schema Metadata", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1392,7 +1392,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-project", Short: "Get Repository/Project SBOMs", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1489,7 +1489,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "delete-file", Short: "Delete Vault File", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1522,7 +1522,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-file", Short: "Get Vault File", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1555,7 +1555,7 @@ func RegisterContextCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "patch-file", Short: "Patch Vault File", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} diff --git a/internal/commands/cspm.go b/internal/commands/cspm.go index df1fa4a..7ec76ee 100644 --- a/internal/commands/cspm.go +++ b/internal/commands/cspm.go @@ -60,7 +60,7 @@ func RegisterCspmCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-findings", Short: "Get CSPM Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -128,7 +128,7 @@ func RegisterCspmCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-scans", Short: "Get CSPM Scan", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} diff --git a/internal/commands/dast.go b/internal/commands/dast.go index 224e606..6f45c4e 100644 --- a/internal/commands/dast.go +++ b/internal/commands/dast.go @@ -109,7 +109,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-findings", Short: "Get BugHunt Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -142,7 +142,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "patch-allowlist", Short: "Allowlist BugHunt Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -175,7 +175,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-events", Short: "Get BugHunt Finding Events", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -208,7 +208,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-triage", Short: "Get Bug Hunt Finding Triage", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -299,7 +299,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-scans", Short: "Get BugHunt Scan", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -332,7 +332,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-findings", Short: "Get BugHunt Scan Findings", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -365,7 +365,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-logs", Short: "Get BugHunt Sub-Agent Network Log", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -398,7 +398,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-stop", Short: "Stop BugHunt Scan", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -605,7 +605,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "delete-credentials", Short: "Delete Credential", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -638,7 +638,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-credentials", Short: "Get Credential", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -671,7 +671,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "update-credentials", Short: "Update Credential", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -704,7 +704,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-validate", Short: "Validate Credential", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1275,7 +1275,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "update-applications", Short: "Update Application", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1308,7 +1308,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-applications", Short: "Get Pentest App Config", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1341,7 +1341,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "delete-applications", Short: "Delete Pentest App Config", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1413,7 +1413,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-findings", Short: "Get Pentest Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1446,7 +1446,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-allowlist", Short: "Allowlist Pentest Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1479,7 +1479,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-events", Short: "Get Pentest Finding Events", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1512,7 +1512,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-full", Short: "Get Pentest Finding Full", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1545,7 +1545,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-ticket", Short: "Create Jira Ticket for Pentest Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1578,7 +1578,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-triage", Short: "Get Pentest Finding Full", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1671,7 +1671,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "patch-external", Short: "Update External Scan", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1704,7 +1704,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-scans", Short: "Get Pentest Scan", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1737,7 +1737,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-findings", Short: "Get Pentest Scan Findings", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1770,7 +1770,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-hypotheses", Short: "Get Auth Matrix Hypotheses", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1803,7 +1803,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-logs", Short: "Get Sub-Agent Network Log", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1836,7 +1836,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-report", Short: "Get Pentest Scan Report", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1869,7 +1869,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-download", Short: "Get Pentest Scan Report Download URL", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1902,7 +1902,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-pdf", Short: "Get Pentest Scan External Report PDF", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1935,7 +1935,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-pdf", Short: "Get Pentest Scan Report PDF", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1968,7 +1968,7 @@ func RegisterDastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-stop", Short: "Stop Pentest Scan", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} diff --git a/internal/commands/manager.go b/internal/commands/manager.go index 1870241..c7d562e 100644 --- a/internal/commands/manager.go +++ b/internal/commands/manager.go @@ -262,7 +262,7 @@ func RegisterManagerCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "delete-campaigns", Short: "Delete Campaign", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -295,7 +295,7 @@ func RegisterManagerCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-campaigns", Short: "Get Campaign by ID", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -328,7 +328,7 @@ func RegisterManagerCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "patch-campaigns", Short: "Patch Campaign", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -361,7 +361,7 @@ func RegisterManagerCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-events", Short: "Get Campaign Events", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -396,7 +396,7 @@ func RegisterManagerCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-findings", Short: "Get (Triaged) Findings Associated with a campaign", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -429,7 +429,7 @@ func RegisterManagerCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-preview", Short: "Get Campaign Preview Details", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -609,7 +609,7 @@ func RegisterManagerCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "patch-escalations", Short: "Patch Escalation", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -702,7 +702,7 @@ func RegisterManagerCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "delete-events", Short: "Delete A Campaign Event", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -735,7 +735,7 @@ func RegisterManagerCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-events", Short: "Get Events by Finding ID", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} diff --git a/internal/commands/sast.go b/internal/commands/sast.go index a644f19..f9e3db6 100644 --- a/internal/commands/sast.go +++ b/internal/commands/sast.go @@ -365,7 +365,7 @@ func RegisterSastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-findings", Short: "Get Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -398,7 +398,7 @@ func RegisterSastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "patch-findings", Short: "Update Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -431,7 +431,7 @@ func RegisterSastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-allowlist", Short: "Allowlist Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -464,7 +464,7 @@ func RegisterSastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-cache", Short: "Post Cache AutoFix", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -497,7 +497,7 @@ func RegisterSastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-create_pr", Short: "Post Finding AutoFix", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -530,7 +530,7 @@ func RegisterSastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-diff", Short: "Get Finding Autofix Diff", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -563,7 +563,7 @@ func RegisterSastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-fix", Short: "Post Finding AutoFix", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -596,7 +596,7 @@ func RegisterSastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-events", Short: "Get Finding Events", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -629,7 +629,7 @@ func RegisterSastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-pentest", Short: "Update SAST Finding With Pentest Result", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -662,7 +662,7 @@ func RegisterSastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-ticket", Short: "Create Jira Ticket", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -695,7 +695,7 @@ func RegisterSastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-triage", Short: "Get Triaged Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -728,7 +728,7 @@ func RegisterSastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-unallowlist", Short: "Unallowlist Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -761,7 +761,7 @@ func RegisterSastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-users", Short: "Get Finding Users", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -825,7 +825,7 @@ func RegisterSastCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-repositories", Short: "Get Repository Stats", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} diff --git a/internal/commands/sca.go b/internal/commands/sca.go index e87724d..b78d18d 100644 --- a/internal/commands/sca.go +++ b/internal/commands/sca.go @@ -203,7 +203,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-findings", Short: "Get Container Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -236,7 +236,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "patch-findings", Short: "Update Container Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -269,7 +269,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-allowlist", Short: "Allowlist Container Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -302,7 +302,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-cache", Short: "Post Cache AutoFix - SCA Containers", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -335,7 +335,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-fix", Short: "Fix Container Findings", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -368,7 +368,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-events", Short: "Get SCA Container Finding Events", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -401,7 +401,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-ticket", Short: "Create Jira Ticket", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -434,7 +434,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-triage", Short: "Get Triaged Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -467,7 +467,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-unallowlist", Short: "Unallowlist Container Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -500,7 +500,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-users", Short: "Get Finding Related Users", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -743,7 +743,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-create_pr", Short: "Create Pull Request for Dependency Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -776,7 +776,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "patch-findings", Short: "Update Dependencies Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -809,7 +809,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-findings", Short: "Get Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -842,7 +842,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-allowlist", Short: "Allowlist Dependency Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -875,7 +875,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-cache", Short: "Post Cache AutoFix", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -908,7 +908,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-diff", Short: "Get SCA Dependency Finding Fix", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -941,7 +941,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-fix", Short: "Fix Dependency Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -974,7 +974,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-events", Short: "Get SCA Dependency Finding Events", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1007,7 +1007,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-pentest", Short: "Update SCA Dependency Finding With Pentest Result", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1040,7 +1040,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-ticket", Short: "Create Jira Ticket", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1073,7 +1073,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-triage", Short: "Get Triaged Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1106,7 +1106,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "create-unallowlist", Short: "Unallowlist Dependency Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1139,7 +1139,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-users", Short: "Get Finding Related Users", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1267,7 +1267,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-repositories", Short: "Get Repository Stats", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -1300,7 +1300,7 @@ func RegisterScaCommands(parent *cobra.Command, getClient func() *api.Client) { cmd := &cobra.Command{ Use: "get-sbom", Short: "Get Repository SBOM", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} diff --git a/internal/commands/secrets.go b/internal/commands/secrets.go index fd85662..a03db44 100644 --- a/internal/commands/secrets.go +++ b/internal/commands/secrets.go @@ -292,7 +292,7 @@ func RegisterSecretsCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "patch-findings", Short: "Update Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -325,7 +325,7 @@ func RegisterSecretsCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-findings", Short: "Get Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -358,7 +358,7 @@ func RegisterSecretsCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "create-allowlist", Short: "Allowlist Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -391,7 +391,7 @@ func RegisterSecretsCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-events", Short: "Get Finding Events", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -424,7 +424,7 @@ func RegisterSecretsCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "create-ticket", Short: "Create Ticket", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -457,7 +457,7 @@ func RegisterSecretsCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-triage", Short: "Get Triaged Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -490,7 +490,7 @@ func RegisterSecretsCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "create-unallowlist", Short: "Unallowlist Finding", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} @@ -523,7 +523,7 @@ func RegisterSecretsCommands(parent *cobra.Command, getClient func() *api.Client cmd := &cobra.Command{ Use: "get-users", Short: "Get Finding Related Users", - Args: cobra.MaximumNArgs(1), + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { client := getClient() params := url.Values{} diff --git a/internal/lib/auth_config.go b/internal/lib/auth_config.go index 380a308..5ad6816 100644 --- a/internal/lib/auth_config.go +++ b/internal/lib/auth_config.go @@ -6,8 +6,8 @@ import ( "fmt" "os" + "github.com/nullify-platform/cli/internal/logger" "github.com/nullify-platform/cli/internal/models" - "github.com/nullify-platform/logger/pkg/logger" ) // ParseAuthConfig reads and parses an authentication configuration file diff --git a/internal/lib/auth_headers.go b/internal/lib/auth_headers.go index 65d57ac..9c874a9 100644 --- a/internal/lib/auth_headers.go +++ b/internal/lib/auth_headers.go @@ -6,7 +6,7 @@ import ( "regexp" "strings" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" ) var multiHeaderPattern = regexp.MustCompile(`, [A-Z][a-zA-Z0-9-]+: `) diff --git a/internal/lib/auth_headers_test.go b/internal/lib/auth_headers_test.go index 3c4aaa9..55115cb 100644 --- a/internal/lib/auth_headers_test.go +++ b/internal/lib/auth_headers_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" "github.com/stretchr/testify/require" ) diff --git a/internal/lib/get_token.go b/internal/lib/get_token.go index d880e0f..7659307 100644 --- a/internal/lib/get_token.go +++ b/internal/lib/get_token.go @@ -13,7 +13,7 @@ import ( "github.com/nullify-platform/cli/internal/auth" "github.com/nullify-platform/cli/internal/client" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" ) var ErrNoToken = errors.New("no token detected") diff --git a/internal/lib/get_token_test.go b/internal/lib/get_token_test.go index 3359708..a22c0d1 100644 --- a/internal/lib/get_token_test.go +++ b/internal/lib/get_token_test.go @@ -7,7 +7,7 @@ import ( "time" "github.com/nullify-platform/cli/internal/auth" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" "github.com/stretchr/testify/require" ) diff --git a/internal/lib/openapi.go b/internal/lib/openapi.go index cfa33e3..462b2ac 100644 --- a/internal/lib/openapi.go +++ b/internal/lib/openapi.go @@ -9,7 +9,7 @@ import ( "path/filepath" "strconv" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" "gopkg.in/yaml.v3" ) diff --git a/internal/logger/logger.go b/internal/logger/logger.go new file mode 100644 index 0000000..e8de8cf --- /dev/null +++ b/internal/logger/logger.go @@ -0,0 +1,65 @@ +package logger + +import ( + "context" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +// Version is set at build time via ldflags. +var Version string + +// Field is a type alias for zap.Field so callers use logger.Field transparently. +type Field = zap.Field + +// Logger wraps a *zap.Logger. +type Logger struct { + *zap.Logger +} + +type ctxKey struct{} + +// ConfigureDevelopmentLogger creates a zap development logger at the given level, +// stores it in the returned context, and returns the context. +// Logs are written to stderr so they don't corrupt piped stdout output. +func ConfigureDevelopmentLogger(ctx context.Context, logLevel string) (context.Context, error) { + level, err := zapcore.ParseLevel(logLevel) + if err != nil { + return ctx, err + } + + cfg := zap.NewDevelopmentConfig() + cfg.Level = zap.NewAtomicLevelAt(level) + cfg.OutputPaths = []string{"stderr"} + cfg.ErrorOutputPaths = []string{"stderr"} + + zapLogger, err := cfg.Build() + if err != nil { + return ctx, err + } + + return context.WithValue(ctx, ctxKey{}, &Logger{zapLogger}), nil +} + +// L retrieves the logger from context. Returns a no-op logger if none is set. +func L(ctx context.Context) *Logger { + if l, ok := ctx.Value(ctxKey{}).(*Logger); ok { + return l + } + return &Logger{zap.NewNop()} +} + +// Close flushes the logger, ignoring the sync error that is expected +// on stderr-based loggers (see https://github.com/uber-go/zap/issues/880). +func Close(ctx context.Context) { + _ = L(ctx).Sync() +} + +// Field constructors + +func String(key, val string) Field { return zap.String(key, val) } +func Int(key string, val int) Field { return zap.Int(key, val) } +func Err(err error) Field { return zap.Error(err) } +func Any(key string, val any) Field { return zap.Any(key, val) } +func Strings(key string, val []string) Field { return zap.Strings(key, val) } diff --git a/internal/logger/logger_test.go b/internal/logger/logger_test.go new file mode 100644 index 0000000..f7eb928 --- /dev/null +++ b/internal/logger/logger_test.go @@ -0,0 +1,89 @@ +package logger + +import ( + "bytes" + "context" + "os" + "testing" +) + +func TestConfigureDevelopmentLogger_RoundTrip(t *testing.T) { + ctx, err := ConfigureDevelopmentLogger(context.Background(), "debug") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + l := L(ctx) + if l == nil || l.Logger == nil { + t.Fatal("expected non-nil logger from context") + } + + // Should not panic + l.Debug("test debug message", String("key", "value")) + l.Info("test info message", Int("count", 42)) + l.Warn("test warn message", Err(nil)) + l.Error("test error message", Any("data", map[string]int{"a": 1})) +} + +func TestL_MissingContext_ReturnsNop(t *testing.T) { + l := L(context.Background()) + if l == nil || l.Logger == nil { + t.Fatal("expected non-nil no-op logger") + } + + // Should not panic on no-op logger + l.Info("this should be silently discarded") + l.Error("this too", String("key", "value")) +} + +func TestConfigureDevelopmentLogger_ValidLevels(t *testing.T) { + for _, level := range []string{"debug", "info", "warn", "error"} { + _, err := ConfigureDevelopmentLogger(context.Background(), level) + if err != nil { + t.Errorf("level %q should be valid, got error: %v", level, err) + } + } +} + +func TestConfigureDevelopmentLogger_InvalidLevel(t *testing.T) { + _, err := ConfigureDevelopmentLogger(context.Background(), "invalid") + if err == nil { + t.Error("expected error for invalid log level") + } +} + +func TestFieldConstructors(t *testing.T) { + // Verify field constructors don't panic and produce fields + _ = String("key", "val") + _ = Int("key", 1) + _ = Err(nil) + _ = Any("key", struct{}{}) + _ = Strings("key", []string{"a", "b"}) +} + +func TestLogsGoToStderr_NotStdout(t *testing.T) { + // Capture stdout to verify logs don't appear there + origStdout := os.Stdout + r, w, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + os.Stdout = w + + ctx, err := ConfigureDevelopmentLogger(context.Background(), "debug") + if err != nil { + os.Stdout = origStdout + t.Fatalf("unexpected error: %v", err) + } + + L(ctx).Info("should go to stderr not stdout") + + w.Close() + var buf bytes.Buffer + _, _ = buf.ReadFrom(r) + os.Stdout = origStdout + + if buf.Len() > 0 { + t.Errorf("expected no output on stdout, got: %s", buf.String()) + } +} diff --git a/internal/mcp/server.go b/internal/mcp/server.go index 3b3e890..8104f82 100644 --- a/internal/mcp/server.go +++ b/internal/mcp/server.go @@ -8,7 +8,7 @@ import ( "github.com/nullify-platform/cli/internal/client" "github.com/nullify-platform/cli/internal/lib" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" diff --git a/internal/mcp/server_test.go b/internal/mcp/server_test.go index e90e770..2a2f232 100644 --- a/internal/mcp/server_test.go +++ b/internal/mcp/server_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/nullify-platform/cli/internal/client" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" "github.com/stretchr/testify/require" ) diff --git a/internal/pentest/local_scan.go b/internal/pentest/local_scan.go index 9883727..f6d33d2 100644 --- a/internal/pentest/local_scan.go +++ b/internal/pentest/local_scan.go @@ -15,8 +15,8 @@ import ( docker "github.com/docker/docker/client" "github.com/docker/docker/pkg/stdcopy" "github.com/nullify-platform/cli/internal/client" + "github.com/nullify-platform/cli/internal/logger" "github.com/nullify-platform/cli/internal/models" - "github.com/nullify-platform/logger/pkg/logger" ) type PentestExternalScanInput struct { diff --git a/internal/pentest/pentest.go b/internal/pentest/pentest.go index 6e2b5b9..15d93de 100644 --- a/internal/pentest/pentest.go +++ b/internal/pentest/pentest.go @@ -5,8 +5,8 @@ import ( "github.com/nullify-platform/cli/internal/client" "github.com/nullify-platform/cli/internal/lib" + "github.com/nullify-platform/cli/internal/logger" "github.com/nullify-platform/cli/internal/models" - "github.com/nullify-platform/logger/pkg/logger" ) type PentestScan struct { diff --git a/internal/testutil/testutil.go b/internal/testutil/testutil.go index a759315..8f5fdd9 100644 --- a/internal/testutil/testutil.go +++ b/internal/testutil/testutil.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "github.com/nullify-platform/cli/internal/client" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" ) // MockNullifyClient creates a NullifyClient that talks to a test HTTP server. diff --git a/internal/wizard/steps.go b/internal/wizard/steps.go index fef7633..78a0069 100644 --- a/internal/wizard/steps.go +++ b/internal/wizard/steps.go @@ -11,7 +11,7 @@ import ( "github.com/nullify-platform/cli/internal/auth" "github.com/nullify-platform/cli/internal/lib" - "github.com/nullify-platform/logger/pkg/logger" + "github.com/nullify-platform/cli/internal/logger" ) // DomainStep checks if a valid host is configured and prompts the user if not. diff --git a/scripts/generate/main.go b/scripts/generate/main.go index ba05e6f..1207015 100644 --- a/scripts/generate/main.go +++ b/scripts/generate/main.go @@ -66,28 +66,28 @@ type Schema struct { // Service grouping based on path prefix var serviceMapping = map[string]string{ - "/sast/": "sast", - "/sca/": "sca", - "/secrets/": "secrets", - "/dast/": "dast", - "/admin/": "admin", - "/manager/": "manager", - "/context/": "context", - "/cspm/": "cspm", - "/ticket/": "ticket", + "/sast/": "sast", + "/sca/": "sca", + "/secrets/": "secrets", + "/dast/": "dast", + "/admin/": "admin", + "/manager/": "manager", + "/context/": "context", + "/cspm/": "cspm", + "/ticket/": "ticket", } // serviceDescriptions maps service names to human-readable descriptions. var serviceDescriptions = map[string]string{ - "sast": "Static Application Security Testing (SAST)", - "sca": "Software Composition Analysis (SCA)", - "secrets": "Secrets Detection", - "dast": "Dynamic Application Security Testing (DAST)", - "admin": "Administration and Metrics", - "manager": "Finding Lifecycle Management", - "context": "Repository and Code Classification", - "cspm": "Cloud Security Posture Management (CSPM)", - "ticket": "Ticket Integration", + "sast": "Static Application Security Testing (SAST)", + "sca": "Software Composition Analysis (SCA)", + "secrets": "Secrets Detection", + "dast": "Dynamic Application Security Testing (DAST)", + "admin": "Administration and Metrics", + "manager": "Finding Lifecycle Management", + "context": "Repository and Code Classification", + "cspm": "Cloud Security Posture Management (CSPM)", + "ticket": "Ticket Integration", } // Paths to exclude from CLI generation @@ -98,18 +98,18 @@ var excludePrefixes = []string{ } type Endpoint struct { - Path string - Method string - Summary string - Description string - Service string - Parameters []Parameter - HasBody bool - BodySchema string + Path string + Method string + Summary string + Description string + Service string + Parameters []Parameter + HasBody bool + BodySchema string OutputSchema string - FuncName string - CobraCmd string - CobraPath string + FuncName string + CobraCmd string + CobraPath string } func main() { @@ -145,8 +145,14 @@ func main() { endpoints := extractEndpoints(spec) grouped := groupByService(endpoints) - os.MkdirAll(outputDir, 0755) - os.MkdirAll(cmdOutputDir, 0755) + if err := os.MkdirAll(outputDir, 0755); err != nil { + fmt.Fprintf(os.Stderr, "Error creating output dir: %v\n", err) + os.Exit(1) + } + if err := os.MkdirAll(cmdOutputDir, 0755); err != nil { + fmt.Fprintf(os.Stderr, "Error creating cmd output dir: %v\n", err) + os.Exit(1) + } generateClient(outputDir, grouped) generateCommands(cmdOutputDir, grouped) @@ -368,20 +374,20 @@ func generateClientFile(outputDir string, service string, endpoints []Endpoint) sb.WriteString(")\n") for _, ep := range endpoints { - sb.WriteString(fmt.Sprintf("\n// %s - %s\n// %s %s\n", ep.FuncName, ep.Summary, ep.Method, ep.Path)) + fmt.Fprintf(&sb, "\n// %s - %s\n// %s %s\n", ep.FuncName, ep.Summary, ep.Method, ep.Path) if ep.HasBody { - sb.WriteString(fmt.Sprintf("func (c *Client) %s(ctx context.Context, params url.Values, body io.Reader) ([]byte, error) {\n", ep.FuncName)) + fmt.Fprintf(&sb, "func (c *Client) %s(ctx context.Context, params url.Values, body io.Reader) ([]byte, error) {\n", ep.FuncName) } else { - sb.WriteString(fmt.Sprintf("func (c *Client) %s(ctx context.Context, params url.Values) ([]byte, error) {\n", ep.FuncName)) + fmt.Fprintf(&sb, "func (c *Client) %s(ctx context.Context, params url.Values) ([]byte, error) {\n", ep.FuncName) } - sb.WriteString(fmt.Sprintf("\tpath := %q\n", ep.Path)) + fmt.Fprintf(&sb, "\tpath := %q\n", ep.Path) // Path parameter substitution for _, p := range ep.Parameters { if p.In == "path" { - sb.WriteString(fmt.Sprintf("\tpath = strings.Replace(path, \"{%s}\", params.Get(%q), 1)\n", p.Name, p.Name)) + fmt.Fprintf(&sb, "\tpath = strings.Replace(path, \"{%s}\", params.Get(%q), 1)\n", p.Name, p.Name) } } @@ -389,22 +395,25 @@ func generateClientFile(outputDir string, service string, endpoints []Endpoint) for _, p := range ep.Parameters { if p.In == "query" { - sb.WriteString(fmt.Sprintf("\tif v := params.Get(%q); v != \"\" {\n\t\tquery.Set(%q, v)\n\t}\n", p.Name, p.Name)) + fmt.Fprintf(&sb, "\tif v := params.Get(%q); v != \"\" {\n\t\tquery.Set(%q, v)\n\t}\n", p.Name, p.Name) } } sb.WriteString("\n\tfullURL := fmt.Sprintf(\"%s%s\", c.BaseURL, path)\n\tif len(query) > 0 {\n\t\tfullURL += \"?\" + query.Encode()\n\t}\n\n") if ep.HasBody { - sb.WriteString(fmt.Sprintf("\treturn c.do(ctx, %q, fullURL, body)\n", ep.Method)) + fmt.Fprintf(&sb, "\treturn c.do(ctx, %q, fullURL, body)\n", ep.Method) } else { - sb.WriteString(fmt.Sprintf("\treturn c.do(ctx, %q, fullURL, nil)\n", ep.Method)) + fmt.Fprintf(&sb, "\treturn c.do(ctx, %q, fullURL, nil)\n", ep.Method) } sb.WriteString("}\n") } - os.WriteFile(filePath, []byte(sb.String()), 0644) + if err := os.WriteFile(filePath, []byte(sb.String()), 0644); err != nil { + fmt.Fprintf(os.Stderr, "Error writing file %s: %v\n", filePath, err) + os.Exit(1) + } } func generateClient(outputDir string, grouped map[string][]Endpoint) { @@ -498,7 +507,10 @@ func (c *Client) do(ctx context.Context, method, url string, body io.Reader) ([] return respBody, nil } ` - os.WriteFile(filePath, []byte(content), 0644) + if err := os.WriteFile(filePath, []byte(content), 0644); err != nil { + fmt.Fprintf(os.Stderr, "Error writing file %s: %v\n", filePath, err) + os.Exit(1) + } } func generateCommands(outputDir string, grouped map[string][]Endpoint) { @@ -530,8 +542,8 @@ func generateCommandFile(outputDir string, service string, endpoints []Endpoint) if svcDesc == "" { svcDesc = svcPascal + " commands" } - sb.WriteString(fmt.Sprintf("func Register%sCommands(parent *cobra.Command, getClient func() *api.Client) {\n", svcPascal)) - sb.WriteString(fmt.Sprintf("\tserviceCmd := &cobra.Command{\n\t\tUse: %q,\n\t\tShort: %q,\n\t}\n\tparent.AddCommand(serviceCmd)\n\n", service, svcDesc)) + fmt.Fprintf(&sb, "func Register%sCommands(parent *cobra.Command, getClient func() *api.Client) {\n", svcPascal) + fmt.Fprintf(&sb, "\tserviceCmd := &cobra.Command{\n\t\tUse: %q,\n\t\tShort: %q,\n\t}\n\tparent.AddCommand(serviceCmd)\n\n", service, svcDesc) for _, ep := range endpoints { cobraUse := generateCobraUse(ep) @@ -550,10 +562,10 @@ func generateCommandFile(outputDir string, service string, endpoints []Endpoint) } sb.WriteString("\t{\n") - sb.WriteString(fmt.Sprintf("\t\tcmd := &cobra.Command{\n\t\t\tUse: %q,\n\t\t\tShort: %q,\n", cobraUse, summary)) + fmt.Fprintf(&sb, "\t\tcmd := &cobra.Command{\n\t\t\tUse: %q,\n\t\t\tShort: %q,\n", cobraUse, summary) if pathParamName != "" { - sb.WriteString(fmt.Sprintf("\t\t\tArgs: cobra.MaximumNArgs(1),\n")) + sb.WriteString("\t\t\tArgs: cobra.MaximumNArgs(1),\n") } sb.WriteString("\t\t\tRunE: func(cmd *cobra.Command, args []string) error {\n") @@ -575,7 +587,7 @@ func generateCommandFile(outputDir string, service string, endpoints []Endpoint) for _, p := range queryParams { kebab := toKebabCase(p.Name) if kebab != p.Name { - sb.WriteString(fmt.Sprintf("\t\t\t\t\t%q: %q,\n", kebab, p.Name)) + fmt.Fprintf(&sb, "\t\t\t\t\t%q: %q,\n", kebab, p.Name) } } sb.WriteString("\t\t\t\t}\n") @@ -585,13 +597,13 @@ func generateCommandFile(outputDir string, service string, endpoints []Endpoint) } if pathParamName != "" { - sb.WriteString(fmt.Sprintf("\t\t\t\tif len(args) > 0 {\n\t\t\t\t\tparams.Set(%q, args[0])\n\t\t\t\t}\n", pathParamName)) + fmt.Fprintf(&sb, "\t\t\t\tif len(args) > 0 {\n\t\t\t\t\tparams.Set(%q, args[0])\n\t\t\t\t}\n", pathParamName) } if ep.HasBody { - sb.WriteString(fmt.Sprintf("\t\t\t\tresult, err := client.%s(cmd.Context(), params, os.Stdin)\n", ep.FuncName)) + fmt.Fprintf(&sb, "\t\t\t\tresult, err := client.%s(cmd.Context(), params, os.Stdin)\n", ep.FuncName) } else { - sb.WriteString(fmt.Sprintf("\t\t\t\tresult, err := client.%s(cmd.Context(), params)\n", ep.FuncName)) + fmt.Fprintf(&sb, "\t\t\t\tresult, err := client.%s(cmd.Context(), params)\n", ep.FuncName) } sb.WriteString("\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n") @@ -609,7 +621,10 @@ func generateCommandFile(outputDir string, service string, endpoints []Endpoint) sb.WriteString("}\n") - os.WriteFile(filePath, []byte(sb.String()), 0644) + if err := os.WriteFile(filePath, []byte(sb.String()), 0644); err != nil { + fmt.Fprintf(os.Stderr, "Error writing file %s: %v\n", filePath, err) + os.Exit(1) + } } func generateCobraUse(ep Endpoint) string {