From 30282f8661cc851cd7777d4418372df1deb7b17e Mon Sep 17 00:00:00 2001 From: jarvis Date: Tue, 29 Sep 2026 13:36:33 -0400 Subject: [PATCH 1/2] feat(cli): support cf native commands Signed-off-by: jarvis --- internal/execution/broker.go | 2 +- internal/execution/broker_test.go | 33 +++++++++++++++++++++++++------ internal/execution/run.go | 6 +++--- specs/cli.feature | 8 ++++---- 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/internal/execution/broker.go b/internal/execution/broker.go index fce956d..878f8e3 100644 --- a/internal/execution/broker.go +++ b/internal/execution/broker.go @@ -175,7 +175,7 @@ func (b *Broker) handler(mapPath func(*http.Request) (string, error), authorize func (b *Broker) cloudflareHandler(providerBase string) http.HandlerFunc { return func(response http.ResponseWriter, request *http.Request) { if !strings.HasPrefix(request.URL.Path, "/client/v4/") { - http.Error(response, "Wrangler request is outside Cloudflare API v4", http.StatusBadRequest) + http.Error(response, "request is outside Cloudflare API v4", http.StatusBadRequest) return } path := strings.TrimPrefix(request.URL.RequestURI(), "/client/v4") diff --git a/internal/execution/broker_test.go b/internal/execution/broker_test.go index 87c113f..4e61a27 100644 --- a/internal/execution/broker_test.go +++ b/internal/execution/broker_test.go @@ -220,23 +220,44 @@ func TestNativeResourceToolRejectsUnadvertisedExecutables(t *testing.T) { } } -func TestNativeCommandsDescribeWrappedWranglerExecutables(t *testing.T) { +func TestNativeCommandsDescribeWrappedCloudflareExecutables(t *testing.T) { t.Parallel() commands := NativeCommands([]catalog.ToolIntegration{ {ID: "git", Executables: []string{"git"}, Protocol: "git-smart-http"}, {ID: "wrangler", Executables: []string{"wrangler", "npx", "pnpm"}, Protocol: "cloudflare-api-base"}, + {ID: "cf", Executables: []string{"cf", "npx", "pnpm"}, Protocol: "cloudflare-api-base"}, }) - if got := strings.Join(commands, ", "); got != "git, wrangler, npx wrangler, pnpm wrangler" { + if got := strings.Join(commands, ", "); got != "git, wrangler, npx wrangler, pnpm wrangler, cf, npx cf, pnpm cf" { t.Fatalf("commands = %q", got) } } +func TestNativeCloudflareCommandSelectsOnlyAdvertisedPackage(t *testing.T) { + t.Parallel() + integrations := []catalog.ToolIntegration{ + {ID: "wrangler", Executables: []string{"wrangler", "npx", "pnpm"}, Protocol: "cloudflare-api-base"}, + {ID: "cf", Executables: []string{"cf", "npx", "pnpm"}, Protocol: "cloudflare-api-base"}, + } + for _, command := range [][]string{{"npx", "cf", "zones", "list"}, {"pnpm", "cf", "zones", "list"}} { + integration, _, err := selectIntegration(integrations, command) + if err != nil || integration.ID != "cf" { + t.Fatalf("command %v selected %q: %v", command, integration.ID, err) + } + } + _, _, err := selectIntegration(integrations, []string{"npx", "unrelated"}) + if err == nil || !strings.Contains(err.Error(), "does not advertise") { + t.Fatalf("unadvertised package error = %v", err) + } +} + func TestCloudflareNativeToolEnvironmentRemovesProviderCredentials(t *testing.T) { t.Parallel() - values := cleanEnvironment([]string{"PATH=/bin", "CLOUDFLARE_API_TOKEN=secret", "CF_API_KEY=secret", "SAFE=value"}, providerCredentialNames("wrangler")) - joined := strings.Join(values, "\n") - if strings.Contains(joined, "secret") || !strings.Contains(joined, "SAFE=value") { - t.Fatalf("environment = %q", joined) + for _, integration := range []string{"wrangler", "cf"} { + values := cleanEnvironment([]string{"PATH=/bin", "CLOUDFLARE_API_TOKEN=secret", "CF_API_KEY=secret", "SAFE=value"}, providerCredentialNames(integration)) + joined := strings.Join(values, "\n") + if strings.Contains(joined, "secret") || !strings.Contains(joined, "SAFE=value") { + t.Fatalf("%s environment = %q", integration, joined) + } } } diff --git a/internal/execution/run.go b/internal/execution/run.go index 0a0c53b..70d1372 100644 --- a/internal/execution/run.go +++ b/internal/execution/run.go @@ -241,8 +241,8 @@ func NativeCommands(integrations []catalog.ToolIntegration) []string { } func nativeCommandPrefix(integration catalog.ToolIntegration, executable string) []string { - if (executable == "npx" || executable == "pnpm") && integration.ID == "wrangler" { - return []string{executable, "wrangler"} + if (executable == "npx" || executable == "pnpm") && (integration.ID == "wrangler" || integration.ID == "cf") { + return []string{executable, integration.ID} } return []string{executable} } @@ -272,7 +272,7 @@ func setEnvironment(values []string, pairs ...string) []string { func providerCredentialNames(id string) []string { switch id { - case "wrangler": + case "wrangler", "cf": return []string{"CLOUDFLARE_API_TOKEN", "CLOUDFLARE_API_KEY", "CLOUDFLARE_EMAIL", "CF_API_TOKEN", "CF_API_KEY", "CF_EMAIL"} case "gh": return []string{"GH_TOKEN", "GITHUB_TOKEN", "GH_ENTERPRISE_TOKEN", "GITHUB_ENTERPRISE_TOKEN", "GH_HOST", "GH_CONFIG_DIR", "SSL_CERT_FILE"} diff --git a/specs/cli.feature b/specs/cli.feature index 160c150..0e9349b 100644 --- a/specs/cli.feature +++ b/specs/cli.feature @@ -164,11 +164,11 @@ Feature: Realmroot Toolbox command line And local commits derive stable name and email from the immutable Agent username without changing global Git configuration @journey:cloudflare-native-tool @entrypoint:exec - Scenario: Use Wrangler as the stable Agent - Given Cloudflare advertises a Wrangler integration + Scenario: Use Cloudflare CLIs as the stable Agent + Given Cloudflare advertises Wrangler and cf integrations And the Agent has approved Cloudflare authority - When it runs Wrangler through "realmroot exec cloudflare" - Then Wrangler API traffic is routed through the Cloudflare Resource Server + When it runs Wrangler or cf through "realmroot exec cloudflare" + Then Cloudflare API traffic is routed through the Cloudflare Resource Server And existing Cloudflare credentials are removed from the child environment And Cloudflare asset-upload credentials remain process-local and are accepted only for their matching upload session From fb0ac506a0a10e4e6593911f39fff27c2a481eb8 Mon Sep 17 00:00:00 2001 From: jarvis Date: Tue, 29 Sep 2026 13:41:37 -0400 Subject: [PATCH 2/2] test(cli): resolve wrapper executables within the test Signed-off-by: jarvis --- internal/execution/broker_test.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/execution/broker_test.go b/internal/execution/broker_test.go index 4e61a27..abf538b 100644 --- a/internal/execution/broker_test.go +++ b/internal/execution/broker_test.go @@ -7,6 +7,7 @@ import ( "net/http" "net/http/httptest" "os" + "path/filepath" "strings" "testing" "time" @@ -233,7 +234,13 @@ func TestNativeCommandsDescribeWrappedCloudflareExecutables(t *testing.T) { } func TestNativeCloudflareCommandSelectsOnlyAdvertisedPackage(t *testing.T) { - t.Parallel() + bin := t.TempDir() + for _, name := range []string{"npx", "pnpm"} { + if err := os.WriteFile(filepath.Join(bin, name), nil, 0o755); err != nil { + t.Fatal(err) + } + } + t.Setenv("PATH", bin) integrations := []catalog.ToolIntegration{ {ID: "wrangler", Executables: []string{"wrangler", "npx", "pnpm"}, Protocol: "cloudflare-api-base"}, {ID: "cf", Executables: []string{"cf", "npx", "pnpm"}, Protocol: "cloudflare-api-base"},