Skip to content

feat(connectivity): add connector and connector-instance commands - #174

Draft
Dav-14 wants to merge 21 commits into
mainfrom
feat/connectivity-cli
Draft

feat(connectivity): add connector and connector-instance commands#174
Dav-14 wants to merge 21 commits into
mainfrom
feat/connectivity-cli

Conversation

@Dav-14

@Dav-14 Dav-14 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add a stack-authenticated fctl connectivity module
  • expose plugin discovery and instance lifecycle workflows
  • support schema-aware --set, --env-file, --config, and @file inputs
  • add dynamic completion for plugins, instances, versions, schema keys, and paths
  • redact inline configuration values from plain and debug output
  • adapt merge-patch payloads to the Connectivity 0.1.0 server shape at the HTTP boundary

Compatibility note

--start-sequence is intentionally not exposed: Connectivity API 0.1.0 models an integer sequence, while the pinned CRD uses an opaque byte cursor without a lossless mapping.

Testing

  • nix develop -c just tests
  • nix develop -c go test -race ./internal/connectivityclient ./cmd/connectivity/... ./pkg
  • nix develop -c just completions
  • nix develop -c just pre-commit

Combined Connectivity coverage: 87.5%.

@Dav-14
Dav-14 requested a review from a team as a code owner August 7, 2026 11:58
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 08defbe2-15ea-48e3-a433-36a34a925943

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

The change adds a stack-authenticated Connectivity API client, models, CLI command tree, connector and connector-instance workflows, schema-aware configuration parsing, bounded shell completion, context propagation, and debug-body redaction.

Changes

Connectivity API and authentication

Layer / File(s) Summary
API client and wire contracts
internal/connectivityclient/*, pkg/http.go, pkg/http_test.go
Adds Connectivity models, validated HTTP operations, API errors, patch serialization, integration tests, and redaction of configuration values in debug bodies.
Authentication and bounded execution
cmd/connectivity/internal/*, pkg/clients.go, pkg/clients_test.go
Adds client-factory authentication, silent non-interactive execution, and propagation of command deadlines and cancellation.
Connector catalog commands
cmd/connectivity/connectors/*
Adds connector listing, inspection, schema rendering, pagination, aliases, completion, JSON output, and error handling.

Connector-instance workflows

Layer / File(s) Summary
Schema-aware instance configuration
cmd/connectivity/connectorinstances/config.go, cmd/connectivity/connectorinstances/config_test.go
Adds schema extraction and configuration builders for YAML, dotenv, --set, files, Secret references, and ConfigMap references.
Instance listing, inspection, and completion
cmd/connectivity/connectorinstances/list.go, cmd/connectivity/connectorinstances/show.go, cmd/connectivity/connectorinstances/completion.go, cmd/connectivity/connectorinstances/test_helpers_test.go, cmd/connectivity/connectorinstances/*_test.go
Adds instance list and show commands, version and name completion, schema-key completion, filesystem completion, pagination, rendering, and JSON output.
Instance mutations
cmd/connectivity/connectorinstances/install.go, cmd/connectivity/connectorinstances/configure.go, cmd/connectivity/connectorinstances/uninstall.go, cmd/connectivity/connectorinstances/version.go, cmd/connectivity/connectorinstances/*_test.go
Adds approval-gated install, configure, and uninstall flows with version resolution, API mutations, response storage, rendering, and validation tests.
Connectivity command tree
cmd/connectivity/root.go, cmd/connectivity/connectorinstances/root.go, cmd/connectivity/connectors/root.go, cmd/root.go, cmd/*_test.go
Registers the Connectivity command and wires connector and connector-instance subcommands with shared dependencies and persistent flags.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ConnectivityCommand
  participant ClientFactory
  participant ConnectivityAPI
  participant Renderer
  User->>ConnectivityCommand: run connector or instance command
  ConnectivityCommand->>ClientFactory: create authenticated client
  ClientFactory->>ConnectivityAPI: authenticate and resolve stack access
  ConnectivityCommand->>ConnectivityAPI: list, retrieve, create, patch, or delete resource
  ConnectivityAPI-->>ConnectivityCommand: return validated response
  ConnectivityCommand->>Renderer: store and render result
  Renderer-->>User: display table, JSON, or confirmation
Loading

Poem

I’m a rabbit with a client in my pack,
Tokens stay quiet, then safely come back.
Connectors bloom in a neatly wired tree,
Config files hop where their values should be.
With paths and schemas, I thump in delight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.24% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: adding connector and connector-instance commands under the connectivity module.
Description check ✅ Passed The description directly covers the connectivity module, lifecycle workflows, configuration inputs, completion, redaction, compatibility, and testing.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/connectivity-cli

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@NumaryBot

NumaryBot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

🛑 Changes requested — automated review

The patch can expose inline configuration secrets in structured output and has several correctness issues around no-op updates, error reporting, and debug serialization.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (10)
cmd/connectivity/root_test.go (1)

13-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover public command aliases in this test.

The test checks only canonical command names. Add representative alias paths such as plugin list, instances ls, and instances delete so alias regressions fail in CI. The alias contract is defined in docs/superpowers/specs/2026-08-07-connectivity-cli-design.md.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/connectivity/root_test.go` around lines 13 - 25, Extend the command paths
table in the connectivity command test to include representative public aliases,
specifically plugin list, instances ls, and instances delete. Keep the existing
Find and name assertions so each alias path is resolved and validated alongside
canonical commands.
internal/connectivityclient/client.go (1)

208-233: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Consider bounding the response read.

decodeResponse calls io.ReadAll on the response body without a limit. A large or hostile response can exhaust memory in the CLI process. Wrap the body in io.LimitReader with a generous cap.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/connectivityclient/client.go` around lines 208 - 233, Update
decodeResponse to read through io.LimitReader with a generous maximum response
size before calling io.ReadAll, preserving the existing decoding and validation
behavior while preventing unbounded response-body memory usage.
internal/connectivityclient/integration_test.go (1)

74-94: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Do not call require inside the HTTP handler goroutine.

require.* calls t.FailNow, and testing documents that FailNow must run on the goroutine that runs the test function. The httptest handler runs on a server goroutine. On failure the handler exits through runtime.Goexit without writing a response, so the client reports an unrelated EOF error and the real assertion message is lost.

Use assert.* in the handler, or capture the decoded body and assert after the call returns.

🛠️ Proposed change
-		require.Equal(t, http.MethodPatch, req.Method)
-		require.Equal(t, "application/merge-patch+json", req.Header.Get("Content-Type"))
+		assert.Equal(t, http.MethodPatch, req.Method)
+		assert.Equal(t, "application/merge-patch+json", req.Header.Get("Content-Type"))
 
 		var patch map[string]any
-		require.NoError(t, json.NewDecoder(req.Body).Decode(&patch))
-		spec, ok := patch["spec"].(map[string]any)
-		require.True(t, ok)
-		require.NotContains(t, spec, "config")
+		if !assert.NoError(t, json.NewDecoder(req.Body).Decode(&patch)) {
+			return
+		}
+		spec, _ := patch["spec"].(map[string]any)
+		assert.NotContains(t, spec, "config")

The same change applies to the require.Equal call at line 20 and line 27 and line 33 in the lifecycle handler.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/connectivityclient/integration_test.go` around lines 74 - 94,
Replace all require.* assertions inside the httptest HTTP handler(s), including
the lifecycle handler, with assert.* so failures do not terminate the server
goroutine; keep require assertions in the test goroutine after the client call
where needed.
pkg/clients.go (1)

636-637: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Storing the context is acceptable here; document why.

oauth2.TokenSource.Token() takes no context, so the stored ctx is the only way to bound refresh and fetch. Go guidance normally discourages a context field. Add a short comment on the field to record the constraint and to prevent a future removal.

🛠️ Proposed addition
 type stackTokenSource struct {
 	mu  sync.Mutex
+	// ctx bounds Refresh and FetchStackToken. oauth2.TokenSource.Token
+	// accepts no context, so the command context is captured here.
 	ctx context.Context

Also applies to: 675-675, 689-689, 711-736

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/clients.go` around lines 636 - 637, Add a concise comment to the stored
ctx field in each affected client struct explaining that
oauth2.TokenSource.Token() accepts no context, so the field is required to bound
token refresh and fetch operations; keep the existing context usage unchanged.
pkg/http.go (1)

56-80: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Redaction depends on container key names.

isConfigContainer recognizes config, defaults, env, and files. A secret that the server returns outside these containers, for example inside an error details object that echoes a submitted value, still prints in debug output. The current Connectivity models place inline secrets only in those containers, so the coverage matches today's contract. Track this coupling if the wire contract gains a new secret-bearing container.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/http.go` around lines 56 - 80, Update the redaction logic around
isConfigContainer and redactInlineConfigValues to recognize every server
response container that may hold inline secret values, including error details
objects that echo submitted values. Extend the container-key coverage as the
wire contract adds secret-bearing containers, while preserving redaction of
value fields within the existing config, defaults, env, and files paths.
cmd/connectivity/internal/client.go (1)

85-87: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Confirm fctl.Dialog has only the Info method.

silentDialog implements Info alone. If Dialog gains another method, the non-interactive path breaks at compile time in a distant package. Add a compile-time assertion to localize the failure.

🛠️ Proposed addition
 type silentDialog struct{}
 
+var _ fctl.Dialog = silentDialog{}
+
 func (silentDialog) Info(string, ...any) {}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/connectivity/internal/client.go` around lines 85 - 87, Add a compile-time
interface assertion near silentDialog confirming it implements fctl.Dialog,
while preserving its existing Info method implementation; use the repository’s
fctl.Dialog symbol so any future interface-method mismatch fails at this local
declaration.
pkg/clients_test.go (1)

42-47: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Make the handler signal idempotent and widen the timing margins.

Two robustness problems exist in these deadline tests.

  1. The handler calls close(tokenStarted) directly. If the transport issues the request twice, for example after a redirect or a retry, the second call panics on a closed channel and crashes the test binary. Guard the close with sync.Once.
  2. The test uses a 75 ms deadline and a 300 ms assertion budget. A loaded CI runner can exceed 300 ms of scheduling delay, which produces a flaky failure. Increase both values, keeping the deadline well below the budget.

The same pattern exists at lines 100 and 145 in TestStackTokenSourceCancellationBoundsStackTokenRefresh.

🛠️ Proposed change
 	tokenStarted := make(chan struct{})
 	releaseToken := make(chan struct{})
+	var startOnce sync.Once
 	var server *httptest.Server
 	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 		switch req.URL.Path {
 		case "/api/auth/.well-known/openid-configuration":
 			w.Header().Set("Content-Type", "application/json")
 			_, _ = fmt.Fprintf(w, `{"token_endpoint":%q}`, server.URL+"/token")
 		case "/token":
-			close(tokenStarted)
+			startOnce.Do(func() { close(tokenStarted) })
 			select {
 			case <-req.Context().Done():
 			case <-releaseToken:
 			}
-	ctx, cancel := context.WithTimeout(context.Background(), 75*time.Millisecond)
+	ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
-	case <-time.After(300 * time.Millisecond):
+	case <-time.After(2 * time.Second):

Also applies to: 54-55, 85-89

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/clients_test.go` around lines 42 - 47, The deadline tests use
non-idempotent token-start signaling and timing margins that are too tight. In
the handlers for the affected cases in
TestStackTokenSourceCancellationBoundsStackTokenRefresh and the related test,
guard each close(tokenStarted) with sync.Once, and increase both the 75 ms
deadline and 300 ms assertion budget while keeping the deadline shorter than the
budget.
cmd/connectivity/instances/config_test.go (1)

285-296: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Wrap each case in t.Run for named failures.

The loop asserts two assignments without a subtest. A failure does not identify which assignment failed. Other table tests in this file use t.Run.

♻️ Proposed change
 	for _, assignment := range tests {
-		_, err := BuildInstallConfig(
-			&cobra.Command{},
-			pluginWithSchemaAndDefaults(),
-			InputOptions{SetValues: []string{assignment}},
-			mapReadFile(nil),
-		)
-
-		require.Error(t, err)
-		require.NotContains(t, err.Error(), assignment)
-		require.NotContains(t, err.Error(), secretSentinel)
+		t.Run(assignment, func(t *testing.T) {
+			_, err := BuildInstallConfig(
+				&cobra.Command{},
+				pluginWithSchemaAndDefaults(),
+				InputOptions{SetValues: []string{assignment}},
+				mapReadFile(nil),
+			)
+
+			require.Error(t, err)
+			require.NotContains(t, err.Error(), assignment)
+			require.NotContains(t, err.Error(), secretSentinel)
+		})
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/connectivity/instances/config_test.go` around lines 285 - 296, Wrap each
assignment iteration in a named t.Run subtest, using assignment as the subtest
name, and move the BuildInstallConfig call plus all related assertions into that
subtest so failures identify the specific input while preserving the existing
checks.
cmd/connectivity/instances/config.go (1)

259-259: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Consider raising the scanner buffer limit.

bufio.Scanner uses a 64 KiB maximum token size by default. A dotenv line that holds an inline certificate or key can exceed this limit. The scan then fails with bufio.Scanner: token too long, which does not explain the cause.

♻️ Proposed change
 	scanner := bufio.NewScanner(strings.NewReader(contents))
+	scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024)
 	line := 0
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/connectivity/instances/config.go` at line 259, Increase the buffer
capacity and maximum token size for the bufio.Scanner initialized in the dotenv
parsing flow before scanning contents, so long certificate or key lines are
accepted while preserving the existing parsing behavior.
cmd/connectivity/instances/install.go (1)

70-72: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Rename the internal version completion helper.

cmd/connectivity/instances/configure.go:53 calls completeVersions(...) while the exported helper is CompleteVersions(...). The unexported helper is only a wrapper, so give it a distinct name such as completeVersionsByName or inline the wrapper to avoid confusion at call sites.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/connectivity/instances/install.go` around lines 70 - 72, Rename the
unexported version-completion wrapper called by configure.go’s
completeVersions(...) to a distinct name such as completeVersionsByName, and
update all its call sites consistently while preserving the exported
CompleteVersions(...) helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/connectivity/instances/configure.go`:
- Around line 103-122: Update the config-change flow around BuildConfigureConfig
and specPatch["config"] to detect when the resolved InstanceConfig contains no
environment values and no files, using the existing configHasData helper from
install.go if applicable. Prevent an empty configuration from being added as a
successful no-op patch, and preserve applying non-empty configuration changes.

In `@cmd/connectivity/instances/show.go`:
- Line 65: Redact inline configuration values before storing instances for JSON
output, without mutating the API response model: update show.go line 65 to store
a copied redacted view preserving the inline source while removing
EnvValue.Value and FileMount.Value, and apply the same transformation to each
instance at list.go line 84. Update show_test.go lines 93-109 and list_test.go
lines 50-74 to assert JSON omits inline values; these tests require direct
changes.

In `@cmd/connectivity/plugins/list.go`:
- Line 80: Redact inline default values before storing output models: in
cmd/connectivity/plugins/list.go:80, map each response item to an output-safe
plugin model before assigning c.store.Plugins; in
cmd/connectivity/plugins/show.go:63, apply the same conversion before assigning
c.store.Plugin. Update the JSON tests to verify inline values are redacted while
configuration keys and references remain available.

In `@internal/connectivityclient/client.go`:
- Around line 286-301: Update decodeAPIError so the APIError Message falls back
to response.Status whenever payload.Message is empty, including successfully
decoded null or non-object-compatible payloads; preserve the existing
decode-error fallback and other payload fields.
- Around line 95-115: Update adaptInstancePatchForPinnedAPI so that after
validating configObject, it explicitly sets spec["env"] and spec["files"] to nil
when their corresponding keys are absent, while preserving provided values.
Ensure both sections are cleared before returning the patch.

In `@pkg/http.go`:
- Around line 43-54: Update redactDebugBody to decode JSON with a decoder
configured with UseNumber, preserving numeric text during redaction and
re-marshal. Also update printBody’s map[string]any decoding path to use the same
number-preserving decoder so colorized output retains large integer values.

---

Nitpick comments:
In `@cmd/connectivity/instances/config_test.go`:
- Around line 285-296: Wrap each assignment iteration in a named t.Run subtest,
using assignment as the subtest name, and move the BuildInstallConfig call plus
all related assertions into that subtest so failures identify the specific input
while preserving the existing checks.

In `@cmd/connectivity/instances/config.go`:
- Line 259: Increase the buffer capacity and maximum token size for the
bufio.Scanner initialized in the dotenv parsing flow before scanning contents,
so long certificate or key lines are accepted while preserving the existing
parsing behavior.

In `@cmd/connectivity/instances/install.go`:
- Around line 70-72: Rename the unexported version-completion wrapper called by
configure.go’s completeVersions(...) to a distinct name such as
completeVersionsByName, and update all its call sites consistently while
preserving the exported CompleteVersions(...) helper.

In `@cmd/connectivity/internal/client.go`:
- Around line 85-87: Add a compile-time interface assertion near silentDialog
confirming it implements fctl.Dialog, while preserving its existing Info method
implementation; use the repository’s fctl.Dialog symbol so any future
interface-method mismatch fails at this local declaration.

In `@cmd/connectivity/root_test.go`:
- Around line 13-25: Extend the command paths table in the connectivity command
test to include representative public aliases, specifically plugin list,
instances ls, and instances delete. Keep the existing Find and name assertions
so each alias path is resolved and validated alongside canonical commands.

In `@internal/connectivityclient/client.go`:
- Around line 208-233: Update decodeResponse to read through io.LimitReader with
a generous maximum response size before calling io.ReadAll, preserving the
existing decoding and validation behavior while preventing unbounded
response-body memory usage.

In `@internal/connectivityclient/integration_test.go`:
- Around line 74-94: Replace all require.* assertions inside the httptest HTTP
handler(s), including the lifecycle handler, with assert.* so failures do not
terminate the server goroutine; keep require assertions in the test goroutine
after the client call where needed.

In `@pkg/clients_test.go`:
- Around line 42-47: The deadline tests use non-idempotent token-start signaling
and timing margins that are too tight. In the handlers for the affected cases in
TestStackTokenSourceCancellationBoundsStackTokenRefresh and the related test,
guard each close(tokenStarted) with sync.Once, and increase both the 75 ms
deadline and 300 ms assertion budget while keeping the deadline shorter than the
budget.

In `@pkg/clients.go`:
- Around line 636-637: Add a concise comment to the stored ctx field in each
affected client struct explaining that oauth2.TokenSource.Token() accepts no
context, so the field is required to bound token refresh and fetch operations;
keep the existing context usage unchanged.

In `@pkg/http.go`:
- Around line 56-80: Update the redaction logic around isConfigContainer and
redactInlineConfigValues to recognize every server response container that may
hold inline secret values, including error details objects that echo submitted
values. Extend the container-key coverage as the wire contract adds
secret-bearing containers, while preserving redaction of value fields within the
existing config, defaults, env, and files paths.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ba339659-a6d5-49a8-beb2-da1f914d5f45

📥 Commits

Reviewing files that changed from the base of the PR and between e1080a5 and 78bab7d.

📒 Files selected for processing (40)
  • cmd/connectivity/instances/completion.go
  • cmd/connectivity/instances/completion_test.go
  • cmd/connectivity/instances/config.go
  • cmd/connectivity/instances/config_test.go
  • cmd/connectivity/instances/configure.go
  • cmd/connectivity/instances/configure_test.go
  • cmd/connectivity/instances/install.go
  • cmd/connectivity/instances/install_test.go
  • cmd/connectivity/instances/list.go
  • cmd/connectivity/instances/list_test.go
  • cmd/connectivity/instances/root.go
  • cmd/connectivity/instances/show.go
  • cmd/connectivity/instances/show_test.go
  • cmd/connectivity/instances/test_helpers_test.go
  • cmd/connectivity/instances/uninstall.go
  • cmd/connectivity/instances/uninstall_test.go
  • cmd/connectivity/internal/client.go
  • cmd/connectivity/internal/client_test.go
  • cmd/connectivity/plugins/completion.go
  • cmd/connectivity/plugins/completion_test.go
  • cmd/connectivity/plugins/list.go
  • cmd/connectivity/plugins/list_test.go
  • cmd/connectivity/plugins/root.go
  • cmd/connectivity/plugins/show.go
  • cmd/connectivity/plugins/show_test.go
  • cmd/connectivity/plugins/test_helpers_test.go
  • cmd/connectivity/root.go
  • cmd/connectivity/root_test.go
  • cmd/root.go
  • cmd/root_test.go
  • docs/superpowers/plans/2026-08-07-connectivity-cli.md
  • docs/superpowers/specs/2026-08-07-connectivity-cli-design.md
  • internal/connectivityclient/client.go
  • internal/connectivityclient/client_test.go
  • internal/connectivityclient/integration_test.go
  • internal/connectivityclient/models.go
  • pkg/clients.go
  • pkg/clients_test.go
  • pkg/http.go
  • pkg/http_test.go

Comment on lines +103 to +122
configChanged := cmd.Flags().Changed(configFlag) || cmd.Flags().Changed(envFileFlag) || cmd.Flags().Changed(setFlag)
if configChanged {
envFiles, err := cmd.Flags().GetStringArray(envFileFlag)
if err != nil {
return nil, err
}
setValues, err := cmd.Flags().GetStringArray(setFlag)
if err != nil {
return nil, err
}
config, err := BuildConfigureConfig(cmd, plugin, instance.Spec.Config, InputOptions{
ConfigFile: fctl.GetString(cmd, configFlag),
EnvFiles: envFiles,
SetValues: setValues,
}, c.read)
if err != nil {
return nil, err
}
specPatch["config"] = config
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Empty configuration input produces a no-op patch that reports success.

BuildConfigureConfig always returns a non-nil *InstanceConfig from cloneConfig. If the current instance has no configuration and the user passes an empty --config file, config holds an empty Env map and no files. InstanceConfig marks both fields omitempty, so the patch serializes as "config": {}. The server applies nothing, and the command still prints Instance "…" configured.

install.go guards this case with configHasData at lines 131-133. Apply the same guard here, or fail when the user requested a configuration change that resolves to no data.

♻️ Proposed change
 		if err != nil {
 			return nil, err
 		}
-		specPatch["config"] = config
+		if configHasData(config) {
+			specPatch["config"] = config
+		}
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
configChanged := cmd.Flags().Changed(configFlag) || cmd.Flags().Changed(envFileFlag) || cmd.Flags().Changed(setFlag)
if configChanged {
envFiles, err := cmd.Flags().GetStringArray(envFileFlag)
if err != nil {
return nil, err
}
setValues, err := cmd.Flags().GetStringArray(setFlag)
if err != nil {
return nil, err
}
config, err := BuildConfigureConfig(cmd, plugin, instance.Spec.Config, InputOptions{
ConfigFile: fctl.GetString(cmd, configFlag),
EnvFiles: envFiles,
SetValues: setValues,
}, c.read)
if err != nil {
return nil, err
}
specPatch["config"] = config
}
configChanged := cmd.Flags().Changed(configFlag) || cmd.Flags().Changed(envFileFlag) || cmd.Flags().Changed(setFlag)
if configChanged {
envFiles, err := cmd.Flags().GetStringArray(envFileFlag)
if err != nil {
return nil, err
}
setValues, err := cmd.Flags().GetStringArray(setFlag)
if err != nil {
return nil, err
}
config, err := BuildConfigureConfig(cmd, plugin, instance.Spec.Config, InputOptions{
ConfigFile: fctl.GetString(cmd, configFlag),
EnvFiles: envFiles,
SetValues: setValues,
}, c.read)
if err != nil {
return nil, err
}
if configHasData(config) {
specPatch["config"] = config
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/connectivity/instances/configure.go` around lines 103 - 122, Update the
config-change flow around BuildConfigureConfig and specPatch["config"] to detect
when the resolved InstanceConfig contains no environment values and no files,
using the existing configHasData helper from install.go if applicable. Prevent
an empty configuration from being added as a successful no-op patch, and
preserve applying non-empty configuration changes.

Comment thread cmd/connectivity/instances/show.go Outdated
if instance == nil {
return nil, fmt.Errorf("show connectivity instance %q: empty response", args[0])
}
c.store.Instance = *instance

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Redact inline configuration values before JSON serialization.

ShowStore and ListStore retain raw EnvValue.Value and FileMount.Value fields. Therefore, --output json exposes inline secrets. This conflicts with the PR redaction requirement.

  • cmd/connectivity/instances/show.go#L65-L65: Store a redacted view that preserves the inline source without retaining the inline value.
  • cmd/connectivity/instances/list.go#L84-L84: Store the same redacted view for every listed instance.
  • cmd/connectivity/instances/show_test.go#L93-L109: Assert that JSON omits the inline value.
  • cmd/connectivity/instances/list_test.go#L50-L74: Assert that JSON omits the inline value.

Do not mutate the API response in place. A caller can reuse that model after rendering.

📍 Affects 4 files
  • cmd/connectivity/instances/show.go#L65-L65 (this comment)
  • cmd/connectivity/instances/list.go#L84-L84
  • cmd/connectivity/instances/show_test.go#L93-L109
  • cmd/connectivity/instances/list_test.go#L50-L74
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/connectivity/instances/show.go` at line 65, Redact inline configuration
values before storing instances for JSON output, without mutating the API
response model: update show.go line 65 to store a copied redacted view
preserving the inline source while removing EnvValue.Value and FileMount.Value,
and apply the same transformation to each instance at list.go line 84. Update
show_test.go lines 93-109 and list_test.go lines 50-74 to assert JSON omits
inline values; these tests require direct changes.

Comment thread cmd/connectivity/plugins/list.go Outdated
return nil, fmt.Errorf("list connectivity plugins: empty response")
}

c.store.Plugins = response.Items

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Redact inline default values before storing output models.

Both commands store raw connectivityclient.Plugin models for JSON output. Plugin.Spec.Defaults can contain inline secret values. JSON output can expose these values, despite the PR redaction requirement.

  • cmd/connectivity/plugins/list.go#L80-L80: map each response item to an output-safe plugin model before assigning c.store.Plugins.
  • cmd/connectivity/plugins/show.go#L63-L63: map the response to an output-safe plugin model before assigning c.store.Plugin.

Update the JSON tests to assert that inline values are redacted while configuration keys and references remain available.

📍 Affects 2 files
  • cmd/connectivity/plugins/list.go#L80-L80 (this comment)
  • cmd/connectivity/plugins/show.go#L63-L63
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/connectivity/plugins/list.go` at line 80, Redact inline default values
before storing output models: in cmd/connectivity/plugins/list.go:80, map each
response item to an output-safe plugin model before assigning c.store.Plugins;
in cmd/connectivity/plugins/show.go:63, apply the same conversion before
assigning c.store.Plugin. Update the JSON tests to verify inline values are
redacted while configuration keys and references remain available.

Comment thread internal/connectivityclient/client.go Outdated
Comment on lines +95 to +115
config, exists := spec["config"]
if !exists {
return wirePatch, nil
}
delete(spec, "config")
if config == nil {
spec["env"] = nil
spec["files"] = nil
return wirePatch, nil
}
configObject, ok := config.(map[string]any)
if !ok {
return nil, fmt.Errorf("reshape connectivity instance patch: spec.config must be an object")
}
if env, ok := configObject["env"]; ok {
spec["env"] = env
}
if files, ok := configObject["files"]; ok {
spec["files"] = files
}
return wirePatch, nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect how configure builds the instance config and whether empty env/files are reachable.
fd -t f 'config.go' cmd/connectivity/instances --exec cat -n {}

Repository: formancehq/fctl

Length of output: 21619


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate connectivityclient files =="
fd -t f . internal/connectivityclient

echo "== client.go around reshape function =="
wc -l internal/connectivityclient/client.go
sed -n '1,180p' internal/connectivityclient/client.go | cat -n

echo "== models / config types =="
sed -n '1,220p' internal/connectivityclient/models.go | cat -n

echo "== usages of buildConfig/BuildConfigureConfig/config files =="
rg -n "BuildConfigureConfig|BuildInstallConfig|buildConfig|InstanceConfig|Env|Files|config" cmd/connectivity/instances internal/connectivityclient -S

echo "== behavioral probe: JSON/YAML marshaling and merge-patch key presence =="
python3 - <<'PY'
import json
import copy
current = {"spec": {"config": {"env": {"OLD": "value"}, "files": [{"path": "/old"}]}}}
for new in [
    {},
    {"env": {}},
    {"files": []},
    {"env": {}, "files": []},
    {"env": {"NEW": "value"}, "files": [{"path": "/new"}]},
]:
    next_ = copy.deepcopy(current)
    config = new.get("config")
    if config is not None:
        del next_["spec"]["config"]
        if config is not None:
            if "env" in config:
                next_["spec"]["env"] = config["env"]
            if "files" in config:
                next_["spec"]["files"] = config["files"]
    print(json.dumps({"new": new, "result_spec_keys": sorted(next_["spec"].keys()), "env": next_["spec"].get("env"), "files": next_["spec"].get("files")}))
PY

Repository: formancehq/fctl

Length of output: 50371


Clear configured env/files when spec.config has none.

BuildConfigureConfig preserves the current configuration by cloning it, so a configure request that sets no new input leaves config.env and/or config.files absent. In adaptInstancePatchForPinnedAPI, absent keys are not copied into the merge patch, so previous spec.env or spec.files remain on the instance. Write explicit nil values for missing sections before returning the patch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/connectivityclient/client.go` around lines 95 - 115, Update
adaptInstancePatchForPinnedAPI so that after validating configObject, it
explicitly sets spec["env"] and spec["files"] to nil when their corresponding
keys are absent, while preserving provided values. Ensure both sections are
cleared before returning the patch.

Comment on lines +286 to +301
func decodeAPIError(response *http.Response) error {
payload := struct {
Code string `json:"code"`
Message string `json:"message"`
Details map[string]any `json:"details"`
}{}
if err := json.NewDecoder(response.Body).Decode(&payload); err != nil {
return &APIError{StatusCode: response.StatusCode, Message: response.Status}
}
return &APIError{
StatusCode: response.StatusCode,
Code: payload.Code,
Message: payload.Message,
Details: payload.Details,
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Handle non-object error payloads.

json.Decode succeeds for a body such as null or "text" is rejected only for type mismatch; null decodes into the zero struct. The resulting APIError then carries an empty Code and Message, which hides the HTTP status text. Fall back to response.Status when payload.Message is empty.

🛠️ Proposed fix
 	if err := json.NewDecoder(response.Body).Decode(&payload); err != nil {
 		return &APIError{StatusCode: response.StatusCode, Message: response.Status}
 	}
+	if payload.Message == "" {
+		payload.Message = response.Status
+	}
 	return &APIError{
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func decodeAPIError(response *http.Response) error {
payload := struct {
Code string `json:"code"`
Message string `json:"message"`
Details map[string]any `json:"details"`
}{}
if err := json.NewDecoder(response.Body).Decode(&payload); err != nil {
return &APIError{StatusCode: response.StatusCode, Message: response.Status}
}
return &APIError{
StatusCode: response.StatusCode,
Code: payload.Code,
Message: payload.Message,
Details: payload.Details,
}
}
func decodeAPIError(response *http.Response) error {
payload := struct {
Code string `json:"code"`
Message string `json:"message"`
Details map[string]any `json:"details"`
}{}
if err := json.NewDecoder(response.Body).Decode(&payload); err != nil {
return &APIError{StatusCode: response.StatusCode, Message: response.Status}
}
if payload.Message == "" {
payload.Message = response.Status
}
return &APIError{
StatusCode: response.StatusCode,
Code: payload.Code,
Message: payload.Message,
Details: payload.Details,
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/connectivityclient/client.go` around lines 286 - 301, Update
decodeAPIError so the APIError Message falls back to response.Status whenever
payload.Message is empty, including successfully decoded null or
non-object-compatible payloads; preserve the existing decode-error fallback and
other payload fields.

Comment thread pkg/http.go
Comment on lines +43 to +54
func redactDebugBody(data []byte) []byte {
var body any
if err := json.Unmarshal(data, &body); err != nil {
return append([]byte(nil), data...)
}
redactInlineConfigValues(body, false)
redacted, err := json.Marshal(body)
if err != nil {
return append([]byte(nil), data...)
}
return redacted
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Debug output loses numeric fidelity after the re-marshal.

redactDebugBody decodes into any, so every JSON number becomes a float64. The re-marshal then prints large integers in an altered form. A 64-bit identifier such as 1234567890123456789 prints as 1234567890123456800. The previous code printed the raw bytes.

Use a decoder with UseNumber to keep the original numeric text.

🛠️ Proposed fix
 func redactDebugBody(data []byte) []byte {
 	var body any
-	if err := json.Unmarshal(data, &body); err != nil {
+	decoder := json.NewDecoder(bytes.NewReader(data))
+	decoder.UseNumber()
+	if err := decoder.Decode(&body); err != nil {
 		return append([]byte(nil), data...)
 	}

Note that printBody at line 30 also unmarshals into map[string]any before formatting, so the same rounding applies there. Fixing redactDebugBody alone does not remove the rounding in the colorized path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/http.go` around lines 43 - 54, Update redactDebugBody to decode JSON with
a decoder configured with UseNumber, preserving numeric text during redaction
and re-marshal. Also update printBody’s map[string]any decoding path to use the
same number-preserving decoder so colorized output retains large integer values.

Dav-14 added 14 commits August 12, 2026 19:08
The connectivity API renamed its resources to Connector /
ConnectorVersion / ConnectorInstance and now serves them under
/connectors, /connectors/{name}/versions and /connectorinstances.

Rename the CLI to match and re-model the client against the current
openapi.yaml:

- cmd/connectivity/plugins -> cmd/connectivity/connectors,
  cmd/connectivity/instances -> cmd/connectivity/connectorinstances.
- ConnectorSpec is now lean metadata; the installable versions and their
  configSchema live in ConnectorVersion objects, so install/configure
  resolve a version (the pin, the applied version, or the newest) and
  validate configuration against that version's schema.
- The API publishes no per-connector config defaults, so an install now
  starts from an empty configuration.
- Drop the client-side patch shim: the API reshapes spec.config into the
  CRD layout itself, so the client sends the documented API shape.
- Surface the resolved connector/version/digest and the spec channel in
  `connectorinstances show`; `--plugin` becomes `--connector`.
@Dav-14
Dav-14 force-pushed the feat/connectivity-cli branch from f40be48 to 0fdc07a Compare August 12, 2026 18:00
@Dav-14 Dav-14 changed the title feat(connectivity): add plugin and instance commands feat(connectivity): add connector and connector-instance commands Aug 12, 2026
@Dav-14

Dav-14 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto `main` and aligned with the renamed connectivity-api (RFC-0011 restore, on `develop`): distribution vocabulary is now Connector / ConnectorInstance / ConnectorVersion (paths `/connectors`, `/connectorinstances`), matching the server contract.

Not a pure rename — the old models targeted a fatter, older Plugin CRD, so the client was realigned to the leaner shipped shape:

  • configSchema now lives on ConnectorVersion; install/configure resolve a version (pin → applied → newest) before reading the schema. Unpinned installs still let the server resolve spec.version.
  • Dropped the client-side patch shim (adaptInstancePatchForPinnedAPI); the server now reshapes patches (internal/server/mapping.go), so the client sends the documented ConnectorInstancePatch.
  • install starts from an empty config: connector defaults are not exposed by the API today — they will be delivered via configSchema later.

Breaking for users of the earlier branch: JSON keys instanceconnectorInstance, pluginsconnectors; flag --plugin--connector.

Build/test/lint clean (go build ./..., go test ./... 0 failures, golangci-lint 0 issues).

@NumaryBot NumaryBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NumaryBot posted 4 new inline findings.

Summary: #174 (comment)

if instance == nil {
return nil, fmt.Errorf("show connectivity connector instance %q: empty response", args[0])
}
c.store.ConnectorInstance = *instance

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 [blocker] Redact inline configuration before storing output

When an instance contains inline config.env[*].value or config.files[*].value, assigning the API model directly exposes those secrets through --output json. The same raw-model storage occurs in list, install, and configure, so all instance command stores should use a redacted copy while preserving references and source metadata.

if err != nil {
return nil, err
}
specPatch["config"] = config

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [major] Reject empty configuration updates

When the current instance has no config and the user supplies an empty --config file, BuildConfigureConfig returns an empty non-nil config and this sends "config": {}. A JSON merge patch applies no change, yet the command reports success; omit the config patch when configHasData(config) is false so the existing no-changes error is returned.

Message string `json:"message"`
Details map[string]any `json:"details"`
}{}
if err := json.NewDecoder(response.Body).Decode(&payload); err != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [major] Fall back to HTTP status for empty API errors

For an error response whose body is null or an object without message, decoding succeeds but returns an APIError with an empty message, hiding useful status information such as 500 Internal Server Error. Use response.Status whenever the decoded message is empty.

Comment thread pkg/http.go
}
}

func redactDebugBody(data []byte) []byte {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [major] Preserve JSON numbers in debug output

With --debug, decoding JSON into any converts numbers to float64, so large integer identifiers or sequences are rounded before being printed (for example, 1234567890123456789 changes). Use decoders with UseNumber in both the redaction and color-formatting passes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
internal/connectivityclient/client.go (1)

288-303: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fall back to the HTTP status text when the error payload has no message.

json.Decode succeeds for a body of null and leaves payload zeroed. APIError.Error() then prints an empty code and message, which hides the HTTP status. Set payload.Message to response.Status when it is empty.

🛠️ Proposed fix
 	if err := json.NewDecoder(response.Body).Decode(&payload); err != nil {
 		return &APIError{StatusCode: response.StatusCode, Message: response.Status}
 	}
+	if payload.Message == "" {
+		payload.Message = response.Status
+	}
 	return &APIError{
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/connectivityclient/client.go` around lines 288 - 303, Update
decodeAPIError so that after successfully decoding the response payload, an
empty payload.Message is replaced with response.Status before constructing
APIError. Preserve the existing decode-error fallback and all populated payload
fields.
pkg/http.go (1)

43-54: 🎯 Functional Correctness | 🟡 Minor | 💤 Low value

Debug output loses numeric fidelity after the re-marshal.

redactDebugBody decodes into any, so each JSON number becomes a float64. Large integers then print in an altered form. For example, 1234567890123456789 prints as 1234567890123456800. Decode with UseNumber to keep the original numeric text. The same rounding applies to the map[string]any decode in printBody at Line 30.

🛠️ Proposed fix
 func redactDebugBody(data []byte) []byte {
 	var body any
-	if err := json.Unmarshal(data, &body); err != nil {
+	decoder := json.NewDecoder(bytes.NewReader(data))
+	decoder.UseNumber()
+	if err := decoder.Decode(&body); err != nil {
 		return append([]byte(nil), data...)
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/http.go` around lines 43 - 54, Update redactDebugBody and the
map[string]any decoding in printBody to use json.Decoder with UseNumber enabled
instead of json.Unmarshal into any, preserving original JSON number text through
redaction and re-marshalling while keeping existing fallback behavior.
🧹 Nitpick comments (4)
cmd/connectivity/connectorinstances/completion.go (1)

226-240: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the setFlag constant instead of the literal "set".

suppliedSetKeys looks up the flag by the literal "set". The command definitions in this package register the flag through the setFlag constant. If the constant changes, this lookup silently returns no supplied keys and completion suggests keys the user already provided.

♻️ Proposed refactor
-	if cmd != nil && cmd.Flags().Lookup("set") != nil {
-		if setValues, err := cmd.Flags().GetStringArray("set"); err == nil {
+	if cmd != nil && cmd.Flags().Lookup(setFlag) != nil {
+		if setValues, err := cmd.Flags().GetStringArray(setFlag); err == nil {
 			values = append(values, setValues...)
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/connectivity/connectorinstances/completion.go` around lines 226 - 240,
Update suppliedSetKeys to use the existing setFlag constant when looking up and
reading the flag, replacing the literal "set" while preserving the current
argument-merging and key-detection behavior.
cmd/connectivity/connectorinstances/list_test.go (1)

76-91: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move the root registration test and cover all five subcommands.

This test verifies the command tree built by root.go, but it lives in list_test.go. It also asserts only list and show, while NewCommand registers install, configure, and uninstall as well. Add the missing entries so an accidental removal of a mutation command fails this package's tests.

♻️ Proposed change
 	wantAliases := map[string][]string{
 		"list": {"ls", "l"},
 		"show": {"get", "g", "sh", "s"},
+		"install":   {"i"},
+		"configure": {"config", "update", "c"},
+		"uninstall": {"remove", "rm", "delete", "u"},
 	}

Align the expected aliases with the values declared in install.go, configure.go, and uninstall.go. Consider placing this test in a root_test.go file inside this package.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/connectivity/connectorinstances/list_test.go` around lines 76 - 91, Move
TestConnectorInstanceRootRegistersReadCommandsAndAliases from list_test.go into
a root_test.go test file, and expand its expected command map to cover list,
show, install, configure, and uninstall. Use the aliases declared by each
corresponding command implementation, while preserving the existing root Use and
root Aliases assertions.
internal/connectivityclient/integration_test.go (1)

16-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use assert inside both httptest handlers.

require calls t.FailNow, which exits only the server goroutine. This can hide the assertion failure behind an HTTP client error. Use assert for handler checks, or record failures and assert them from the test goroutine.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/connectivityclient/integration_test.go` around lines 16 - 44,
Replace the require.Equal calls inside the httptest server handler with
assert.Equal, covering the Accept header in the connector list case and
Content-Type checks in the POST and PATCH cases. Keep the existing request
validation and response behavior unchanged.
cmd/connectivity/connectorinstances/config.go (1)

254-284: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Raise the scanner buffer for long dotenv lines.

bufio.Scanner uses a 64 KiB maximum token size by default. If an env file contains one long line, for example an inlined certificate or key material, scanner.Err() returns bufio.ErrTooLong and the command fails with an opaque message. Set an explicit larger buffer.

♻️ Proposed change
 	scanner := bufio.NewScanner(strings.NewReader(contents))
+	scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024)
 	line := 0
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/connectivity/connectorinstances/config.go` around lines 254 - 284, Update
applyDotenv to configure the bufio.Scanner buffer before scanning, setting a
larger explicit maximum token size sufficient for long dotenv values such as
certificates or keys. Preserve the existing line parsing and scanner.Err
handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@internal/connectivityclient/client.go`:
- Around line 288-303: Update decodeAPIError so that after successfully decoding
the response payload, an empty payload.Message is replaced with response.Status
before constructing APIError. Preserve the existing decode-error fallback and
all populated payload fields.

In `@pkg/http.go`:
- Around line 43-54: Update redactDebugBody and the map[string]any decoding in
printBody to use json.Decoder with UseNumber enabled instead of json.Unmarshal
into any, preserving original JSON number text through redaction and
re-marshalling while keeping existing fallback behavior.

---

Nitpick comments:
In `@cmd/connectivity/connectorinstances/completion.go`:
- Around line 226-240: Update suppliedSetKeys to use the existing setFlag
constant when looking up and reading the flag, replacing the literal "set" while
preserving the current argument-merging and key-detection behavior.

In `@cmd/connectivity/connectorinstances/config.go`:
- Around line 254-284: Update applyDotenv to configure the bufio.Scanner buffer
before scanning, setting a larger explicit maximum token size sufficient for
long dotenv values such as certificates or keys. Preserve the existing line
parsing and scanner.Err handling.

In `@cmd/connectivity/connectorinstances/list_test.go`:
- Around line 76-91: Move
TestConnectorInstanceRootRegistersReadCommandsAndAliases from list_test.go into
a root_test.go test file, and expand its expected command map to cover list,
show, install, configure, and uninstall. Use the aliases declared by each
corresponding command implementation, while preserving the existing root Use and
root Aliases assertions.

In `@internal/connectivityclient/integration_test.go`:
- Around line 16-44: Replace the require.Equal calls inside the httptest server
handler with assert.Equal, covering the Accept header in the connector list case
and Content-Type checks in the POST and PATCH cases. Keep the existing request
validation and response behavior unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cf3cafad-454b-4726-83a9-eb3eab453b55

📥 Commits

Reviewing files that changed from the base of the PR and between 78bab7d and 0fdc07a.

📒 Files selected for processing (33)
  • cmd/connectivity/connectorinstances/completion.go
  • cmd/connectivity/connectorinstances/completion_test.go
  • cmd/connectivity/connectorinstances/config.go
  • cmd/connectivity/connectorinstances/config_test.go
  • cmd/connectivity/connectorinstances/configure.go
  • cmd/connectivity/connectorinstances/configure_test.go
  • cmd/connectivity/connectorinstances/install.go
  • cmd/connectivity/connectorinstances/install_test.go
  • cmd/connectivity/connectorinstances/list.go
  • cmd/connectivity/connectorinstances/list_test.go
  • cmd/connectivity/connectorinstances/root.go
  • cmd/connectivity/connectorinstances/show.go
  • cmd/connectivity/connectorinstances/show_test.go
  • cmd/connectivity/connectorinstances/test_helpers_test.go
  • cmd/connectivity/connectorinstances/uninstall.go
  • cmd/connectivity/connectorinstances/uninstall_test.go
  • cmd/connectivity/connectorinstances/version.go
  • cmd/connectivity/connectors/completion.go
  • cmd/connectivity/connectors/completion_test.go
  • cmd/connectivity/connectors/list.go
  • cmd/connectivity/connectors/list_test.go
  • cmd/connectivity/connectors/root.go
  • cmd/connectivity/connectors/show.go
  • cmd/connectivity/connectors/show_test.go
  • cmd/connectivity/connectors/test_helpers_test.go
  • cmd/connectivity/root.go
  • cmd/connectivity/root_test.go
  • internal/connectivityclient/client.go
  • internal/connectivityclient/client_test.go
  • internal/connectivityclient/integration_test.go
  • internal/connectivityclient/models.go
  • pkg/http.go
  • pkg/http_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • cmd/connectivity/root_test.go
  • cmd/connectivity/root.go
  • pkg/http_test.go

@Dav-14
Dav-14 marked this pull request as draft August 13, 2026 11:44
Dav-14 added 6 commits August 14, 2026 12:13
Descriptions are full sentences and pterm sizes a column to its widest cell,
so one connector wrapped every row and the table stopped lining up -- the
`Tags` column, which is what the list is useful for, ended up on its own line
under each entry.

`connectors show` prints the description, which is where a sentence belongs.
`connectorinstances list` failed with

  decode connectivity response: cursor.data[4]: connector instance
  spec.connector is required

on a response the server had answered 200. A ConnectorInstance may pin
spec.image on the CR instead of referencing a Connector -- the two are
mutually exclusive -- so the API returns it with an empty connector, and one
such row failed the decode of the entire page.

validateConnectorInstance guards decoding, not creation: both call sites are
response paths, so requiring a field here only rejects legitimate data.
Keep metadata.name, which identifies the row and Kubernetes always sets, and
drop connector and ledger -- enforcing a write-side invariant while reading is
the same mistake in both cases.

The two table cases asserting the old behaviour go with it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants