profiletest

package
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 8, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package profiletest implements a CLI-driven end-to-end probe of a ccwrap profile: POST /v1/messages with max_tokens=1 against the configured upstream, then classify the response. The probe is direct in-process — it does not depend on a running ccwrap session and does not appear in the inspect web /recent timeline.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func MarshalResult

func MarshalResult(r ProbeResult) ([]byte, error)

MarshalResult formats a ProbeResult as the wire JSON used by both the CLI (`ccwrap profile test --format json`) and the inspect-web `/profile/test` endpoint. *T pointer fields encode as `null` when the underlying value is the zero-value.

Schema:

profile, status, latency_ms, http_status, base_url_host,
model_sent, model_sent_rewritten_to, model_echoed, error,
skipped_reason

Types

type EgressProbeOptions

type EgressProbeOptions struct {
	// Timeout caps the entire probe (DNS+TLS+request+response).
	Timeout time.Duration

	// Target overrides both env var and hardcoded default. Used by
	// tests and by the CLI --target flag. Leave empty in normal paths.
	Target string

	// NoProxy overlays bypass rules onto the resolved transport. The
	// supervisor probe path passes posture.egress.NoProxy here so a
	// mode=inherit profile probes through the same bypass list the
	// forward path honors — egress.Resolve's default branch drops
	// NoProxy (only resolveFromEnv populates it), so without this
	// overlay a probe through a flag-shaped URL silently ignores
	// NO_PROXY hosts that the running session would bypass. Same
	// comma-separated shape as the standard NO_PROXY env var.
	NoProxy string
}

EgressProbeOptions controls one egress probe. Zero value uses defaults (5s timeout; target resolved from env or hardcoded default).

type EgressProbeResult

type EgressProbeResult struct {
	Profile string        `json:"profile"`
	Status  ProbeStatus   `json:"status"`
	Latency time.Duration `json:"-"` // serialized via LatencyMs below
	// LatencyMs is always serialized — 0 is a real value (sub-ms probe
	// or early-failure latency before network), not "missing". Dropping
	// the field with omitempty would conflate a successful fast probe
	// with a probe that never started; both surfaces (CLI + popover)
	// would render "—" for an OK result, which users read as broken.
	LatencyMs  int64  `json:"latency_ms"`
	Target     string `json:"target"`     // URL actually probed
	EgressVia  string `json:"egress_via"` // sanitized (no userinfo)
	HTTPStatus int    `json:"http_status,omitempty"`

	// From ipinfo response body (best-effort parse on OK)
	PublicIP string `json:"public_ip,omitempty"`
	Country  string `json:"country,omitempty"`
	Region   string `json:"region,omitempty"`
	City     string `json:"city,omitempty"`
	Org      string `json:"org,omitempty"`

	Err string `json:"err,omitempty"`
}

EgressProbeResult is what one egress probe returns. Status reuses ProbeStatus from probe.go so consumer switch statements stay uniform; only OK / TIMEOUT / NET_FAIL / HTTP_4XX / HTTP_5XX are actually emitted (SKIPPED / AUTH_FAIL / MODEL_404 are upstream-probe concepts and never appear here).

func ProbeEgress

func ProbeEgress(profile profiles.Profile, opts EgressProbeOptions) EgressProbeResult

ProbeEgress runs one HTTPS GET against the resolved probe target, routed through profile.Egress. Result is always non-nil; never panics. Safe for concurrent use across different profiles.

Target resolution precedence:

  1. opts.Target (test / CLI --target)
  2. os.Getenv(envEgressTestTarget)
  3. defaultEgressTestTarget

Profile.Auth is intentionally ignored — egress probe sends NO credentials. profile.BaseURL is ignored too; the target is always the egress-test URL.

Treatment of profile.Egress.Mode:

  • "direct" → no proxy (explicit bypass of env)
  • "http" → HTTP CONNECT via profile.Egress.URL
  • "socks5", "socks5h" → SOCKS via profile.Egress.URL
  • "inherit" or unset → resolves through the calling process's environment via egress.Resolve (HTTPS_PROXY, HTTP_PROXY, ALL_PROXY, NO_PROXY). With no proxy env set, this is direct. With env set, the probe traverses that proxy — the same traffic-shape the caller would inherit at runtime. Callers wanting an explicit direct bypass must use mode="direct".
  • anything else → same env-resolution path as inherit (forward-compat; unknown modes do not crash but they DO follow env, so they are not equivalent to "direct")

Latency and LatencyMs are both populated at every result-return point; callers should not need to derive one from the other.

func (EgressProbeResult) MarshalJSON

func (r EgressProbeResult) MarshalJSON() ([]byte, error)

MarshalJSON renders Status as its string form (e.g. "OK", "NET_FAIL") so the wire schema exposes a stable string rather than the underlying int Go would serialize by default. Other fields use their tagged defaults. The Latency time.Duration is omitted from JSON via its `json:"-"` tag; LatencyMs is the wire-visible variant.

type ProbeOptions

type ProbeOptions struct {
	// Model, if non-empty, overrides the auto-resolved probe model.
	// When set, alias rewriting is bypassed (the literal value goes
	// into the request body's "model" field).
	Model string
	// Timeout caps the entire probe (DNS+connect+TLS+request+response).
	Timeout time.Duration
}

ProbeOptions controls a single probe.

type ProbeResult

type ProbeResult struct {
	Profile              string
	Status               ProbeStatus
	Latency              time.Duration
	HTTPStatus           int    // 0 if no response
	BaseURLHost          string // host only, no userinfo
	ModelSent            string // the model field actually transmitted
	ModelSentRewroteFrom string // empty unless alias rewrite happened
	ModelEchoed          string // upstream response body's model field
	Err                  string // single-line summary
	SkippedReason        string // populated when Status == StatusSkipped
}

ProbeResult is what one probe returns.

func Probe

func Probe(profile profiles.Profile, opts ProbeOptions) ProbeResult

Probe runs a single probe against one profile. The result is always non-nil and never panics. Callers may run Probe concurrently for different profiles.

A profile with no auth block (profile.Auth == nil) means "ccwrap does not own auth": there is no credential the probe can inject, so the probe short-circuits to SKIPPED, the same disposition as a passthrough profile.

type ProbeStatus

type ProbeStatus int

ProbeStatus is the classified outcome of a single probe.

const (
	StatusOK ProbeStatus = iota
	StatusSkipped
	StatusAuthFail
	StatusModel404
	StatusHTTP4xx
	StatusHTTP5xx
	StatusTimeout
	StatusNetFail
)

func (ProbeStatus) IsFailure

func (s ProbeStatus) IsFailure() bool

IsFailure reports whether this status should contribute to a non-zero process exit code. OK and SKIPPED do not.

func (ProbeStatus) String

func (s ProbeStatus) String() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL