seaportal

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 13, 2026 License: MIT Imports: 4 Imported by: 0

README

SeaPortal

Fast content extraction for AI agents. HTTP-first, no browser required.

Install

# npm (recommended)
npm install -g seaportal

# Go
go install github.com/pinchtab/seaportal/cmd/seaportal@latest

Usage

seaportal https://pinchtab.com

# Options
seaportal --json https://pinchtab.com       # JSON output
seaportal --snapshot https://pinchtab.com   # Accessibility tree
seaportal --fast https://pinchtab.com       # Bail early if browser needed
seaportal --no-dedupe https://pinchtab.com  # Disable deduplication

# Subcommands
seaportal sitemap https://pinchtab.com/sitemap.xml  # Flatten a sitemap
seaportal feed https://pinchtab.com/feed.xml        # Parse RSS / Atom / JSON Feed
seaportal scrape https://pinchtab.com               # Scrape a whole site → structured JSON
seaportal mcp                                       # Run as an MCP server over stdio

# Version
seaportal --version

The full flag list and subcommands are in the CLI reference. SeaPortal also runs as an MCP server (seaportal mcp), and ships seabench, a benchmark/evaluation harness.

Site scraping

seaportal scrape <base-url> (and the ScrapeSite library call / scrape_site MCP tool) crawls a whole site: it discovers URLs via robots.txt + sitemap (or a bounded crawl fallback), clusters similar paths into pattern groups, samples within a page budget, and extracts each page into a structured ScrapeResult (site, pageGroups, pages, summary). The JSON is designed as a hand-off to PinchTab for deep browser enrichment — screenshots, console/network capture, visual regression, and accessibility checks — keeping SeaPortal focused on fast HTTP-level discovery and extraction. See the scrape CLI docs.

Accessibility Snapshot

The --snapshot flag outputs a semantic accessibility tree — useful for AI agents that need to understand page structure and interact with elements:

seaportal --snapshot https://pinchtab.com
{
  "role": "document",
  "children": [
    {
      "role": "navigation",
      "name": "Main",
      "tag": "nav",
      "ref": "e1",
      "selector": "#main-nav",
      "depth": 0,
      "children": [
        {"role": "link", "name": "Home", "tag": "a", "ref": "e2", "selector": "a.nav-link", "depth": 1, "href": "/", "interactive": true}
      ]
    }
  ]
}

Each node includes:

  • role — Accessibility role (heading, link, button, textbox, etc.)
  • name — Accessible name (from aria-label, title, alt, or text)
  • tag — HTML tag name (div, a, button, etc.)
  • ref — Element reference (e1, e2...) for targeting
  • selector — CSS selector for the element
  • depth — Nesting depth in the tree
  • interactive — Whether the element can be clicked/typed
  • level — Heading level (1-6) for headings
  • href — Link target for links
Snapshot Options
# Filter to interactive elements only
seaportal --snapshot --filter=interactive https://example.com

# Compact text output (instead of JSON)
seaportal --snapshot --format=compact https://example.com

# Limit output size (approximate token count)
seaportal --snapshot --max-tokens=2000 https://example.com

# Combine options
seaportal --snapshot --filter=interactive --format=compact https://example.com

Compact format outputs a readable text tree:

document
  e1 navigation "Main" <nav> [interactive]
    e2 link "Home" <a> [interactive] href=/
    e3 link "Docs" <a> [interactive] href=/docs
  e4 main <main>
    e5 heading "Welcome" <h1> level=1

As a Library

The public package is the module root, github.com/pinchtab/seaportal:

import "github.com/pinchtab/seaportal"

// Extract content
result := seaportal.FromURL("https://pinchtab.com")
fmt.Println(result.Content) // extracted Markdown

// With options
result := seaportal.FromURLWithOptions("https://pinchtab.com", seaportal.Options{
    Dedupe:   true,
    FastMode: true,
})

// Build accessibility snapshot
snapshot, err := seaportal.BuildSnapshot(htmlString)

// Snapshot with options (filter, max tokens)
opts := seaportal.SnapshotOptions{
    FilterInteractive: true,
    MaxTokens:         2000,
}
snapshot, err := seaportal.BuildSnapshotWithOptions(htmlString, opts)

// Compact text output
fmt.Println(snapshot.ToCompact())

See the API reference for the full surface.

Features

  • Fast on its niche — Pure HTTP; on reachable static/SSR pages p50 ~1s, p95 ~2s (across the open web the tail is much longer)
  • Stealthy — Chrome TLS fingerprint, realistic headers
  • Smart — Readability extraction + Markdown conversion
  • Semantic — Accessibility tree for AI agents
  • Honest — Classifies pages, signals when browser is needed
  • Clean — Deduplicates repeated content blocks

Detection

Automatically detects:

  • Bot protection (Cloudflare, AWS WAF, DataDome, PerimeterX)
  • Captcha pages
  • Access denied / login walls
  • SPA / JavaScript-only content

Page Classification

Class Description
static Pure HTML, high confidence
ssr Server-rendered, good extraction
hydrated SSR + JS enhancement, usually extractable
spa JavaScript-only content, needs browser
dynamic Heavy client-side rendering
blocked Bot protection, captcha, access denied

The quality float is an advisory soft signal, not a gate — clean server-rendered pages routinely score ~0 while extracting perfectly. Route on the page class and browser-recommendation signal (profile.decision / browserRecommended), not the raw quality value. See api.md and browser-discriminator.md.

Reliability / what to expect

SeaPortal is a fast first-pass triage that fails over, not a universal fetcher. It wins on static and server-rendered pages and tells you when to reach for a browser instead of pretending every URL extracts.

Numbers below are a frozen snapshot of the committed live sweeps — full breakdown, dates, and git SHAs in the reliability reference:

Reachable, in-niche (static/SSR) Across the open web (Tranco top-1000)
Latency (ok fetches) p50 ~1s, p95 ~2s p50 ~1.6s, p90 >10s, p95 ~15s
Success ~94% ok 40% ok — ~53% netting out the ~242 dead CDN/DNS infra hosts

What that means in practice:

  • In its niche it's fast and reliable.
  • Across the raw open web, ~1 in 3 hosts time out and ~1 in 4 error — many are CDN/DNS infrastructure domains (akamaiedge.net, cloudfront.net, …) that never serve HTML.
  • Treat extraction as triage: set --timeout and route on the browser-recommendation signal (profile.decision / browserRecommended), failing over to a real browser rather than assuming the happy path.

Regenerate any time with ./dev bench sweep (see the seabench reference).

What It Doesn't Do

  • JavaScript execution
  • Full browser rendering
  • Cookie/session management

For JS-heavy pages, use a browser and pass HTML to seaportal.FromHTML().

Core vs. advanced surfaces

SeaPortal is, first, one thing: a fast, no-browser fetch-and-extract primitive that returns clean Markdown + an accessibility snapshot and tells you when a page needs a browser. That is the core, and everything in the value prop above describes it.

Layered on top are secondary, opt-in helpers — useful, but not the identity and off by default:

Surface What it is Where
Chunking (--chunk) Split Markdown into heading/sentence/window chunks for RAG api.md
BM25 ranking (--query) Score heading-bounded sections by relevance api.md
Split output (--split-*) Shard a large extraction across files api.md
TEI-Lite XML (--xml) Wrap a result as TEI-Lite for corpus tooling api.md
Sitemaps & feeds Flatten sitemap.xml, parse RSS/Atom/JSON Feed api.md
seabench Benchmark / capability harness — dev tooling, not shipped product seabench.md

If you only want the core, ignore all of the above: seaportal <url> and seaportal.FromURL(...) never touch them.

License

MIT

Documentation

Index

Constants

View Source
const (
	DefaultClientTimeout     = engine.DefaultClientTimeout
	DefaultMaxRetryWait      = engine.DefaultMaxRetryWait
	DefaultTotalRetryTimeout = engine.DefaultTotalRetryTimeout
	DefaultRetryBackoffBase  = engine.DefaultRetryBackoffBase
	DefaultSitemapMaxDepth   = engine.DefaultSitemapMaxDepth
	DefaultSitemapMaxURLs    = engine.DefaultSitemapMaxURLs
	DefaultFeedMaxItems      = engine.DefaultFeedMaxItems
)
View Source
const (
	LinkRetentionAll    = engine.LinkRetentionAll
	LinkRetentionNone   = engine.LinkRetentionNone
	LinkRetentionText   = engine.LinkRetentionText
	LinkRetentionFooter = engine.LinkRetentionFooter
)
View Source
const (
	ChunkOff      = engine.ChunkOff
	ChunkHeading  = engine.ChunkHeading
	ChunkSentence = engine.ChunkSentence
	ChunkWindow   = engine.ChunkWindow
)
View Source
const (
	DecisionStaticHighConfidence = engine.DecisionStaticHighConfidence
	DecisionStaticOK             = engine.DecisionStaticOK
	DecisionStaticCaution        = engine.DecisionStaticCaution
	DecisionBrowserNeeded        = engine.DecisionBrowserNeeded
	DecisionBlocked              = engine.DecisionBlocked
	DecisionAuthRequired         = engine.DecisionAuthRequired
	DecisionUnreachable          = engine.DecisionUnreachable
	DecisionNotFound             = engine.DecisionNotFound
	DecisionUnsupported          = engine.DecisionUnsupported
)
View Source
const (
	SampleBalanced = engine.SampleBalanced
	SampleRandom   = engine.SampleRandom
	SamplePriority = engine.SamplePriority
)
View Source
const (
	OutputJSON      = engine.OutputJSON
	OutputMarkdown  = engine.OutputMarkdown
	OutputDirectory = engine.OutputDirectory
)
View Source
const (
	DefaultScrapeMaxPages      = engine.DefaultScrapeMaxPages
	DefaultScrapeMaxPerPattern = engine.DefaultScrapeMaxPerPattern
	DefaultScrapeTimeout       = engine.DefaultScrapeTimeout
)

Variables

View Source
var (
	ErrSecurityScheme     = engine.ErrSecurityScheme
	ErrSecurityDomain     = engine.ErrSecurityDomain
	ErrPrivateIPBlocked   = engine.ErrPrivateIPBlocked
	ErrSecurityResolve    = engine.ErrSecurityResolve
	ErrResponseTooLarge   = engine.ErrResponseTooLarge
	ErrDecompressTooLarge = engine.ErrDecompressTooLarge
	ErrBlockedByRobots    = engine.ErrBlockedByRobots
	ErrNeedsBrowser       = engine.ErrNeedsBrowser
)
View Source
var ErrMissingBaseURL = engine.ErrMissingBaseURL

Functions

func CleanupMarkdown

func CleanupMarkdown(md string) string

func ContentChanged

func ContentChanged(oldContent, newContent string) bool

func DetectBlocked

func DetectBlocked(html string) bool

func DetectSPA

func DetectSPA(html string) (signals []string, isSPA bool)

func ExtractFromHTML

func ExtractFromHTML(html string, targetURL string) (string, error)

func FetchBytes

func FetchBytes(ctx context.Context, rawURL string, opts FetchBytesOptions) ([]byte, http.Header, int, error)

func PreprocessHTML

func PreprocessHTML(html string) string

func QuickNeedsBrowser

func QuickNeedsBrowser(html string) (needsBrowser bool, reason string)

func RenderScrapeJSON added in v0.2.0

func RenderScrapeJSON(res *ScrapeResult) ([]byte, error)

func RenderScrapeMarkdown added in v0.2.0

func RenderScrapeMarkdown(res *ScrapeResult) string

func ResultToTEIXML

func ResultToTEIXML(r Result) ([]byte, error)

func SemanticFingerprint

func SemanticFingerprint(content string) string

func WriteScrapeDirectory added in v0.2.0

func WriteScrapeDirectory(res *ScrapeResult, dir string) ([]string, error)

Types

type BrowserDecision

type BrowserDecision = engine.BrowserDecision

type CDNInfo added in v0.2.0

type CDNInfo = engine.CDNInfo

type CacheAnalysis added in v0.2.0

type CacheAnalysis = engine.CacheAnalysis

type CardItem

type CardItem = engine.CardItem

type Chunk

type Chunk = engine.Chunk

func ChunkMarkdown

func ChunkMarkdown(md string, cfg ChunkConfig) []Chunk

type ChunkConfig

type ChunkConfig = engine.ChunkConfig

func ParseChunkConfig

func ParseChunkConfig(s string) (ChunkConfig, error)

type ChunkStrategy

type ChunkStrategy = engine.ChunkStrategy

type DedupeOptions

type DedupeOptions = engine.DedupeOptions

type DedupeResult

type DedupeResult = engine.DedupeResult

func Dedupe

func Dedupe(content string) DedupeResult

func DedupeWithOptions

func DedupeWithOptions(content string, opts DedupeOptions) DedupeResult

type DedupeStats added in v0.2.0

type DedupeStats = engine.DedupeStats

type ExtractionOutcome

type ExtractionOutcome = engine.ExtractionOutcome

type FeedItem

type FeedItem = engine.FeedItem

func ParseFeed

func ParseFeed(ctx context.Context, feedURL string, opts ParseFeedOptions) ([]FeedItem, error)

type FetchBytesOptions

type FetchBytesOptions = engine.FetchBytesOptions

type FlattenSitemapOptions

type FlattenSitemapOptions = engine.FlattenSitemapOptions

type IndexPageResult

type IndexPageResult = engine.IndexPageResult

type LinkRetention

type LinkRetention = engine.LinkRetention

func ParseLinkRetention

func ParseLinkRetention(s string) (LinkRetention, error)

type Options

type Options = engine.Options

type OutputFormat added in v0.2.0

type OutputFormat = engine.OutputFormat

type PageClass

type PageClass = engine.PageClass

type PageGroup added in v0.2.0

type PageGroup = engine.PageGroup

type PageObject added in v0.2.0

type PageObject = engine.PageObject

type PagePerformance added in v0.2.0

type PagePerformance = engine.PagePerformance

type PageProfile

type PageProfile = engine.PageProfile

func ClassifyPage

func ClassifyPage(result Result) PageProfile

type ParseFeedOptions

type ParseFeedOptions = engine.ParseFeedOptions

type RankedSection

type RankedSection = engine.RankedSection

func RankSections

func RankSections(content, query string, k1, b float64, topN int) []RankedSection

type ResponseHeaders added in v0.2.0

type ResponseHeaders = engine.ResponseHeaders

type Result

type Result = engine.Result

func FromHTML

func FromHTML(html string, targetURL string) Result

func FromHTMLWithOptions

func FromHTMLWithOptions(html string, targetURL string, opts Options) Result

func FromResponse

func FromResponse(resp *http.Response, targetURL string, start time.Time) Result

func FromURL

func FromURL(targetURL string) Result

func FromURLContext added in v0.2.0

func FromURLContext(ctx context.Context, targetURL string, opts Options) Result

func FromURLWithDedupe

func FromURLWithDedupe(targetURL string) Result

func FromURLWithOptions

func FromURLWithOptions(targetURL string, opts Options) Result

type SampleStrategy added in v0.2.0

type SampleStrategy = engine.SampleStrategy

type ScrapeOptions added in v0.2.0

type ScrapeOptions = engine.ScrapeOptions

type ScrapeResult added in v0.2.0

type ScrapeResult = engine.ScrapeResult

func ScrapeSite added in v0.2.0

func ScrapeSite(ctx context.Context, opts *ScrapeOptions) (*ScrapeResult, error)

type ScrapeSummary added in v0.2.0

type ScrapeSummary = engine.ScrapeSummary

type SecurityPolicy

type SecurityPolicy = engine.SecurityPolicy

func DefaultSecurityPolicy

func DefaultSecurityPolicy() *SecurityPolicy

type SiteInfo added in v0.2.0

type SiteInfo = engine.SiteInfo

type SitemapEntry

type SitemapEntry = engine.SitemapEntry

func FlattenSitemap

func FlattenSitemap(ctx context.Context, sitemapURL string, opts FlattenSitemapOptions) ([]SitemapEntry, error)

type SnapshotNode

type SnapshotNode = engine.SnapshotNode

func BuildSnapshot

func BuildSnapshot(htmlStr string) (*SnapshotNode, error)

func BuildSnapshotWithOptions

func BuildSnapshotWithOptions(htmlStr string, opts SnapshotOptions) (*SnapshotNode, error)

type SnapshotOptions

type SnapshotOptions = engine.SnapshotOptions

type SplitConfig

type SplitConfig = engine.SplitConfig

type SplitFile

type SplitFile = engine.SplitFile

func SplitResultToFiles

func SplitResultToFiles(r Result, cfg SplitConfig) ([]SplitFile, error)

type TraceInfo added in v0.2.0

type TraceInfo = engine.TraceInfo

type TransportInfo added in v0.2.0

type TransportInfo = engine.TransportInfo

type Validation

type Validation = engine.Validation

func ValidateExtraction

func ValidateExtraction(r *Result) Validation

Directories

Path Synopsis
cmd
seabench command
seaportal command
internal
mcp
tests
tools/commenteraser command
Command commenteraser removes non-directive Go comments in place and gofmts the result.
Command commenteraser removes non-directive Go comments in place and gofmts the result.

Jump to

Keyboard shortcuts

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