Built with
Kernel
What it does here
lib/server/kernel.ts wraps @onkernel/sdk. It opens a headless browser, runs a short Playwright script in it, and closes it. lib/server/comps.ts makes sense of what comes back, with the AI model.
| Function | What it does |
|---|---|
searchSite(site, queries) | Searches one marketplace with each query in turn until it has 12 listing cards, then returns each card's link, text (with its price) and photo. |
readProductPage(url) | Opens a product page and returns its title, og: price and image, JSON-LD and the start of the page text. |
withBrowser(fn) | Creates a browser, runs fn, always deletes the browser, and queues when too many are open. |
Browsers and limits
- Browsers are created with
headless: trueandtimeout_seconds: 120; each Playwright run getstimeout_sec: 55. - At most
KERNEL_MAX_BROWSERSare open at once (default 5, the free plan's limit). Extra ones wait in a queue, and a create that still hits the cap (another process) retries up to 5 times with a growing wait. - One browser per site, side by side, so a site that hangs or blocks can't take the others down. Each site gets 45 seconds; after that comps go ahead with whatever it found. Once a site has some cards, another query only runs if it's under 25 seconds in.
- Images, media and fonts are blocked. Pages load faster and don't crash, and cards keep their image links.
- Plain browsers, not stealth: in testing the stealth proxy made eBay serve error pages. eBay's "something went wrong" page is retried up to three times. Mercari crashed the page every time, so it isn't searched.
Comps, step by step
findComps in lib/server/comps.ts, used by research and the sidekick:
- The fast model writes 1 to 3 search queries the way a reseller would type them, most specific first.
- eBay, Poshmark and Depop are searched at once, one browser each. Up to 16 cards from each site are kept, so one big site doesn't crowd out the rest.
- The main model checks every card against the item: the
samemodel (any colour), abundlewith real extras, ordifferent(another generation or size, parts, lots, broken). It reads the asking price and the condition. - Exact matches are kept. With fewer than 5, bundles fill in. Asks under 40 percent or over 250 percent of the median are dropped as typos, and up to 10 are kept, taken from each site in turn.
- The numbers come from used listings (and ones that don't say) when there are at least two: the listed median and quartiles, and a sells-for range of 85 to 100 percent of the listed median, rounded to the dollar, with 92 percent as the single estimate. Confidence is high with 6 or more tightly clustered matches, medium with 3, low otherwise.
8 like it listed now
Listed around $210, so likely sells for $179 to $210Listing prices, not sold prices
What something actually sold for is the best evidence, but it's out of reach. eBay's sold and completed search moved behind a sign-in in 2026, and its official sold-data APIs are shut down or closed to new apps. Searching signed in to a real account would break eBay's terms. So comps read what the same thing is listed for right now and assume it sells a little under that. The page says what things are listed for, never what they sold for. The price prompt tells the model the same: listings usually sell 0 to 15 percent under their ask.
The longer write-up, with the options that were weighed, is docs/sold-prices.md in the repo.
Reading pasted links
When a shopper pastes a link into the sidekick, readProductPage opens it, waits for the page to settle, and pulls the structured data shops publish (JSON-LD, og:title, product:price:amount, og:image) plus the first 2,500 characters of text. The fast model reads that to name the item and its price, then findComps runs on it. See Research and agents.
Not built yet
Signed-in sites are not wired
lib/server/market-logins.ts, the market_login table and a sold-search URL for eBay), which would open up eBay's sold prices and Facebook Marketplace. Nothing calls it. Comps today search public listings on eBay, Poshmark and Depop only.What it unlocks
- Prices grounded in what the same thing is for sale for today, checked one listing at a time, not a guess.
- A "Listed now on eBay, Poshmark and Depop" source on the findings sheet, with links to each listing.
- The shopping sidekick: "is this worth buying to resell?" with a holds-its-value score.
- Reading product pages that don't have an API.
Without it
Comps need both KERNEL_API_KEY and ANTHROPIC_API_KEY (compsConfigured). Without them the research run has no "Listings" step and prices from Channel3 and the model alone, and the sidekick page shows a Coming soon screen.
Setup
KERNEL_API_KEY=… # from kernel.sh
KERNEL_MAX_BROWSERS=5 # optional: raise it on a paid planA comps run takes most of a minute (queries, about 45 seconds of browsing at most, then matching), which is why research starts it straight after identifying the item and collects it last.