[object Object]

← back to Cli Printing Press

fix(cli): default cookie HTML scrapes to browser transport (#822)

7a1d83ad26f96b81e877831f0a43000eaa37a85c · 2026-05-09 11:35:11 -0700 · Trevin Chow

Files touched

Diff

commit 7a1d83ad26f96b81e877831f0a43000eaa37a85c
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Sat May 9 11:35:11 2026 -0700

    fix(cli): default cookie HTML scrapes to browser transport (#822)
---
 internal/generator/generator_test.go | 42 ++++++++++++++++++++++++++++++++++++
 internal/spec/spec.go                | 12 +++++++++++
 internal/spec/spec_test.go           | 25 +++++++++++++++++++++
 skills/printing-press/SKILL.md       | 15 +++++++++++++
 4 files changed, 94 insertions(+)

diff --git a/internal/generator/generator_test.go b/internal/generator/generator_test.go
index ecd16e7e..2a1e87d8 100644
--- a/internal/generator/generator_test.go
+++ b/internal/generator/generator_test.go
@@ -1421,6 +1421,48 @@ func TestGenerateBrowserHTTPTransportDisablesHTTP2(t *testing.T) {
 	assert.NotContains(t, gomod, "github.com/enetx/surf")
 }
 
+func TestGenerateCookieHTMLDefaultsBrowserChromeTransport(t *testing.T) {
+	t.Parallel()
+
+	apiSpec := minimalSpec("cookiehtml")
+	apiSpec.BaseURL = "https://www.example.com"
+	apiSpec.Auth = spec.AuthConfig{
+		Type:         "cookie",
+		Header:       "Cookie",
+		In:           "cookie",
+		CookieDomain: ".example.com",
+		EnvVars:      []string{"COOKIEHTML_COOKIES"},
+	}
+	apiSpec.Resources = map[string]spec.Resource{
+		"diary": {
+			Description: "Read diary pages",
+			Endpoints: map[string]spec.Endpoint{
+				"get_day": {
+					Method:         "GET",
+					Path:           "/food/diary",
+					Description:    "Read a diary page",
+					ResponseFormat: spec.ResponseFormatHTML,
+					HTMLExtract: &spec.HTMLExtract{
+						Mode: spec.HTMLExtractModePage,
+					},
+				},
+			},
+		},
+	}
+
+	outputDir := filepath.Join(t.TempDir(), "cookiehtml-pp-cli")
+	require.NoError(t, New(apiSpec, outputDir).Generate())
+
+	gomod := readGeneratedFile(t, outputDir, "go.mod")
+	assert.Contains(t, gomod, "github.com/enetx/surf")
+
+	clientGo := readGeneratedFile(t, outputDir, "internal", "client", "client.go")
+	assert.Contains(t, clientGo, `"github.com/enetx/surf"`)
+	assert.Contains(t, clientGo, "Impersonate()")
+	assert.Contains(t, clientGo, "Chrome()")
+	assert.NotContains(t, clientGo, `req.Header.Set("User-Agent", "cookiehtml-pp-cli/0.1.0")`)
+}
+
 func TestGenerateHTMLExtractionEndpoint(t *testing.T) {
 	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "text/html; charset=utf-8")
diff --git a/internal/spec/spec.go b/internal/spec/spec.go
index f7160c0e..6745d439 100644
--- a/internal/spec/spec.go
+++ b/internal/spec/spec.go
@@ -297,6 +297,9 @@ func (s *APISpec) EffectiveHTTPTransport() string {
 	case HTTPTransportStandard, HTTPTransportBrowserHTTP, HTTPTransportBrowserChrome, HTTPTransportBrowserChromeH3:
 		return s.HTTPTransport
 	}
+	if s.usesBrowserAuthForHTML() {
+		return HTTPTransportBrowserChrome
+	}
 	switch s.SpecSource {
 	case "community", "sniffed":
 		return HTTPTransportBrowserChrome
@@ -305,6 +308,15 @@ func (s *APISpec) EffectiveHTTPTransport() string {
 	}
 }
 
+func (s *APISpec) usesBrowserAuthForHTML() bool {
+	switch strings.ToLower(strings.TrimSpace(s.Auth.Type)) {
+	case "cookie", "composed":
+		return s.HasHTMLExtraction()
+	default:
+		return false
+	}
+}
+
 func (s *APISpec) UsesBrowserHTTPTransport() bool {
 	switch s.EffectiveHTTPTransport() {
 	case HTTPTransportBrowserChrome, HTTPTransportBrowserChromeH3:
diff --git a/internal/spec/spec_test.go b/internal/spec/spec_test.go
index 0a724dca..cd3daba6 100644
--- a/internal/spec/spec_test.go
+++ b/internal/spec/spec_test.go
@@ -2479,6 +2479,31 @@ func TestHTTPTransportValidationAndDefaults(t *testing.T) {
 	assert.Equal(t, HTTPTransportStandard, override.EffectiveHTTPTransport())
 	require.NoError(t, override.Validate())
 
+	cookieHTML := base
+	cookieHTML.Auth.Type = "cookie"
+	cookieHTML.Resources = map[string]Resource{
+		"pages": {
+			Endpoints: map[string]Endpoint{
+				"show": {Method: "GET", Path: "/page", ResponseFormat: ResponseFormatHTML},
+			},
+		},
+	}
+	assert.Equal(t, HTTPTransportBrowserChrome, cookieHTML.EffectiveHTTPTransport())
+
+	composedHTML := cookieHTML
+	composedHTML.Auth.Type = "composed"
+	assert.Equal(t, HTTPTransportBrowserChrome, composedHTML.EffectiveHTTPTransport())
+
+	jsonCookie := cookieHTML
+	jsonCookie.Resources = map[string]Resource{
+		"items": {Endpoints: map[string]Endpoint{"list": {Method: "GET", Path: "/items"}}},
+	}
+	assert.Equal(t, HTTPTransportStandard, jsonCookie.EffectiveHTTPTransport())
+
+	cookieHTMLOverride := cookieHTML
+	cookieHTMLOverride.HTTPTransport = HTTPTransportStandard
+	assert.Equal(t, HTTPTransportStandard, cookieHTMLOverride.EffectiveHTTPTransport())
+
 	runtime := base
 	runtime.HTTPTransport = "browser-runtime"
 	assert.Equal(t, HTTPTransportStandard, runtime.EffectiveHTTPTransport())
diff --git a/skills/printing-press/SKILL.md b/skills/printing-press/SKILL.md
index 193f5edf..705779d4 100644
--- a/skills/printing-press/SKILL.md
+++ b/skills/printing-press/SKILL.md
@@ -1731,6 +1731,21 @@ resources:
         # no_auth defaults to false — placing an order needs auth
 ```
 
+#### Cookie/composed HTML transport
+
+For specs with `auth.type: cookie` or `auth.type: composed` and any
+`response_format: html` endpoint, treat browser fingerprint compatibility as
+the safe default. The generator emits Surf-backed Chrome transport for that
+shape unless the spec explicitly says `http_transport: standard`.
+
+Before setting an explicit standard opt-out, run
+`printing-press probe-reachability` against a representative HTML GET endpoint.
+If the probe returns `standard_http`, record `http_transport: standard` in the
+spec. If it returns `browser_http`, leave the default or set `http_transport:
+browser-chrome`. If it returns `browser_clearance_http`, return to the
+browser-clearance flow above so the generated CLI has both browser-compatible
+HTTP and reusable browser auth proof.
+
 ### Pre-Generation MCP Enrichment
 
 Before generating, count the spec's MCP tool surface and decide whether to opt

← 9bb46da6 fix(cli): correct HTTP Basic auth env vars (#810)  ·  back to Cli Printing Press  ·  fix(catalog): Align Google Flights catalog with Flight Goat' 19702102 →